llm.rb 0.1.0 → 0.2.1

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +85 -24
  3. data/lib/llm/conversation.rb +62 -10
  4. data/lib/llm/core_ext/ostruct.rb +0 -0
  5. data/lib/llm/error.rb +0 -0
  6. data/lib/llm/file.rb +0 -0
  7. data/lib/llm/http_client.rb +0 -0
  8. data/lib/llm/message.rb +1 -1
  9. data/lib/llm/message_queue.rb +18 -11
  10. data/lib/llm/model.rb +7 -0
  11. data/lib/llm/provider.rb +144 -98
  12. data/lib/llm/providers/anthropic/error_handler.rb +1 -1
  13. data/lib/llm/providers/anthropic/format.rb +7 -1
  14. data/lib/llm/providers/anthropic/response_parser.rb +0 -0
  15. data/lib/llm/providers/anthropic.rb +31 -15
  16. data/lib/llm/providers/gemini/error_handler.rb +0 -0
  17. data/lib/llm/providers/gemini/format.rb +7 -1
  18. data/lib/llm/providers/gemini/response_parser.rb +0 -0
  19. data/lib/llm/providers/gemini.rb +25 -14
  20. data/lib/llm/providers/ollama/error_handler.rb +0 -0
  21. data/lib/llm/providers/ollama/format.rb +7 -1
  22. data/lib/llm/providers/ollama/response_parser.rb +13 -0
  23. data/lib/llm/providers/ollama.rb +32 -8
  24. data/lib/llm/providers/openai/error_handler.rb +0 -0
  25. data/lib/llm/providers/openai/format.rb +7 -1
  26. data/lib/llm/providers/openai/response_parser.rb +5 -3
  27. data/lib/llm/providers/openai.rb +22 -12
  28. data/lib/llm/providers/voyageai/error_handler.rb +32 -0
  29. data/lib/llm/providers/voyageai/response_parser.rb +13 -0
  30. data/lib/llm/providers/voyageai.rb +44 -0
  31. data/lib/llm/response/completion.rb +0 -0
  32. data/lib/llm/response/embedding.rb +0 -0
  33. data/lib/llm/response.rb +0 -0
  34. data/lib/llm/version.rb +1 -1
  35. data/lib/llm.rb +19 -9
  36. data/llm.gemspec +6 -1
  37. data/share/llm/models/anthropic.yml +35 -0
  38. data/share/llm/models/gemini.yml +35 -0
  39. data/share/llm/models/ollama.yml +155 -0
  40. data/share/llm/models/openai.yml +46 -0
  41. data/spec/anthropic/completion_spec.rb +11 -27
  42. data/spec/anthropic/embedding_spec.rb +25 -0
  43. data/spec/gemini/completion_spec.rb +34 -29
  44. data/spec/gemini/embedding_spec.rb +4 -12
  45. data/spec/llm/conversation_spec.rb +93 -1
  46. data/spec/ollama/completion_spec.rb +7 -16
  47. data/spec/ollama/embedding_spec.rb +14 -5
  48. data/spec/openai/completion_spec.rb +40 -43
  49. data/spec/openai/embedding_spec.rb +4 -12
  50. data/spec/readme_spec.rb +9 -12
  51. data/spec/setup.rb +7 -16
  52. metadata +81 -4
  53. data/lib/llm/lazy_conversation.rb +0 -39
  54. data/spec/llm/lazy_conversation_spec.rb +0 -110
