Skip to content

Commit 392268f

Browse files
committed
Add with_messages for scoping chat messages
1 parent 1e315ab commit 392268f

4 files changed

Lines changed: 114 additions & 1 deletion

File tree

lib/ruby_llm/active_record/acts_as_legacy.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ def with_schema(...)
151151
self
152152
end
153153

154+
def with_messages(...)
155+
to_llm.with_messages(...)
156+
self
157+
end
158+
154159
def on_new_message(&block)
155160
to_llm
156161

lib/ruby_llm/active_record/chat_methods.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ def with_schema(...)
139139
self
140140
end
141141

142+
def with_messages(...)
143+
to_llm.with_messages(...)
144+
self
145+
end
146+
142147
def on_new_message(&block)
143148
to_llm
144149

lib/ruby_llm/chat.rb

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def initialize(model: nil, provider: nil, assume_model_exists: false, context: n
1818
with_model(model_id, provider: provider, assume_exists: assume_model_exists)
1919
@temperature = nil
2020
@messages = []
21+
@messages_scope = nil
2122
@tools = {}
2223
@params = {}
2324
@headers = {}
@@ -97,6 +98,16 @@ def with_schema(schema)
9798
self
9899
end
99100

101+
def with_messages(&block)
102+
if block.nil?
103+
@messages_scope = nil
104+
return self
105+
end
106+
107+
@messages_scope = block
108+
self
109+
end
110+
100111
def on_new_message(&block)
101112
@on[:new_message] = block
102113
self
@@ -123,7 +134,7 @@ def each(&)
123134

124135
def complete(&) # rubocop:disable Metrics/PerceivedComplexity
125136
response = @provider.complete(
126-
messages,
137+
scoped_messages,
127138
tools: @tools,
128139
temperature: @temperature,
129140
model: @model,
@@ -169,6 +180,10 @@ def instance_variables
169180

170181
private
171182

183+
def scoped_messages
184+
@messages_scope.present? ? @messages_scope.call(messages) : messages
185+
end
186+
172187
def wrap_streaming_block(&block)
173188
return nil unless block_given?
174189

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
RSpec.describe RubyLLM::Chat do
6+
include_context 'with configured RubyLLM'
7+
8+
describe '#with_messages' do
9+
it 'uses all messages by default when no scope is configured' do
10+
chat = RubyLLM.chat
11+
provider = chat.instance_variable_get(:@provider)
12+
13+
allow(provider).to receive(:complete).and_return(
14+
RubyLLM::Message.new(role: :assistant, content: 'Test response')
15+
)
16+
17+
chat.add_message(role: :user, content: 'one')
18+
chat.add_message(role: :assistant, content: 'two')
19+
20+
chat.complete
21+
22+
expect(provider).to have_received(:complete).with(
23+
chat.messages,
24+
hash_including(
25+
tools: chat.tools,
26+
temperature: anything,
27+
model: chat.model,
28+
params: chat.params,
29+
headers: chat.headers,
30+
schema: chat.schema
31+
)
32+
)
33+
end
34+
35+
it 'applies a callable scope to the messages passed to the provider' do
36+
chat = RubyLLM.chat
37+
provider = chat.instance_variable_get(:@provider)
38+
39+
allow(provider).to receive(:complete).and_return(
40+
RubyLLM::Message.new(role: :assistant, content: 'Scoped response')
41+
)
42+
43+
first = chat.add_message(role: :user, content: 'first')
44+
second = chat.add_message(role: :assistant, content: 'second')
45+
46+
chat.with_messages { |msgs| [msgs.last] }
47+
48+
chat.complete
49+
50+
expect(provider).to have_received(:complete) do |messages_arg, **_options|
51+
expect(messages_arg).to eq([second])
52+
expect(messages_arg).not_to include(first)
53+
end
54+
end
55+
56+
it 'can clear the scope by calling without a block' do
57+
chat = RubyLLM.chat
58+
provider = chat.instance_variable_get(:@provider)
59+
60+
allow(provider).to receive(:complete).and_return(
61+
RubyLLM::Message.new(role: :assistant, content: 'Cleared scope response')
62+
)
63+
64+
chat.add_message(role: :user, content: 'one')
65+
chat.add_message(role: :assistant, content: 'two')
66+
67+
chat.with_messages { |msgs| [msgs.last] }
68+
chat.with_messages
69+
70+
chat.complete
71+
72+
expect(provider).to have_received(:complete).with(
73+
chat.messages,
74+
hash_including(
75+
tools: chat.tools,
76+
temperature: anything,
77+
model: chat.model,
78+
params: chat.params,
79+
headers: chat.headers,
80+
schema: chat.schema
81+
)
82+
)
83+
end
84+
85+
end
86+
end
87+
88+

0 commit comments

Comments
 (0)