Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
f3136e3
Support modalities for gemini-2.0-flash-preview-image-generation
tpaulshippy Jun 14, 2025
6e3128c
Extract images from chat response
tpaulshippy Jun 14, 2025
28cf942
Rubocop
tpaulshippy Jun 14, 2025
357ea8a
Set modalities from capabilities
tpaulshippy Jun 14, 2025
0fed244
Merge branch 'main' into image-to-image
tpaulshippy Jul 20, 2025
9a1eeb8
Attach output image to message content
tpaulshippy Jul 20, 2025
1f60caa
Update comment
tpaulshippy Jul 20, 2025
98097d4
Refine image in conversation
tpaulshippy Jul 20, 2025
1b58d43
Merge branch 'main' into image-to-image
tpaulshippy Jul 24, 2025
d14d1e9
Remove duplicate SEO tags from docs
crmne Jul 28, 2025
460108c
Updated models
crmne Jul 28, 2025
730a8c8
fix: add missing blank lines for improved readability in generator an…
crmne Jul 28, 2025
1c50176
fix: Rails integration with_context now works without global config
crmne Jul 30, 2025
2afc6b2
Anthropic: Fix system prompt (use plain text instead of serialized JS…
MichaelHoste Jul 30, 2025
af0ead4
Provide access to raw response object from Faraday (#304)
tpaulshippy Jul 30, 2025
98dabdb
Add Chat#on_tool_call callback (#299)
bryan-ash Jul 30, 2025
cbb4276
Added proper handling of streaming error responses across both Farada…
dansingerman Jul 30, 2025
8626a77
Add message ordering guidance to Rails docs (#288)
crmne Jul 30, 2025
b6095a5
Bump version to 1.4.0 and update VCR cassettes
crmne Jul 30, 2025
8b0809b
Update model pricing and capabilities in JSON configuration
crmne Jul 30, 2025
598b584
Fix Action Cable capitalization in Rails guide
crmne Jul 31, 2025
20ae7a5
Update README and docs with comprehensive feature list
crmne Jul 31, 2025
7595a4e
Update Rails guide with instant message display pattern
crmne Jul 31, 2025
8f0ba07
Add Perplexity provider support
crmne Jul 31, 2025
7842a0b
Move available models guide to top-level navigation
crmne Jul 31, 2025
fe9d9d9
Fix broken links to available-models guide after relocation
crmne Jul 31, 2025
b6f9c13
Add Mistral AI provider support
crmne Jul 31, 2025
81f0a8c
Update specs to disable additional RuboCop checks for multi-turn conv…
crmne Jul 31, 2025
c5e059a
docs: add mistral provider
crmne Jul 31, 2025
9ae1018
reorder providers alphabetically
crmne Jul 31, 2025
2336483
Bust cache of gem version badge in README
crmne Jul 31, 2025
bf8c096
Fix Rails generator migration order and PostgreSQL detection
crmne Jul 31, 2025
2ff42aa
Removed unnecessary rubocop disable comments after last commit
crmne Jul 31, 2025
5a19a41
Fix Mistral models created_at timestamps
crmne Jul 31, 2025
a405e9e
Version bump to 1.5.0
crmne Jul 31, 2025
07444e5
Fix model capabilities format and imagen output modality
crmne Aug 1, 2025
a6dcd40
Automatically generate appraisal gemfiles
crmne Aug 1, 2025
b3b4684
Update JRuby version in CI matrix to jruby-10.0.1.0
crmne Aug 1, 2025
98f0cd1
Bump version to 1.5.1
crmne Aug 1, 2025
db1d563
Bust cache for gem badge in README
crmne Aug 1, 2025
43afe2f
Bust cache again for gem badge
crmne Aug 1, 2025
4f7a163
Wire up on_tool_call when using acts_as_chat rails integration (#318)
agarcher Aug 1, 2025
f291744
Resolve rubocop offenses
tpaulshippy Aug 3, 2025
76c7714
Merge branch 'main' into image-to-image
tpaulshippy Aug 3, 2025
84a939f
Update guides
tpaulshippy Aug 3, 2025
5c2c5d2
Merge branch 'main' into image-to-image
tpaulshippy Aug 7, 2025
a68483a
Merge branch 'main' into image-to-image
tpaulshippy Aug 25, 2025
061a8a5
Merge branch 'main' into image-to-image
tpaulshippy Aug 28, 2025
9bf9e3e
Refactor image to image specs
tpaulshippy Aug 28, 2025
9704fe1
Support attachments when accumulating streams
tpaulshippy Aug 28, 2025
a6e8ce1
Support attachments when accumulating streams
tpaulshippy Aug 28, 2025
bd71bef
Merge branch 'main' into image-to-image
tpaulshippy Aug 30, 2025
6a64d78
Failing specs for #7 and #8
tpaulshippy Aug 29, 2025
9c492e5
Failing spec for #9
tpaulshippy Aug 29, 2025
71e12f2
Do not merge duplicate attachments
tpaulshippy Aug 29, 2025
88f9af7
Make messages with images attached serializable
tpaulshippy Aug 30, 2025
1016d1c
Rename some spec variables
tpaulshippy Aug 30, 2025
192da9b
Merge branch 'main' into image-to-image
tpaulshippy Sep 8, 2025
c12c088
Move image saving helper method to separate file
tpaulshippy Sep 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions lib/ruby_llm/message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module RubyLLM
class Message
ROLES = %i[system user assistant tool].freeze

attr_reader :role, :tool_calls, :tool_call_id, :input_tokens, :output_tokens, :model_id
attr_reader :role, :tool_calls, :tool_call_id, :input_tokens, :output_tokens, :model_id, :images

def initialize(options = {})
@role = options.fetch(:role).to_sym
Expand All @@ -17,6 +17,7 @@ def initialize(options = {})
@output_tokens = options[:output_tokens]
@model_id = options[:model_id]
@tool_call_id = options[:tool_call_id]
@images = options[:images]

ensure_valid_role
end
Expand Down Expand Up @@ -49,7 +50,8 @@ def to_h
tool_call_id: tool_call_id,
input_tokens: input_tokens,
output_tokens: output_tokens,
model_id: model_id
model_id: model_id,
images: images
}.compact
end

Expand Down
3 changes: 3 additions & 0 deletions lib/ruby_llm/providers/gemini/capabilities.rb
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,9 @@ def modalities_for(model_id)
# Embedding output
modalities[:output] << 'embeddings' if model_id.match?(/embedding|gemini-embedding/)

# Image output
modalities[:output] << 'image' if model_id.match?(/image-generation/)

modalities
end

Expand Down
25 changes: 24 additions & 1 deletion lib/ruby_llm/providers/gemini/chat.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ def render_payload(messages, tools:, temperature:, model:, stream: false) # rubo
payload = {
contents: format_messages(messages),
generationConfig: {
temperature: temperature
temperature: temperature,
responseModalities: capabilities.modalities_for(model)[:output]
}
}
payload[:tools] = format_tools(tools) if tools.any?
Expand Down Expand Up @@ -72,6 +73,7 @@ def parse_completion_response(response)
Message.new(
role: :assistant,
content: extract_content(data),
images: extract_images(data),
tool_calls: tool_calls,
input_tokens: data.dig('usageMetadata', 'promptTokenCount'),
output_tokens: data.dig('usageMetadata', 'candidatesTokenCount'),
Expand All @@ -94,6 +96,27 @@ def extract_content(data)
text_parts.map { |p| p['text'] }.join
end

def extract_images(data)
candidate = data.dig('candidates', 0)
return '' unless candidate

# Content will be empty for function calls
return '' if function_call?(candidate)

# Extract image content
parts = candidate.dig('content', 'parts')
image_parts = parts&.select { |p| p['inlineData'] }
return '' unless image_parts&.any?

image_parts.map do |p|
Image.new(
data: p['inlineData']['data'],
mime_type: p['inlineData']['mimeType'],
model_id: data['modelVersion']
)
end
end

def function_call?(candidate)
parts = candidate.dig('content', 'parts')
parts&.any? { |p| p['functionCall'] }
Expand Down
Loading