@@ -0,0 +1,155 @@
1
+ ---
2
+ gemma3:1b:
3
+ name: Gemma
4
+ parameters: 1B
5
+ description: Lightweight version of Google's Gemma 3 language model, suitable for
6
+ low-resource environments
7
+ to_param: gemma3:1b
8
+ gemma3:
9
+ name: Gemma
10
+ parameters: 4B
11
+ description: Balanced Gemma 3 model providing good accuracy with reasonable size
12
+ to_param: gemma3
13
+ gemma3:12b:
14
+ name: Gemma
15
+ parameters: 12B
16
+ description: Larger Gemma 3 model offering improved reasoning and generation abilities
17
+ to_param: gemma3:12b
18
+ gemma3:27b:
19
+ name: Gemma
20
+ parameters: 27B
21
+ description: High-end Gemma 3 model focused on top-tier performance and accuracy
22
+ to_param: gemma3:27b
23
+
24
+ qwq:
25
+ name: QwQ
26
+ parameters: 32B
27
+ description: Large-scale model with high parameter count for complex tasks and
28
+ high-quality generation
29
+ to_param: qwq
30
+
31
+ deepseek-r1:
32
+ name: DeepSeek-R1
33
+ parameters: 7B
34
+ description: Compact DeepSeek model optimized for research and experimentation
35
+ to_param: deepseek-r1
36
+ deepseek-r1:671b:
37
+ name: DeepSeek-R1
38
+ parameters: 671B
39
+ description: Massive-scale DeepSeek model focused on advanced AI reasoning and
40
+ capabilities
41
+ to_param: deepseek-r1:671b
42
+ deepseek-coder:
43
+ name: DeepSeek-Coder
44
+ parameters: 1.3B
45
+ description: Lightweight code generation model trained on 2T tokens of code and natural language
46
+ to_param: deepseek-coder
47
+ deepseek-coder:6.7b:
48
+ name: DeepSeek-Coder
49
+ parameters: 6.7B
50
+ description: Mid-sized DeepSeek-Coder model offering a strong balance between speed and capability for code-related tasks
51
+ to_param: deepseek-coder:6.7b
52
+ deepseek-coder:33b:
53
+ name: DeepSeek-Coder
54
+ parameters: 33B
55
+ description: Large DeepSeek-Coder model with high performance for code generation, understanding, and multilingual coding tasks
56
+ to_param: deepseek-coder:33b
57
+
58
+ llama3.3:
59
+ name: Llama
60
+ parameters: 70B
61
+ description: Latest large Llama model designed for high-end performance in reasoning
62
+ and language tasks
63
+ to_param: llama3.3
64
+ llama3.2:
65
+ name: Llama
66
+ parameters: 3B
67
+ description: Small but capable version of Llama 3.2 for lightweight applications
68
+ to_param: llama3.2
69
+ llama3.2:1b:
70
+ name: Llama
71
+ parameters: 1B
72
+ description: Tiny version of Llama 3.2, extremely lightweight and fast
73
+ to_param: llama3.2:1b
74
+ llama3.2-vision:
75
+ name: Llama Vision
76
+ parameters: 11B
77
+ description: Multimodal Llama 3.2 model with vision capabilities (images + text)
78
+ to_param: llama3.2-vision
79
+ llama3.2-vision:90b:
80
+ name: Llama Vision
81
+ parameters: 90B
82
+ description: Large-scale vision-capable Llama model for advanced multimodal tasks
83
+ to_param: llama3.2-vision:90b
84
+ llama3.1:
85
+ name: Llama
86
+ parameters: 8B
87
+ description: General-purpose Llama model designed for good accuracy and performance
88
+ balance
89
+ to_param: llama3.1
90
+ llama3.1:405b:
91
+ name: Llama
92
+ parameters: 405B
93
+ description: Extremely large-scale version of Llama 3.1, suitable for advanced tasks
94
+ to_param: llama3.1:405b
95
+
96
+ phi4:
97
+ name: Phi
98
+ parameters: 14B
99
+ description: Phi 4 is known for compact size and competitive performance in general
100
+ tasks
101
+ to_param: phi4
102
+ phi4-mini:
103
+ name: Phi Mini
104
+ parameters: 3.8B
105
+ description: Lightweight variant of Phi 4 ideal for quick inference on constrained systems
106
+ to_param: phi4-mini
107
+
108
+ mistral:
109
+ name: Mistral
110
+ parameters: 7B
111
+ description: Popular and versatile open model for general language tasks
112
+ to_param: mistral
113
+
114
+ moondream:
115
+ name: Moondream
116
+ parameters: 1.4B
117
+ description: Compact vision-enabled model with strong general performance
118
+ to_param: moondream
119
+
120
+ neural-chat:
121
+ name: Neural Chat
122
+ parameters: 7B
123
+ description: Chat-focused model fine-tuned for natural conversations
124
+ to_param: neural-chat
125
+
126
+ starling-lm:
127
+ name: Starling
128
+ parameters: 7B
129
+ description: Model focused on instruction-following and conversational performance
130
+ to_param: starling-lm
131
+
132
+ codellama:
133
+ name: Code Llama
134
+ parameters: 7B
135
+ description: Llama model variant fine-tuned specifically for code understanding
136
+ and generation
137
+ to_param: codellama
138
+
139
+ llama2-uncensored:
140
+ name: Llama 2 Uncensored
141
+ parameters: 7B
142
+ description: Unfiltered version of Llama 2 for unrestricted language modeling
143
+ to_param: llama2-uncensored
144
+
145
+ llava:
146
+ name: LLaVA
147
+ parameters: 7B
148
+ description: Multimodal model combining vision and language understanding
149
+ to_param: llava
150
+
151
+ granite3.2:
152
+ name: Granite
153
+ parameters: 8B
154
+ description: IBM’s Granite model for enterprise-grade language applications
155
+ to_param: granite3.2
@@ -0,0 +1,46 @@
1
+ ---
2
+ o3-mini:
3
+ name: OpenAI o3-mini
4
+ parameters: Unknown
5
+ description: Fast, flexible, intelligent reasoning model
6
+ to_param: o3-mini
7
+ o1:
8
+ name: OpenAI o1
9
+ parameters: Unknown
10
+ description: High-intelligence reasoning model
11
+ to_param: o1
12
+ o1-mini:
13
+ name: OpenAI o1-mini
14
+ parameters: Unknown
15
+ description: Faster, more affordable reasoning model than o1
16
+ to_param: o1-mini
17
+ o1-pro:
18
+ name: OpenAI o1-pro
19
+ parameters: Unknown
20
+ description: More compute than o1 for better responses
21
+ to_param: o1-pro
22
+ gpt-4.5-preview:
23
+ name: GPT-4.5 Preview
24
+ parameters: Unknown
25
+ description: Largest and most capable GPT model
26
+ to_param: gpt-4.5-preview
27
+ gpt-4o:
28
+ name: GPT-4o
29
+ parameters: Unknown
30
+ description: Fast, intelligent, flexible GPT model
31
+ to_param: gpt-4o
32
+ gpt-4o-mini:
33
+ name: GPT-4o Mini
34
+ parameters: Mini
35
+ description: Fast, affordable small model for focused tasks
36
+ to_param: gpt-4o-mini
37
+ gpt-4o-realtime-preview:
38
+ name: GPT-4o Realtime
39
+ parameters: Unknown
40
+ description: Realtime model for text and audio inputs/outputs
41
+ to_param: gpt-4o-realtime-preview
42
+ gpt-3.5-turbo:
43
+ name: GPT-3.5 Turbo
44
+ parameters: Unknown
45
+ description: Legacy GPT model for cheaper chat and non-chat tasks
46
+ to_param: gpt-3.5-turbo
@@ -3,29 +3,11 @@
3
3
  require "setup"
