ruby_llm 0.1.0.pre40 → 0.1.0.pre42

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db95ae0d7103334e7760659d58200e1cc882b970a2d3f67f56180a9879bad976
4
- data.tar.gz: f2a95fb00c476cbea2bf060ff5502c4fb61ed2d652578133cf7eee6ff4c7297f
3
+ metadata.gz: 2dac5b1b15a5d840d74ac63af791044a26669e0d41b0512d37236530cdd89693
4
+ data.tar.gz: b737c0061790066680424051afd3b9ea75d9d63f014d1684261dde81c1bedf37
5
5
  SHA512:
6
- metadata.gz: 3a3624f1c5d8184d5f36aca807528b4bc627a66aebe3829ff9e90bb62348a12863b764352d4b466931c0ef3469c1d952832a1b7726f93231b72324b13228ffb1
7
- data.tar.gz: 4bef0ae0373be0c2cd8d4702ddfc2f7a76883fe7c60e5ae5ef501c6f808744328919fba3f16dedf075d6f1cb1585120f46103415579a046306fd1a4d1c81f20b
6
+ metadata.gz: a90362b3d2dbe5b9f5c54851d94646273b343a32eef452528903026ea2e1add335a105273b0efc17a5ebde1da15421d894029ebe401e94c511531a1e8d2f706b
7
+ data.tar.gz: 504e5dd4e3692db83b6b9589e26e33f553865234b59264d05aec1f495e403e8e28eb2b7dfc2b2258047df07dd04f3740005b44dee8a874927b12a55f045fae6e
data/.rspec_status CHANGED
@@ -7,6 +7,8 @@ example_id | status | run_time |
7
7
  ./spec/ruby_llm/chat_content_spec.rb[1:1:3] | passed | 2.54 seconds |
8
8
  ./spec/ruby_llm/chat_content_spec.rb[1:2:1] | passed | 2.77 seconds |
9
9
  ./spec/ruby_llm/chat_content_spec.rb[1:2:2] | passed | 2.1 seconds |
10
+ ./spec/ruby_llm/chat_pdf_spec.rb[1:1:1] | passed | 7.75 seconds |
11
+ ./spec/ruby_llm/chat_pdf_spec.rb[1:1:2] | passed | 13.88 seconds |
10
12
  ./spec/ruby_llm/chat_spec.rb[1:1:1:1] | passed | 1.02 seconds |
11
13
  ./spec/ruby_llm/chat_spec.rb[1:1:1:2] | passed | 3.95 seconds |
12
14
  ./spec/ruby_llm/chat_spec.rb[1:1:2:1] | passed | 0.4854 seconds |
@@ -32,7 +34,8 @@ example_id | status | run_time |
32
34
  ./spec/ruby_llm/embeddings_spec.rb[1:1:1:2] | passed | 0.43632 seconds |
33
35
  ./spec/ruby_llm/embeddings_spec.rb[1:1:2:1] | passed | 0.65614 seconds |
34
36
  ./spec/ruby_llm/embeddings_spec.rb[1:1:2:2] | passed | 2.16 seconds |
35
- ./spec/ruby_llm/error_handling_spec.rb[1:1] | passed | 0.19586 seconds |
36
- ./spec/ruby_llm/image_generation_spec.rb[1:1:1] | passed | 12.77 seconds |
37
- ./spec/ruby_llm/image_generation_spec.rb[1:1:2] | passed | 18.13 seconds |
38
- ./spec/ruby_llm/image_generation_spec.rb[1:1:3] | passed | 0.00035 seconds |
37
+ ./spec/ruby_llm/error_handling_spec.rb[1:1] | passed | 0.29366 seconds |
38
+ ./spec/ruby_llm/image_generation_spec.rb[1:1:1] | passed | 11.61 seconds |
39
+ ./spec/ruby_llm/image_generation_spec.rb[1:1:2] | passed | 17.63 seconds |
40
+ ./spec/ruby_llm/image_generation_spec.rb[1:1:3] | passed | 8.77 seconds |
41
+ ./spec/ruby_llm/image_generation_spec.rb[1:1:4] | passed | 0.00319 seconds |
data/README.md CHANGED
@@ -16,7 +16,6 @@ A delightful Ruby way to work with AI. Chat in text, analyze and generate images
16
16
  <a href="https://badge.fury.io/rb/ruby_llm"><img src="https://badge.fury.io/rb/ruby_llm.svg" alt="Gem Version" /></a>
17
17
  <a href="https://github.com/testdouble/standard"><img src="https://img.shields.io/badge/code_style-standard-brightgreen.svg" alt="Ruby Style Guide" /></a>
18
18
  <a href="https://rubygems.org/gems/ruby_llm"><img alt="Gem Total Downloads" src="https://img.shields.io/gem/dt/ruby_llm"></a>
19
- <a href="https://github.com/crmne/ruby_llm/actions/workflows/cicd.yml"><img src="https://github.com/crmne/ruby_llm/actions/workflows/cicd.yml/badge.svg" alt="CI" /></a>
20
19
  <a href="https://codecov.io/gh/crmne/ruby_llm"><img src="https://codecov.io/gh/crmne/ruby_llm/branch/main/graph/badge.svg" alt="codecov" /></a>
21
20
  </p>
22
21
 
