langchainrb 0.13.3 → 0.13.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d39fd55aeaa36e9d2a0009dac4808743d97941bd139bc9373eafb153cfb7854e
4
- data.tar.gz: ddf8757341169f2e38bb076a9dfb5f5328a0de4e01ce58ec5b3263fdd3c47105
3
+ metadata.gz: d7eac7a6ba7767f6a3f84ee808fa4810eaa1843776695ab0225ddd6b77cf7a73
4
+ data.tar.gz: e9f7c0170fc2a8dbf443f1bac24874878ee0fbba7e0495bf65a8df969d3d86e6
5
5
  SHA512:
6
- metadata.gz: 243d774a05c2d5bfa27c8f98f51adac3ff13252cb108de085d8ac21b65977b8e8c7f9b78f4a58872b2a299c1e615b9fa6852cf27b564b567f6fa3d15dea2cf26
7
- data.tar.gz: 6665f726069148c0adc24947d570b67d2f56f00ad5ffeeb6653fe745401f14768b324c67711cdaf023e782a3fa61c2787050ed2087db6ca34410a65a5e02a8ec
6
+ metadata.gz: e4d14ac64e54e5c7245a9586dfb4899154793ea466f9564a510eb3dfe17a3a7229cf61e408445b38fec37500065b5e1ee725afa634284bea5538abac0766237f
7
+ data.tar.gz: e8fe3e1639a3f2ed087436610dd1653e775703c1c6cc83f7f52eb7d3fb46db554e7be790bc6bc2ddf18ec4e3c26dddbe1ec72e8f25603db1192e5a111d0f9543
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.13.5] - 2024-07-01
4
+ - Add Milvus#remove_texts() method
5
+ - Langchain::Assistant has a `state` now
6
+ - Misc fixes and improvements
7
+
8
+ ## [0.13.4] - 2024-06-16
9
+ - Fix Chroma#remove_texts() method
10
+ - Fix NewsRetriever Tool returning non UTF-8 characters
11
+ - Misc fixes and improvements
12
+
3
13
  ## [0.13.3] - 2024-06-03
4
14
  - New 🛠️ `Langchain::Tool::Tavily` to execute search (better than the GoogleSearch tool)
5
15
  - Remove `activesupport` dependency
data/README.md CHANGED
@@ -57,7 +57,7 @@ Langchain.rb wraps supported LLMs in a unified interface allowing you to easily
57
57
  #### Supported LLMs and features:
58
58
  | LLM providers | `embed()` | `complete()` | `chat()` | `summarize()` | Notes |
59
59
  | -------- |:------------------:| :-------: | :-----------------: | :-------: | :----------------- |