4
4
 
5
5
  RSpec.describe "LLM::Anthropic: completions" do
6
- subject(:anthropic) { LLM.anthropic("") }
6
+ subject(:anthropic) { LLM.anthropic(token) }
7
+ let(:token) { ENV["LLM_SECRET"] || "TOKEN" }
7
8
 
8
- before(:each, :success) do
9
- stub_request(:post, "https://api.anthropic.com/v1/messages")
10
- .with(headers: {"Content-Type" => "application/json"})
11
- .to_return(
12
- status: 200,
13
- body: fixture("anthropic/completions/ok_completion.json"),
14
- headers: {"Content-Type" => "application/json"}
15
- )
16
- end
17
-
18
- before(:each, :unauthorized) do
19
- stub_request(:post, "https://api.anthropic.com/v1/messages")
20
- .with(headers: {"Content-Type" => "application/json"})
21
- .to_return(
22
- status: 403,
23
- body: fixture("anthropic/completions/unauthorized_completion.json"),
24
- headers: {"Content-Type" => "application/json"}
25
- )
26
- end
27
-
28
- context "when given a successful response", :success do
9
+ context "when given a successful response",
10
+ vcr: {cassette_name: "anthropic/completions/successful_response"} do
29
11
  subject(:response) { anthropic.complete("Hello, world", :user) }
30
12
 
31
13
  it "returns a completion" do
@@ -38,9 +20,9 @@ RSpec.describe "LLM::Anthropic: completions" do
38
20
 
39
21
  it "includes token usage" do
40
22
  expect(response).to have_attributes(
41
- prompt_tokens: 2095,
42
- completion_tokens: 503,
43
- total_tokens: 2598
23
+ prompt_tokens: 10,
24
+ completion_tokens: 30,
25
+ total_tokens: 40
44
26
  )
45
27
  end
46
28
 
@@ -50,7 +32,7 @@ RSpec.describe "LLM::Anthropic: completions" do
50
32
  it "has choices" do
51
33
  expect(choice).to have_attributes(
52
34
  role: "assistant",
53
- content: "Hi! My name is Claude."
35
+ content: "Hello! How can I assist you today? Feel free to ask me any questions or let me know if you need help with anything."
54
36
  )