@@ -28,6 +27,7 @@ A delightful Ruby way to work with AI. Chat in text, analyze and generate images
28
27
  - 🎵 **Audio Analysis** - Get audio transcription and understanding with `chat.ask "what's said here?", with: { audio: "clip.wav" }`
29
28
  - 👁️ **Vision Understanding** - Let AIs analyze images with a simple `chat.ask "what's in this?", with: { image: "photo.jpg" }`
30
29
  - 🌊 **Streaming** - Real-time responses with proper Ruby streaming with `chat.ask "hello" do |chunk| puts chunk.content end`
30
+ - 📄 **PDF Analysis** - Analyze PDF documents directly with `chat.ask "What's in this?", with: { pdf: "document.pdf" }`
31
31
  - 🚂 **Rails Integration** - Persist chats and messages with ActiveRecord with `acts_as_{chat|message|tool_call}`
32
32
  - 🛠️ **Tool Support** - Give AIs access to your Ruby code with `chat.with_tool(Calculator).ask "what's 2+2?"`
33
33
  - 🎨 **Paint with AI** - Create images as easily as `RubyLLM.paint "a sunset over mountains"`
@@ -116,6 +116,14 @@ chat.ask "What's being said in this recording?", with: { audio: "meeting.wav" }
116
116
  # Combine multiple pieces of content
117
117
  chat.ask "Compare these diagrams", with: { image: ["diagram1.png", "diagram2.png"] }
118
118
 
119
+ # Ask about PDFs
120
+
121
+ chat = RubyLLM.chat(model: 'claude-3-7-sonnet-20250219')
122
+ chat.ask "Summarize this research paper", with: { pdf: "research.pdf" }
123
+
124
+ # Multiple PDFs work too
125
+ chat.ask "Compare these contracts", with: { pdf: ["contract1.pdf", "contract2.pdf"] }
126
+
119
127
  # Check token usage
120
128
  last_message = chat.messages.last
121
129
  puts "Conversation used #{last_message.input_tokens} input tokens and #{last_message.output_tokens} output tokens"
@@ -440,6 +448,21 @@ pp chat.messages.map(&:role)
440
448
  #=> [:user, :assistant, :tool, :assistant]
441
449
  ```
442
450
 
451
+ ## Provider Comparison
452
+
453
+ | Feature | OpenAI | Anthropic | Google | DeepSeek |
454
+ |---------|--------|-----------|--------|----------|
455
+ | Chat | ✅ GPT-4o, GPT-3.5 | ✅ Claude 3.7, 3.5, 3 | ✅ Gemini 2.0, 1.5 | ✅ DeepSeek Chat, Reasoner |
456
+ | Vision | ✅ GPT-4o, GPT-4 | ✅ All Claude 3 models | ✅ Gemini 2.0, 1.5 | ❌ |
457
+ | Audio | ✅ GPT-4o-audio, Whisper | ❌ | ✅ Gemini models | ❌ |
458
+ | PDF Analysis | ❌ | ✅ All Claude 3 models | ✅ Gemini models | ❌ |
459
+ | Function Calling | ✅ Most models | ✅ Claude 3 models | ✅ Gemini models (except Lite) | ✅ |
460
+ | JSON Mode | ✅ Most recent models | ✅ Claude 3 models | ✅ Gemini models | ❌ |
461
+ | Image Generation | ✅ DALL-E 3 | ❌ | ✅ Imagen | ❌ |
462
+ | Embeddings | ✅ text-embedding-3 | ❌ | ✅ text-embedding-004 | ❌ |
463
+ | Context Size | ⭐ Up to 200K (o1) | ⭐ 200K tokens | ⭐ Up to 2M tokens | 64K tokens |
464
+ | Streaming | ✅ | ✅ | ✅ | ✅ |
465
+
443
466
  ## Development
444
467
 
445
468
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt.
@@ -5,7 +5,7 @@ module RubyLLM
5
5
  # Stores data in a standard internal format, letting providers
6
6
  # handle their own formatting needs.
7
7
  class Content
8
- def initialize(text = nil, attachments = {})
8
+ def initialize(text = nil, attachments = {}) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
9
9
  @parts = []
10
10
  @parts << { type: 'text', text: text } unless text.nil? || text.empty?
11
11
 
@@ -16,6 +16,10 @@ module RubyLLM
16
16
  Array(attachments[:audio]).each do |source|
17
17
  @parts << attach_audio(source)
18
18
  end
19
+
20
+ Array(attachments[:pdf]).each do |source|
21
+ @parts << attach_pdf(source)
22
+ end
19
23
  end
20
24
 
21
25
  def to_a
@@ -64,6 +68,25 @@ module RubyLLM
64
68
  }
65
69
  end
66
70
 
71
+ def attach_pdf(source)
72
+ source = File.expand_path(source) unless source.start_with?('http')
73
+
74
+ pdf_data = {
75
+ type: 'pdf',
76
+ source: source
77
+ }
78
+
79
+ # For local files, validate they exist
80
+ unless source.start_with?('http')
81
+ raise Error, "PDF file not found: #{source}" unless File.exist?(source)
82
+
83
+ # Preload file content for providers that need it
84
+ pdf_data[:content] = File.read(source)
85
+ end
86
+
87
+ pdf_data
88
+ end
89
+
67
90
  def encode_file(source)
68
91
  if source.start_with?('http')
69
92
  response = Faraday.get(source)