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.
- checksums.yaml +4 -4
- data/README.md +85 -24
- data/lib/llm/conversation.rb +62 -10
- data/lib/llm/core_ext/ostruct.rb +0 -0
- data/lib/llm/error.rb +0 -0
- data/lib/llm/file.rb +0 -0
- data/lib/llm/http_client.rb +0 -0
- data/lib/llm/message.rb +1 -1
- data/lib/llm/message_queue.rb +18 -11
- data/lib/llm/model.rb +7 -0
- data/lib/llm/provider.rb +144 -98
- data/lib/llm/providers/anthropic/error_handler.rb +1 -1
- data/lib/llm/providers/anthropic/format.rb +7 -1
- data/lib/llm/providers/anthropic/response_parser.rb +0 -0
- data/lib/llm/providers/anthropic.rb +31 -15
- data/lib/llm/providers/gemini/error_handler.rb +0 -0
- data/lib/llm/providers/gemini/format.rb +7 -1
- data/lib/llm/providers/gemini/response_parser.rb +0 -0
- data/lib/llm/providers/gemini.rb +25 -14
- data/lib/llm/providers/ollama/error_handler.rb +0 -0
- data/lib/llm/providers/ollama/format.rb +7 -1
- data/lib/llm/providers/ollama/response_parser.rb +13 -0
- data/lib/llm/providers/ollama.rb +32 -8
- data/lib/llm/providers/openai/error_handler.rb +0 -0
- data/lib/llm/providers/openai/format.rb +7 -1
- data/lib/llm/providers/openai/response_parser.rb +5 -3
- data/lib/llm/providers/openai.rb +22 -12
- data/lib/llm/providers/voyageai/error_handler.rb +32 -0
- data/lib/llm/providers/voyageai/response_parser.rb +13 -0
- data/lib/llm/providers/voyageai.rb +44 -0
- data/lib/llm/response/completion.rb +0 -0
- data/lib/llm/response/embedding.rb +0 -0
- data/lib/llm/response.rb +0 -0
- data/lib/llm/version.rb +1 -1
- data/lib/llm.rb +19 -9
- data/llm.gemspec +6 -1
- data/share/llm/models/anthropic.yml +35 -0
- data/share/llm/models/gemini.yml +35 -0
- data/share/llm/models/ollama.yml +155 -0
- data/share/llm/models/openai.yml +46 -0
- data/spec/anthropic/completion_spec.rb +11 -27
- data/spec/anthropic/embedding_spec.rb +25 -0
- data/spec/gemini/completion_spec.rb +34 -29
- data/spec/gemini/embedding_spec.rb +4 -12
- data/spec/llm/conversation_spec.rb +93 -1
- data/spec/ollama/completion_spec.rb +7 -16
- data/spec/ollama/embedding_spec.rb +14 -5
- data/spec/openai/completion_spec.rb +40 -43
- data/spec/openai/embedding_spec.rb +4 -12
- data/spec/readme_spec.rb +9 -12
- data/spec/setup.rb +7 -16
- metadata +81 -4
- data/lib/llm/lazy_conversation.rb +0 -39
- 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
|
-
|
9
|
-
|
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:
|
42
|
-
completion_tokens:
|
43
|
-
total_tokens:
|
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: "
|
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",
|
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
|
-
|
9
|
-
|
10
|
-
|
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
|
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:
|
43
|
-
total_tokens:
|
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
|
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
|
68
|
-
|
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
|
-
|
9
|
-
|
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
|
-
|
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
|
-
|
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",
|
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:
|
32
|
-
completion_tokens:
|
33
|
-
total_tokens:
|
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!
|
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",
|
9
|
-
|
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 "
|
12
|
-
expect
|
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
|