55
37
  end
56
38
 
@@ -60,8 +42,10 @@ RSpec.describe "LLM::Anthropic: completions" do
60
42
  end
61
43
  end
62
44
 
63
- context "when given an unauthorized response", :unauthorized do
45
+ context "when given an unauthorized response",
46
+ vcr: {cassette_name: "anthropic/completions/unauthorized_response"} do
64
47
  subject(:response) { anthropic.complete("Hello", :user) }
48
+ let(:token) { "BADTOKEN" }
65
49
 
66
50
  it "raises an error" do
67
51
  expect { response }.to raise_error(LLM::Error::Unauthorized)
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "setup"
4
+
5
+ RSpec.describe "LLM::Anthropic: embeddings" do
6
+ let(:anthropic) { LLM.anthropic(token) }
7
+ let(:token) { ENV["LLM_SECRET"] || "TOKEN" }
8
+
9
+ context "when given a successful response",
10
+ vcr: {cassette_name: "anthropic/embeddings/successful_response"} do
11
+ subject(:response) { anthropic.embed("Hello, world", token:) }
12
+
13
+ it "returns an embedding" do
14
+ expect(response).to be_instance_of(LLM::Response::Embedding)
15
+ end
16
+
17
+ it "returns a model" do
18
+ expect(response.model).to eq("voyage-2")
19
+ end
20
+
21
+ it "has embeddings" do
22
+ expect(response.embeddings).to be_instance_of(Array)
23
+ end
24
+ end
25
+ end
@@ -3,44 +3,26 @@
3
3
  require "setup"
4
4
 
5
5
  RSpec.describe "LLM::Gemini: completions" do
6
- subject(:gemini) { LLM.gemini("") }
6
+ subject(:gemini) { LLM.gemini(token) }
7
+ let(:token) { ENV["LLM_SECRET"] || "TOKEN" }
7
8
 
8
- before(:each, :success) do
9
- stub_request(:post, "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=")
10
- .with(headers: {"Content-Type" => "application/json"})
11
- .to_return(
12
- status: 200,
13
- body: fixture("gemini/completions/ok_completion.json"),
14
- headers: {"Content-Type" => "application/json"}
15
- )
16
- end
17
-
18
- before(:each, :unauthorized) do
19
- stub_request(:post, "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=")
20
- .with(headers: {"Content-Type" => "application/json"})
21
- .to_return(
22
- status: 400,
23
- body: fixture("gemini/completions/unauthorized_completion.json"),
24
- headers: {"Content-Type" => "application/json"}
25
- )
26
- end
27
-
28
- context "when given a successful response", :success do
29
- subject(:response) { gemini.complete(LLM::Message.new("user", "Hello!")) }
9
+ context "when given a successful response",
10
+ vcr: {cassette_name: "gemini/completions/successful_response"} do
11
+ subject(:response) { gemini.complete("Hello!", :user) }
30
12
 
31
13
  it "returns a completion" do
32
14
  expect(response).to be_a(LLM::Response::Completion)
33
15
  end
34
16
 
35
17
  it "returns a model" do
36
- expect(response.model).to eq("gemini-1.5-flash-001")
18
+ expect(response.model).to eq("gemini-1.5-flash")
37
19
  end
38
20
 
39
21
  it "includes token usage" do
40
22
  expect(response).to have_attributes(
41
23
  prompt_tokens: 2,
42
- completion_tokens: 10,
43
- total_tokens: 12
24
+ completion_tokens: 11,
25
+ total_tokens: 13
44
26
  )
45
27
  end
46
28
 
@@ -52,7 +34,7 @@ RSpec.describe "LLM::Gemini: completions" do
52
34
  choices: [
53
35
  have_attributes(
54
36
  role: "model",
55
- content: "Hello! How can I help you today? \n"
37
+ content: "Hello there! How can I help you today?\n"
56
38
  )
57
39
  ]
58
40
  )
@@ -64,8 +46,31 @@ RSpec.describe "LLM::Gemini: completions" do
64
46
  end
65
47
  end
66
48
 