60
- | [OpenAI](https://openai.com/?utm_source=langchainrb&utm_medium=github) | ✅ | ✅ | ✅ | | Including Azure OpenAI |
60
+ | [OpenAI](https://openai.com/?utm_source=langchainrb&utm_medium=github) | ✅ | ✅ | ✅ | | Including Azure OpenAI |
61
61
  | [AI21](https://ai21.com/?utm_source=langchainrb&utm_medium=github) | ❌ | ✅ | ❌ | ✅ | |
62
62
  | [Anthropic](https://anthropic.com/?utm_source=langchainrb&utm_medium=github) | ❌ | ✅ | ✅ | ❌ | |
63
63
  | [AwsBedrock](https://aws.amazon.com/bedrock?utm_source=langchainrb&utm_medium=github) | ✅ | ✅ | ✅ | ❌ | Provides AWS, Cohere, AI21, Antropic and Stability AI models |
@@ -343,7 +343,7 @@ You can instantiate any other supported vector search database:
343
343
  client = Langchain::Vectorsearch::Chroma.new(...) # `gem "chroma-db", "~> 0.6.0"`
344
344
  client = Langchain::Vectorsearch::Epsilla.new(...) # `gem "epsilla-ruby", "~> 0.0.3"`
345
345
  client = Langchain::Vectorsearch::Hnswlib.new(...) # `gem "hnswlib", "~> 0.8.1"`
346
- client = Langchain::Vectorsearch::Milvus.new(...) # `gem "milvus", "~> 0.9.2"`
346
+ client = Langchain::Vectorsearch::Milvus.new(...) # `gem "milvus", "~> 0.9.3"`
347
347
  client = Langchain::Vectorsearch::Pinecone.new(...) # `gem "pinecone", "~> 0.1.6"`
348
348
  client = Langchain::Vectorsearch::Pgvector.new(...) # `gem "pgvector", "~> 0.2"`
349
349
  client = Langchain::Vectorsearch::Qdrant.new(...) # `gem "qdrant-ruby", "~> 0.9.3"`
@@ -15,7 +15,7 @@ module Langchain
15
15
  extend Forwardable
16
16
  def_delegators :thread, :messages, :messages=
17
17
 
18
- attr_reader :llm, :thread, :instructions
18
+ attr_reader :llm, :thread, :instructions, :state
19
19
  attr_accessor :tools
20
20
 
21
21
  SUPPORTED_LLMS = [
@@ -46,6 +46,7 @@ module Langchain
46
46
  @thread = thread || Langchain::Thread.new
47
47
  @tools = tools
48
48
  @instructions = instructions
49
+ @state = :ready
49
50
 
50
51
  raise ArgumentError, "Thread must be an instance of Langchain::Thread" unless @thread.is_a?(Langchain::Thread)
51
52
 
@@ -66,7 +67,10 @@ module Langchain
66
67
  # @return [Array<Langchain::Message>] The messages in the thread
67
68
  def add_message(content: nil, role: "user", tool_calls: [], tool_call_id: nil)
68
69
  message = build_message(role: role, content: content, tool_calls: tool_calls, tool_call_id: tool_call_id)
69
- thread.add_message(message)
70
+ messages = thread.add_message(message)
71
+ @state = :ready
72
+
73
+ messages
70
74
  end
71
75
 
72
76
  # Run the assistant
@@ -76,56 +80,12 @@ module Langchain
76
80
  def run(auto_tool_execution: false)
77
81
  if thread.messages.empty?
78
82
  Langchain.logger.warn("No messages in the thread")
83
+ @state = :completed
79
84
  return
80
85
  end
81
86
 
82
- running = true
83
-
84
- while running
85
- # TODO: I think we need to look at all messages and not just the last one.
86
- last_message = thread.messages.last
87
-
88
- if last_message.system?
89
- # Do nothing
90
- running = false
91
- elsif last_message.llm?
92
- if last_message.tool_calls.any?
93
- if auto_tool_execution
94
- run_tools(last_message.tool_calls)
95
- else
96
- # Maybe log and tell the user that there's outstanding tool calls?
97
- running = false
98
- end
99
- else
100
- # Last message was from the assistant without any tools calls.
101
- # Do nothing
102
- running = false
103
- end
104
- elsif last_message.user?
105
- # Run it!
106
- response = chat_with_llm
107
-
108
- if response.tool_calls.any?
109
- # Re-run the while(running) loop to process the tool calls
110
- running = true
111
- add_message(role: response.role, tool_calls: response.tool_calls)
112
- elsif response.chat_completion
113
- # Stop the while(running) loop and add the assistant's response to the thread
114
- running = false
115
- add_message(role: response.role, content: response.chat_completion)
116
- end
117
- elsif last_message.tool?
118
- # Run it!
119
- response = chat_with_llm
120
- running = true
121
-
122
- if response.tool_calls.any?
123
- add_message(role: response.role, tool_calls: response.tool_calls)
124
- elsif response.chat_completion
125
- add_message(role: response.role, content: response.chat_completion)
126
- end
127
- end
128
- end
87
+ @state = :in_progress
88
+ @state = handle_state until run_finished?(auto_tool_execution)
129
89
 
130
90
  thread.messages
131
91
  end
@@ -146,13 +106,7 @@ module Langchain
146
106
  # @param output [String] The output of the tool
147
107
  # @return [Array<Langchain::Message>] The messages in the thread
148
108
  def submit_tool_output(tool_call_id:, output:)
149
- tool_role = if llm.is_a?(Langchain::LLM::OpenAI)
150
- Langchain::Messages::OpenAIMessage::TOOL_ROLE
151
- elsif [Langchain::LLM::GoogleGemini, Langchain::LLM::GoogleVertexAI].include?(llm.class)
152
- Langchain::Messages::GoogleGeminiMessage::TOOL_ROLE
153
- elsif llm.is_a?(Langchain::LLM::Anthropic)
154
- Langchain::Messages::AnthropicMessage::TOOL_ROLE
155
- end
109
+ tool_role = determine_tool_role
156
110
 
157
111
  # TODO: Validate that `tool_call_id` is valid by scanning messages and checking if this tool call ID was invoked
158
112
  add_message(role: tool_role, content: output, tool_call_id: tool_call_id)
@@ -183,6 +137,114 @@ module Langchain
183
137
 
184
138
  private
185
139
 
140
+ # Check if the run is finished
141
+ #
142
+ # @param auto_tool_execution [Boolean] Whether or not to automatically run tools
143
+ # @return [Boolean] Whether the run is finished
144
+ def run_finished?(auto_tool_execution)
145
+ finished_states = [:completed, :failed]
146
+
147
+ requires_manual_action = (@state == :requires_action) && !auto_tool_execution
148
+ finished_states.include?(@state) || requires_manual_action
149
+ end
150
+
151
+ # Handle the current state and transition to the next state
152
+ #
153
+ # @param state [Symbol] The current state
154
+ # @return [Symbol] The next state
155
+ def handle_state
156
+ case @state
157
+ when :in_progress
158
+ process_latest_message
159
+ when :requires_action
160
+ execute_tools
161
+ end
162
+ end
163
+
164
+ # Process the latest message in the thread
165
+ #
166
+ # @return [Symbol] The next state
167
+ def process_latest_message
168
+ last_message = thread.messages.last
169
+
170
+ case last_message.standard_role
171
+ when :system
172
+ handle_system_message
173
+ when :llm
174
+ handle_llm_message
175
+ when :user, :tool
176
+ handle_user_or_tool_message
177
+ else
178
+ handle_unexpected_message
179
+ end
180
+ end
181
+
182
+ # Handle system message scenario
183
+ #
184
+ # @return [Symbol] The completed state
185
+ def handle_system_message
186
+ Langchain.logger.warn("At least one user message is required after a system message")
187
+ :completed
188
+ end
189
+
190
+ # Handle LLM message scenario
191
+ #
192
+ # @param auto_tool_execution [Boolean] Flag to indicate if tools should be executed automatically
193
+ # @return [Symbol] The next state
194
+ def handle_llm_message
195
+ thread.messages.last.tool_calls.any? ? :requires_action : :completed
196
+ end
197
+
198
+ # Handle unexpected message scenario
199
+ #
200
+ # @return [Symbol] The failed state
201
+ def handle_unexpected_message
202
+ Langchain.logger.error("Unexpected message role encountered: #{thread.messages.last.standard_role}")
203
+ :failed
204
+ end
205
+
206
+ # Handle user or tool message scenario by processing the LLM response
207
+ #
208
+ # @return [Symbol] The next state
209
+ def handle_user_or_tool_message
210
+ response = chat_with_llm
211
+ add_message(role: response.role, content: response.chat_completion, tool_calls: response.tool_calls)
212
+
213
+ if response.tool_calls.any?
214
+ :in_progress
215
+ elsif response.chat_completion
216
+ :completed
217
+ else
218
+ Langchain.logger.error("LLM response does not contain tool calls or chat completion")
219
+ :failed
220
+ end
221
+ end
222
+
223
+ # Execute the tools based on the tool calls in the last message
224
+ #
225
+ # @return [Symbol] The next state
226
+ def execute_tools
227
+ run_tools(thread.messages.last.tool_calls)
228
+ :in_progress
229
+ rescue => e
230
+ Langchain.logger.error("Error running tools: #{e.message}")
231
+ :failed
232
+ end
233
+
234
+ # Determine the tool role based on the LLM type
235
+ #
236
+ # @return [String] The tool role
237
+ def determine_tool_role
238
+ case llm
239
+ when Langchain::LLM::OpenAI
240
+ Langchain::Messages::OpenAIMessage::TOOL_ROLE
241
+ when Langchain::LLM::GoogleGemini, Langchain::LLM::GoogleVertexAI
242
+ Langchain::Messages::GoogleGeminiMessage::TOOL_ROLE
243
+ when Langchain::LLM::Anthropic
244
+ Langchain::Messages::AnthropicMessage::TOOL_ROLE
245
+ end
246
+ end
247
+
186
248
  # Call to the LLM#chat() method
187
249
  #
188
250
  # @return [Langchain::LLM::BaseResponse] The LLM response object
@@ -232,14 +294,6 @@ module Langchain
232
294
 
233
295
  submit_tool_output(tool_call_id: tool_call_id, output: output)
234
296
  end
235
-
236
- response = chat_with_llm
237
-
238
- if response.tool_calls.any?
239
- add_message(role: response.role, tool_calls: response.tool_calls)
240
- elsif response.chat_completion
241
- add_message(role: response.role, content: response.chat_completion)
242
- end
243
297
  end
244
298
 
245
299
  # Extract the tool call information from the OpenAI tool call hash
@@ -7,10 +7,44 @@ module Langchain
7
7
 
8
8
  # Check if the message came from a user
9
9
  #
10
- # @param [Boolean] true/false whether the message came from a user
10
+ # @return [Boolean] true/false whether the message came from a user
11
11
  def user?
12
12
  role == "user"
13
13
  end
14
+
15
+ # Check if the message came from an LLM
16
+ #
17
+ # @raise NotImplementedError if the subclass does not implement this method
18
+ def llm?
19
+ raise NotImplementedError, "Class #{self.class.name} must implement the method 'llm?'"
20
+ end
21
+
22
+ # Check if the message is a tool call
23
+ #
24
+ # @raise NotImplementedError if the subclass does not implement this method
25
+ def tool?
26
+ raise NotImplementedError, "Class #{self.class.name} must implement the method 'tool?'"
27
+ end
28
+
29
+ # Check if the message is a system prompt
30
+ #
31
+ # @raise NotImplementedError if the subclass does not implement this method
32
+ def system?
33
+ raise NotImplementedError, "Class #{self.class.name} must implement the method 'system?'"
34
+ end
35
+
36
+ # Returns the standardized role symbol based on the specific role methods
37
+ #
38
+ # @return [Symbol] the standardized role symbol (:system, :llm, :tool, :user, or :unknown)
39
+ def standard_role
40
+ return :user if user?
41
+ return :llm if llm?
42
+ return :tool if tool?
43
+ return :system if system?
44
+
45
+ # TODO: Should we return :unknown or raise an error?
46
+ :unknown
47
+ end
14
48
  end
15
49
  end
16
50
  end
@@ -35,8 +35,8 @@ module Langchain
35
35
  @logger.respond_to?(method, include_private)
36
36
  end
37
37
 
38
- def method_missing(method, *args, **kwargs, &block)
39
- return @logger.send(method, *args, **kwargs, &block) unless @levels.include?(method)
38
+ def method_missing(method, *args, **kwargs, &)
39
+ return @logger.send(method, *args, **kwargs, &) unless @levels.include?(method)
40
40
 
41
41
  for_class = kwargs.delete(:for)
42
42
  for_class_name = for_class&.name
@@ -18,7 +18,9 @@ module Langchain::LLM
18
18
 
19
19
  chat_parameters.update(
20
20
  model: {default: @defaults[:chat_completion_model_name]},
21
- temperature: {default: @defaults[:temperature]}
21
+ temperature: {default: @defaults[:temperature]},
22
+ generation_config: {default: nil},
23
+ safety_settings: {default: nil}
22
24
  )
23
25
  chat_parameters.remap(
24
26
  messages: :contents,
@@ -42,13 +44,25 @@ module Langchain::LLM
42
44
  raise ArgumentError.new("messages argument is required") if Array(params[:messages]).empty?
43
45
 
44
46
  parameters = chat_parameters.to_params(params)
45
- parameters[:generation_config] = {temperature: parameters.delete(:temperature)} if parameters[:temperature]
47
+ parameters[:generation_config] ||= {}
48
+ parameters[:generation_config][:temperature] ||= parameters[:temperature] if parameters[:temperature]
49
+ parameters.delete(:temperature)
50
+ parameters[:generation_config][:top_p] ||= parameters[:top_p] if parameters[:top_p]
51
+ parameters.delete(:top_p)
52
+ parameters[:generation_config][:top_k] ||= parameters[:top_k] if parameters[:top_k]
53
+ parameters.delete(:top_k)
54
+ parameters[:generation_config][:max_output_tokens] ||= parameters[:max_tokens] if parameters[:max_tokens]
55
+ parameters.delete(:max_tokens)
56
+ parameters[:generation_config][:response_mime_type] ||= parameters[:response_format] if parameters[:response_format]
57
+ parameters.delete(:response_format)
58
+ parameters[:generation_config][:stop_sequences] ||= parameters[:stop] if parameters[:stop]
59
+ parameters.delete(:stop)
46
60
 
47
61
  uri = URI("https://generativelanguage.googleapis.com/v1beta/models/#{parameters[:model]}:generateContent?key=#{api_key}")
48
62
 
49
63
  request = Net::HTTP::Post.new(uri)
50
64
  request.content_type = "application/json"
51
- request.body = parameters.to_json
65
+ request.body = Langchain::Utils::HashTransformer.deep_transform_keys(parameters) { |key| Langchain::Utils::HashTransformer.camelize_lower(key.to_s).to_sym }.to_json
52
66
 
53
67
  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
54
68
  http.request(request)
@@ -64,7 +64,7 @@ module Langchain::LLM
64
64
  # Generate a completion for a given prompt
65
65
  #
66
66
  # @param prompt [String] The prompt to generate a completion for
67
- # @return [Langchain::LLM::ReplicateResponse] Reponse object
67
+ # @return [Langchain::LLM::ReplicateResponse] Response object
68
68
  #
69
69
  def complete(prompt:, **params)
70
70
  response = completion_model.predict(prompt: prompt)
@@ -90,7 +90,9 @@ module Langchain
90
90
  private
91
91
 
92
92
  def load_from_url
93
- URI.parse(@path).open
93
+ unescaped_url = URI.decode_www_form_component(@path)
94
+ escaped_url = URI::DEFAULT_PARSER.escape(unescaped_url)
95
+ URI.parse(escaped_url).open
94
96
  end
95
97
 
96
98
  def load_from_path
@@ -105,7 +107,7 @@ module Langchain
105
107
  # Only load and add to result files with supported extensions
106
108
  Langchain::Loader.new(file, @options).load(&block)
107
109
  rescue
108
- UnknownFormatError nil
110
+ UnknownFormatError.new("Unknown format: #{source_type}")
109
111
  end.flatten.compact
110
112
  end
111
113
  # rubocop:enable Style/ArgumentsForwarding
@@ -123,7 +125,7 @@ module Langchain
123
125
  end
124
126
 
125
127
  def processor_klass
126
- raise UnknownFormatError unless (kind = find_processor)
128
+ raise UnknownFormatError.new("Unknown format: #{source_type}") unless (kind = find_processor)
127
129
 
128
130
  Langchain::Processors.const_get(kind)
129
131
  end
@@ -126,7 +126,10 @@ module Langchain::Tool
126
126
  request["Content-Type"] = "application/json"
127
127
 
128
128
  response = http.request(request)
129
- response.body
129
+ response
130
+ .body
131
+ # Remove non-UTF-8 characters
132
+ .force_encoding(Encoding::UTF_8)
130
133
  end
131
134
  end
132
135
  end
@@ -0,0 +1,25 @@
1
+ module Langchain
2
+ module Utils
3
+ class HashTransformer
4
+ # Converts a string to camelCase
5
+ def self.camelize_lower(str)
6
+ str.split("_").inject([]) { |buffer, e| buffer.push(buffer.empty? ? e : e.capitalize) }.join
7
+ end
8
+
9
+ # Recursively transforms the keys of a hash to camel case
10
+ def self.deep_transform_keys(hash, &block)
11
+ case hash
12
+ when Hash
13
+ hash.each_with_object({}) do |(key, value), result|
14
+ new_key = block.call(key)
15
+ result[new_key] = deep_transform_keys(value, &block)
16
+ end
17
+ when Array
18
+ hash.map { |item| deep_transform_keys(item, &block) }
19
+ else
20
+ hash
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -64,7 +64,9 @@ module Langchain::Vectorsearch
64
64
  # @param ids [Array<String>] The list of ids to remove
65
65
  # @return [Hash] The response from the server
66
66
  def remove_texts(ids:)
67
- collection.delete(ids)
67
+ collection.delete(
68
+ ids: ids.map(&:to_s)
69
+ )
68
70
  end
69
71
 
70
72
  # Create the collection with the default schema
@@ -122,7 +124,7 @@ module Langchain::Vectorsearch
122
124
  # @param k [Integer] The number of results to have in context
123
125
  # @yield [String] Stream responses back one String at a time
124
126
  # @return [String] The answer to the question
125
- def ask(question:, k: 4, &block)
127
+ def ask(question:, k: 4, &)
126
128
  search_results = similarity_search(query: question, k: k)
127
129
 
128
130
  context = search_results.map do |result|
@@ -134,7 +136,7 @@ module Langchain::Vectorsearch
134
136
  prompt = generate_rag_prompt(question: question, context: context)
135
137
 
136
138
  messages = [{role: "user", content: prompt}]
137
- response = llm.chat(messages: messages, &block)
139
+ response = llm.chat(messages: messages, &)
138
140
 
139
141
  response.context = context
140
142
  response
@@ -143,7 +143,7 @@ module Langchain::Vectorsearch
143
143
  # @param k [Integer] The number of results to have in context
144
144
  # @yield [String] Stream responses back one String at a time
145
145
  # @return [String] The answer to the question
146
- def ask(question:, k: 4, &block)
146
+ def ask(question:, k: 4, &)
147
147
  search_results = similarity_search(query: question, k: k)
148
148
 
149
149
  context = search_results.map do |result|
@@ -153,7 +153,7 @@ module Langchain::Vectorsearch
153
153
  prompt = generate_rag_prompt(question: question, context: context)
154
154
 
155
155
  messages = [{role: "user", content: prompt}]
156
- response = llm.chat(messages: messages, &block)
156
+ response = llm.chat(messages: messages, &)
157
157
 
158
158
  response.context = context
159
159
  response
@@ -129,7 +129,7 @@ module Langchain::Vectorsearch
129
129
  # @param k [Integer] The number of results to have in context
130
130
  # @yield [String] Stream responses back one String at a time
131
131
  # @return [String] The answer to the question
132
- def ask(question:, k: 4, &block)
132
+ def ask(question:, k: 4, &)
133
133
  search_results = similarity_search(query: question, k: k)
134
134
 
135
135
  context = search_results.map do |result|
@@ -140,7 +140,7 @@ module Langchain::Vectorsearch
140
140
  prompt = generate_rag_prompt(question: question, context: context)
141
141
 
142
142
  messages = [{role: "user", content: prompt}]
143
- response = llm.chat(messages: messages, &block)
143
+ response = llm.chat(messages: messages, &)
144
144
 
145
145
  response.context = context
146
146
  response
@@ -58,7 +58,7 @@ module Langchain::Vectorsearch
58
58
  #
59
59
  # @param query [String] The text to search for
60
60
  # @param k [Integer] The number of results to return
61
- # @return [Array] Results in the format `[[id1, distance3], [id2, distance2]]`
61
+ # @return [Array] Results in the format `[[id1, id2], [distance1, distance2]]`
62
62
  #
63
63
  def similarity_search(
64
64
  query:,
@@ -77,7 +77,7 @@ module Langchain::Vectorsearch
77
77
  #
78
78
  # @param embedding [Array<Float>] The embedding to search for
79
79
  # @param k [Integer] The number of results to return
80
- # @return [Array] Results in the format `[[id1, distance3], [id2, distance2]]`
80
+ # @return [Array] Results in the format `[[id1, id2], [distance1, distance2]]`
81
81
  #
82
82
  def similarity_search_by_vector(
83
83
  embedding:,
@@ -6,7 +6,7 @@ module Langchain::Vectorsearch
6
6
  # Wrapper around Milvus REST APIs.
7
7
  #
8
8
  # Gem requirements:
9
- # gem "milvus", "~> 0.9.2"
9
+ # gem "milvus", "~> 0.9.3"
10
10
  #
11
11
  # Usage:
12
12
  # milvus = Langchain::Vectorsearch::Milvus.new(url:, index_name:, llm:, api_key:)
@@ -39,6 +39,21 @@ module Langchain::Vectorsearch
39
39
  )
40
40
  end
41
41
 
42
+ # Deletes a list of texts in the index
43
+ #
44
+ # @param ids [Array<Integer>] The ids of texts to delete
45
+ # @return [Boolean] The response from the server
46
+ def remove_texts(ids:)
47
+ raise ArgumentError, "ids must be an array" unless ids.is_a?(Array)
48
+ # Convert ids to integers if strings are passed
49
+ ids = ids.map(&:to_i)
50
+
51
+ client.entities.delete(
52
+ collection_name: index_name,
53
+ expression: "id in #{ids}"
54
+ )
55
+ end
56
+
42
57
  # TODO: Add update_texts method
43
58
 
44
59
  # Create default schema
@@ -83,7 +98,7 @@ module Langchain::Vectorsearch
83
98
  # @return [Boolean] The response from the server
84
99
  def create_default_index
85
100
  client.indices.create(
86
- collection_name: "Documents",
101
+ collection_name: index_name,
87
102
  field_name: "vectors",
88
103
  extra_params: [
89
104
  {key: "metric_type", value: "L2"},
@@ -141,7 +156,7 @@ module Langchain::Vectorsearch
141
156
  # @param k [Integer] The number of results to have in context
142
157
  # @yield [String] Stream responses back one String at a time
143
158
  # @return [String] The answer to the question
144
- def ask(question:, k: 4, &block)
159
+ def ask(question:, k: 4, &)
145
160
  search_results = similarity_search(query: question, k: k)
146
161
 
147
162
  content_field = search_results.dig("results", "fields_data").select { |field| field.dig("field_name") == "content" }
@@ -152,7 +167,7 @@ module Langchain::Vectorsearch
152
167
  prompt = generate_rag_prompt(question: question, context: context)
153
168
 
154
169
  messages = [{role: "user", content: prompt}]
155
- response = llm.chat(messages: messages, &block)
170
+ response = llm.chat(messages: messages, &)
156
171
 
157
172
  response.context = context
158
173
  response
@@ -146,7 +146,7 @@ module Langchain::Vectorsearch
146
146
  # @param k [Integer] The number of results to have in context
147
147
  # @yield [String] Stream responses back one String at a time
148
148
  # @return [String] The answer to the question
149
- def ask(question:, k: 4, &block)
149
+ def ask(question:, k: 4, &)
150
150
  search_results = similarity_search(query: question, k: k)
151
151
 
152
152
  context = search_results.map do |result|
@@ -157,7 +157,7 @@ module Langchain::Vectorsearch
157
157
  prompt = generate_rag_prompt(question: question, context: context)
158
158
 
159
159
  messages = [{role: "user", content: prompt}]
160
- response = llm.chat(messages: messages, &block)
160
+ response = llm.chat(messages: messages, &)
161
161
 
162
162
  response.context = context
163
163
  response
@@ -171,7 +171,7 @@ module Langchain::Vectorsearch
171
171
  # @param filter [String] The filter to use
172
172
  # @yield [String] Stream responses back one String at a time
173
173
  # @return [String] The answer to the question
174
- def ask(question:, namespace: "", filter: nil, k: 4, &block)
174
+ def ask(question:, namespace: "", filter: nil, k: 4, &)
175
175
  search_results = similarity_search(query: question, namespace: namespace, filter: filter, k: k)
176
176
 
177
177
  context = search_results.map do |result|
@@ -182,7 +182,7 @@ module Langchain::Vectorsearch
182
182
  prompt = generate_rag_prompt(question: question, context: context)
183
183
 
184
184
  messages = [{role: "user", content: prompt}]
185
- response = llm.chat(messages: messages, &block)
185
+ response = llm.chat(messages: messages, &)
186
186
 
187
187
  response.context = context
188
188
  response
@@ -137,7 +137,7 @@ module Langchain::Vectorsearch
137
137
  # @param k [Integer] The number of results to have in context
138
138
  # @yield [String] Stream responses back one String at a time
139
139
  # @return [String] The answer to the question
140
- def ask(question:, k: 4, &block)
140
+ def ask(question:, k: 4, &)
141
141
  search_results = similarity_search(query: question, k: k)
142
142
 
143
143
  context = search_results.map do |result|
@@ -148,7 +148,7 @@ module Langchain::Vectorsearch
148
148
  prompt = generate_rag_prompt(question: question, context: context)
149
149
 
150
150
  messages = [{role: "user", content: prompt}]
151
- response = llm.chat(messages: messages, &block)
151
+ response = llm.chat(messages: messages, &)
152
152
 
153
153
  response.context = context
154
154
  response
@@ -143,7 +143,7 @@ module Langchain::Vectorsearch
143
143
  # @param k [Integer] The number of results to have in context
144
144
  # @yield [String] Stream responses back one String at a time
145
145
  # @return [Hash] The answer
146
- def ask(question:, k: 4, &block)
146
+ def ask(question:, k: 4, &)
147
147
  search_results = similarity_search(query: question, k: k)
148
148
 
149
149
  context = search_results.map do |result|
@@ -154,7 +154,7 @@ module Langchain::Vectorsearch
154
154
  prompt = generate_rag_prompt(question: question, context: context)
155
155
 
156
156
  messages = [{role: "user", content: prompt}]
157
- response = llm.chat(messages: messages, &block)
157
+ response = llm.chat(messages: messages, &)
158
158
 
159
159
  response.context = context
160
160
  response
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Langchain
4
- VERSION = "0.13.3"
4
+ VERSION = "0.13.5"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: langchainrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.3
4
+ version: 0.13.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrei Bondarev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-06-03 00:00:00.000000000 Z
11
+ date: 2024-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: baran
@@ -408,14 +408,14 @@ dependencies:
408
408
  requirements:
409
409
  - - "~>"
410
410
  - !ruby/object:Gem::Version
411
- version: 0.9.2
411
+ version: 0.9.3
412
412
  type: :development
413
413
  prerelease: false
414
414
  version_requirements: !ruby/object:Gem::Requirement
415
415
  requirements:
416
416
  - - "~>"
417
417
  - !ruby/object:Gem::Version
418
- version: 0.9.2
418
+ version: 0.9.3
419
419
  - !ruby/object:Gem::Dependency
420
420
  name: llama_cpp
421
421
  requirement: !ruby/object:Gem::Requirement
@@ -809,6 +809,7 @@ files:
809
809
  - lib/langchain/tool/wikipedia/wikipedia.json
810
810
  - lib/langchain/tool/wikipedia/wikipedia.rb
811
811
  - lib/langchain/utils/cosine_similarity.rb
812
+ - lib/langchain/utils/hash_transformer.rb
812
813
  - lib/langchain/utils/token_length/ai21_validator.rb
813
814
  - lib/langchain/utils/token_length/base_validator.rb
814
815
  - lib/langchain/utils/token_length/cohere_validator.rb
@@ -834,8 +835,8 @@ licenses:
834
835
  - MIT
835
836
  metadata:
836
837
  homepage_uri: https://rubygems.org/gems/langchainrb
837
- source_code_uri: https://github.com/andreibondarev/langchainrb
838
- changelog_uri: https://github.com/andreibondarev/langchainrb/CHANGELOG.md
838
+ source_code_uri: https://github.com/patterns-ai-core/langchainrb
839
+ changelog_uri: https://github.com/patterns-ai-core/langchainrb/blob/main/CHANGELOG.md
839
840
  documentation_uri: https://rubydoc.info/gems/langchainrb
840
841
  post_install_message:
841
842
  rdoc_options: []
@@ -852,7 +853,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
852
853
  - !ruby/object:Gem::Version
853
854
  version: '0'
854
855
  requirements: []
855
- rubygems_version: 3.5.11
856
+ rubygems_version: 3.5.14
856
857
  signing_key:
857
858
  specification_version: 4
858
859
  summary: Build LLM-backed Ruby applications with Ruby's Langchain.rb