Skip to content

Commit cb28d86

Browse files
committed
Improve OpenRouter Media Support
1 parent 04d4409 commit cb28d86

16 files changed

+1767
-4
lines changed

lib/ruby_llm/providers/anthropic/chat.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def format_message(msg)
8989
def format_basic_message(msg)
9090
{
9191
role: convert_role(msg.role),
92-
content: Media.format_content(msg.content)
92+
content: self::Media.format_content(msg.content)
9393
}
9494
end
9595

lib/ruby_llm/providers/bedrock/chat.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def format_message(msg)
2929
def format_basic_message(msg)
3030
{
3131
role: Anthropic::Chat.convert_role(msg.role),
32-
content: Media.format_content(msg.content)
32+
content: self::Media.format_content(msg.content)
3333
}
3434
end
3535

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# frozen_string_literal: true
2+
3+
module RubyLLM
4+
module Providers
5+
module DeepSeek
6+
# Handles formatting of media content (images, audio) for DeepSeek APIs
7+
module Media
8+
include OpenAI::Media
9+
10+
module_function :format_content, :format_image, :format_pdf, :format_audio, :format_text
11+
end
12+
end
13+
end
14+
end

lib/ruby_llm/providers/gemini/chat.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def format_parts(msg)
6161
}
6262
}]
6363
else
64-
Media.format_content(msg.content)
64+
self::Media.format_content(msg.content)
6565
end
6666
end
6767

lib/ruby_llm/providers/openai/chat.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def format_messages(messages)
4949
messages.map do |msg|
5050
{
5151
role: format_role(msg.role),
52-
content: Media.format_content(msg.content),
52+
content: self::Media.format_content(msg.content),
5353
tool_calls: format_tool_calls(msg.tool_calls),
5454
tool_call_id: msg.tool_call_id
5555
}.compact

lib/ruby_llm/providers/openrouter.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module Providers
66
module OpenRouter
77
extend OpenAI
88
extend OpenRouter::Models
9+
extend OpenRouter::Media
910

1011
module_function
1112

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# frozen_string_literal: true
2+
3+
module RubyLLM
4+
module Providers
5+
module OpenRouter
6+
# Handles formatting of media content (images, audio) for OpenRouter APIs
7+
module Media
8+
module_function
9+
10+
def format_content(content) # rubocop:disable Metrics/MethodLength
11+
return content unless content.is_a?(Array)
12+
13+
content.map do |part|
14+
case part[:type]
15+
when 'image'
16+
format_image(part)
17+
when 'input_audio'
18+
format_audio(part)
19+
when 'pdf'
20+
format_pdf(part)
21+
else
22+
part
23+
end
24+
end
25+
end
26+
27+
# @see https://openrouter.ai/docs/features/images-and-pdfs#image-inputs
28+
def format_image(part)
29+
{
30+
type: 'image_url',
31+
image_url: {
32+
url: format_image_url(part[:source]),
33+
detail: 'auto'
34+
}
35+
}
36+
end
37+
38+
def format_image_url(source)
39+
if source[:type] == 'base64'
40+
"data:#{source[:media_type]};base64,#{source[:data]}"
41+
else
42+
source[:url]
43+
end
44+
end
45+
46+
def format_audio(part)
47+
{
48+
type: 'input_audio',
49+
input_audio: part[:input_audio]
50+
}
51+
end
52+
53+
# @see https://openrouter.ai/docs/features/images-and-pdfs#pdf-support
54+
def format_pdf(part)
55+
{
56+
type: 'file',
57+
file: {
58+
filename: File.basename(part[:source]),
59+
file_data: format_file_data(part[:content] || part[:source])
60+
}
61+
}
62+
end
63+
64+
def format_file_data(source)
65+
source = Faraday.get(source).body if source.start_with?('http')
66+
67+
"data:application/pdf;base64,#{Base64.strict_encode64(source)}"
68+
end
69+
end
70+
end
71+
end
72+
end

spec/fixtures/vcr_cassettes/chat_pdf_model_with_paths_behaves_like_pdf_models_anthropic_claude-3-5-haiku-20241022_handles_multiple_pdfs.yml

Lines changed: 176 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

spec/fixtures/vcr_cassettes/chat_pdf_model_with_paths_behaves_like_pdf_models_anthropic_claude-3-5-haiku-20241022_understands_pdfs.yml

Lines changed: 188 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

spec/fixtures/vcr_cassettes/chat_pdf_model_with_paths_behaves_like_pdf_models_gemini_gemini-2_0-flash_handles_multiple_pdfs.yml

Lines changed: 241 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)