67
- context "when given an unauthorized response", :unauthorized do
68
- subject(:response) { gemini.complete(LLM::Message.new("user", "Hello!")) }
49
+ context "when given a thread of messages",
50
+ vcr: {cassette_name: "gemini/completions/successful_response_thread"} do
51
+ subject(:response) do
52
+ gemini.complete "What is your name? What age are you?", :user, messages: [
53
+ {role: "user", content: "Answer all of my questions"},
54
+ {role: "user", content: "Your name is Pablo, you are 25 years old and you are my amigo"}
55
+ ]
56
+ end
57
+
58
+ it "has choices" do
59
+ expect(response).to have_attributes(
60
+ choices: [
61
+ have_attributes(
62
+ role: "model",
63
+ content: "My name is Pablo, and I am 25 years old. ¡Amigo!\n"
64
+ )
65
+ ]
66
+ )
67
+ end
68
+ end
69
+
70
+ context "when given an unauthorized response",
71
+ vcr: {cassette_name: "gemini/completions/unauthorized_response"} do
72
+ subject(:response) { gemini.complete("Hello!", :user) }
73
+ let(:token) { "BADTOKEN" }
69
74
 
70
75
  it "raises an error" do
71
76
  expect { response }.to raise_error(LLM::Error::Unauthorized)
@@ -3,19 +3,11 @@
3
3
  require "setup"
4
4
 
5
5
  RSpec.describe "LLM::OpenAI: embeddings" do
6
- let(:gemini) { LLM.gemini("") }
6
+ let(:gemini) { LLM.gemini(token) }
7
+ let(:token) { ENV["LLM_SECRET"] || "TOKEN" }
7
8
 
8
- before(:each, :success) do
9
- stub_request(:post, "https://generativelanguage.googleapis.com/v1beta/models/text-embedding-004:embedContent?key=")
10
- .with(headers: {"Content-Type" => "application/json"})
11
- .to_return(
12
- status: 200,
13
- body: fixture("gemini/embeddings/hello_world_embedding.json"),
14
- headers: {"Content-Type" => "application/json"}
15
- )
16
- end
17
-
18
- context "when given a successful response", :success do
9
+ context "when given a successful response",
10
+ vcr: {cassette_name: "gemini/embeddings/successful_response"} do
19
11
  subject(:response) { gemini.embed("Hello, world") }
20
12
 
21
13
  it "returns an embedding" do
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- RSpec.describe LLM::Conversation do
3
+ require "setup"
4
+
5
+ RSpec.describe "LLM::Conversation: non-lazy" do
4
6
  shared_examples "a multi-turn conversation" do
5
7
  context "when given a thread of messages" do
6
8
  let(:inputs) do
@@ -54,3 +56,93 @@ RSpec.describe LLM::Conversation do
54
56
  include_examples "a multi-turn conversation"
55
57
  end
56
58
  end
59
+
60
+ RSpec.describe "LLM::Conversation: lazy" do
61
+ let(:described_class) { LLM::Conversation }
62
+ let(:token) { ENV["LLM_SECRET"] || "TOKEN" }
63
+ let(:prompt) { "Keep your answers short and concise, and provide three answers to the three questions" }
64
+
65
+ context "with gemini",
66
+ vcr: {cassette_name: "gemini/lazy_conversation/successful_response"} do
67
+ let(:provider) { LLM.gemini(token) }
68
+ let(:conversation) { described_class.new(provider).lazy }
69
+
70
+ context "when given a thread of messages" do
71
+ subject(:message) { conversation.messages.to_a[-1] }
72
+
73
+ before do
74
+ conversation.chat prompt
75
+ conversation.chat "What is 3+2 ?"
76
+ conversation.chat "What is 5+5 ?"
77
+ conversation.chat "What is 5+7 ?"
78
+ end
79
+
80
+ it "maintains a conversation" do
81
+ is_expected.to have_attributes(
82
+ role: "model",
83
+ content: "5\n10\n12\n"
84
+ )
85
+ end
86
+ end
87
+ end
88
+
89
+ context "with openai" do
90
+ let(:provider) { LLM.openai(token) }
91
+ let(:conversation) { described_class.new(provider).lazy }
92
+
93
+ context "when given a thread of messages",
94
+ vcr: {cassette_name: "openai/lazy_conversation/successful_response"} do
95
+ subject(:message) { conversation.recent_message }
96
+
97
+ before do
98
+ conversation.chat prompt, :system
99
+ conversation.chat "What is 3+2 ?"
100
+ conversation.chat "What is 5+5 ?"
101
+ conversation.chat "What is 5+7 ?"
102
+ end
103
+
104
+ it "maintains a conversation" do
105
+ is_expected.to have_attributes(
106
+ role: "assistant",
107
+ content: "1. 5 \n2. 10 \n3. 12 "
108
+ )
109
+ end
110
+ end
111
+
112
+ context "when given a specific model",
113
+ vcr: {cassette_name: "openai/lazy_conversation/successful_response_o3_mini"} do
114
+ let(:conversation) { described_class.new(provider, model: provider.models["o3-mini"]).lazy }
115
+
116
+ it "maintains the model throughout a conversation" do
117
+ conversation.chat(prompt, :system)
118
+ expect(conversation.recent_message.extra[:completion].model).to eq("o3-mini-2025-01-31")
119
+ conversation.chat("What is 5+5?")
120
+ expect(conversation.recent_message.extra[:completion].model).to eq("o3-mini-2025-01-31")
121
+ end
122
+ end
123
+ end
124
+
125
+ context "with ollama",
126
+ vcr: {cassette_name: "ollama/lazy_conversation/successful_response"} do
127
+ let(:provider) { LLM.ollama(nil, host: "eel.home.network") }
128
+ let(:conversation) { described_class.new(provider).lazy }
129
+
130
+ context "when given a thread of messages" do
131
+ subject(:message) { conversation.recent_message }
132
+
133
+ before do
134
+ conversation.chat prompt, :system
135
+ conversation.chat "What is 3+2 ?"
136
+ conversation.chat "What is 5+5 ?"
137
+ conversation.chat "What is 5+7 ?"
138
+ end
139
+
140
+ it "maintains a conversation" do
141
+ is_expected.to have_attributes(
142
+ role: "assistant",
143
+ content: "Here are the calculations:\n\n1. 3 + 2 = 5\n2. 5 + 5 = 10\n3. 5 + 7 = 12"
144
+ )
145
+ end
146
+ end
147
+ end
148
+ end
@@ -3,19 +3,10 @@
3
3
  require "setup"
4
4
 
5
5
  RSpec.describe "LLM::Ollama: completions" do
6
- subject(:ollama) { LLM.ollama("") }
7
-
8
- before(:each, :success) do
9
- stub_request(:post, "localhost:11434/api/chat")
10
- .with(headers: {"Content-Type" => "application/json"})
11
- .to_return(
12
- status: 200,
13
- body: fixture("ollama/completions/ok_completion.json"),
14
- headers: {"Content-Type" => "application/json"}
15
- )
16
- end
6
+ let(:ollama) { LLM.ollama(nil, host: "eel.home.network") }
17
7
 
18
- context "when given a successful response", :success do
8
+ context "when given a successful response",
9
+ vcr: {cassette_name: "ollama/completions/successful_response"} do
19
10
  subject(:response) { ollama.complete("Hello!", :user) }
20
11
 
21
12
  it "returns a completion" do
@@ -28,9 +19,9 @@ RSpec.describe "LLM::Ollama: completions" do
28
19
 
29
20
  it "includes token usage" do
30
21
  expect(response).to have_attributes(
31
- prompt_tokens: 26,
32
- completion_tokens: 298,
33
- total_tokens: 324
22
+ prompt_tokens: 27,
23
+ completion_tokens: 26,
24
+ total_tokens: 53
34
25
  )
35
26
  end
36
27
 
@@ -40,7 +31,7 @@ RSpec.describe "LLM::Ollama: completions" do
40
31
  it "has choices" do
41
32
  expect(choice).to have_attributes(
42
33
  role: "assistant",
43
- content: "Hello! How are you today?"
34
+ content: "Hello! It's nice to meet you. Is there something I can help you with, or would you like to chat?"
44
35
  )
45
36
  end
46
37
 
@@ -3,13 +3,22 @@
3
3
  require "setup"
4
4
 
5
5
  RSpec.describe "LLM::Ollama: embeddings" do
6
- let(:ollama) { LLM.ollama("") }
6
+ let(:ollama) { LLM.ollama(nil, host: "eel.home.network") }
7
7
 
8
- context "when given a successful response", :success do
9
- subject(:response) { ollama.embed("Hello, world") }
8
+ context "when given a successful response",
9
+ vcr: {cassette_name: "ollama/embeddings/successful_response"} do
10
+ subject(:response) { ollama.embed(["This is a paragraph", "This is another one"]) }
10
11
 
11
- it "raises NotImplementedError" do
12
- expect { response }.to raise_error(NotImplementedError)
12
+ it "returns an embedding" do
13
+ expect(response).to be_instance_of(LLM::Response::Embedding)
14
+ end
15
+
16
+ it "returns a model" do
17
+ expect(response.model).to eq("llama3.2")
18
+ end
19
+
20
+ it "has embeddings" do
21
+ expect(response.embeddings.size).to eq(2)
13
22
  end
14
23
  end
15
24
  end