From ba8ef7a73a62f533a8153334cfdfe94d6ca489d6 Mon Sep 17 00:00:00 2001 From: rodrigo toledo Date: Wed, 10 Dec 2025 07:29:33 -0300 Subject: [PATCH 1/3] Bump version to 0.1.13 - Add score_threshold parameter to similarity_search, similarity_search_by_vector, and ask methods - Improve test coverage with comprehensive tests for score_threshold functionality and ask method --- .tool-versions | 2 +- CHANGELOG.md | 17 ++++ Gemfile.lock | 9 +- .../vectorsearch/pgvector.rb | 28 ++++-- lib/langchainrb_rails/active_record/hooks.rb | 10 ++- lib/langchainrb_rails/version.rb | 2 +- .../vectorsearch/pgvector_spec.rb | 88 ++++++++++++++++--- 7 files changed, 126 insertions(+), 30 deletions(-) diff --git a/.tool-versions b/.tool-versions index 0583546..3f03c7a 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -ruby 3.2.2 \ No newline at end of file +ruby 3.4.7 diff --git a/CHANGELOG.md b/CHANGELOG.md index 55ef32a..fdc6519 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,43 +9,60 @@ - [DOCS]: Documentation changes. No changes to the library's behavior. - [SECURITY]: A change which fixes a security vulnerability. +## [0.1.13] - 2025-12-10 + +- [FEATURE]: Add `score_threshold` parameter to `similarity_search`, `similarity_search_by_vector`, and `ask` methods to filter out irrelevant results based on similarity score +- [FEATURE]: Improve test coverage with comprehensive tests for the new `score_threshold` functionality and `ask` method + ## [0.1.12] - 2024-09-20 + - Adding `rails g langchainrb_rails:assistant --llm=...` generator - Adding `rails g langchainrb_rails:prompt` generator ## [0.1.11] - 2024-06-16 + - Add destroy_from_vectorsearch hook ## [0.1.10] - 2024-05-20 ## [0.1.9] - 2024-04-19 + - Bump langchainrb gem to include v0.11.x - Remove pg_vector Overriding Operator Constants ## [0.1.8] - 2024-03-16 + - Bump langchainrb gem ## [0.1.7] - 2024-01-29 + - Fix Pgvector#ask method ## [0.1.6] - 2024-01-25 + - Fix bug when multiple ActiveRecord models use vectorsearch - Bump langchainrb version - Avoid extra query when Pgvector is used ## [0.1.5] - 2023-11-30 + - Qdrant vectorsearch generator ## [0.1.4] - 2023-11-20 + - Bugfix AR integration when using vectorsearch other than Pgvector ## [0.1.3] - 2023-11-01 + - Pgvector vectorsearch generator ## [0.1.2] - 2023-10-27 + - Pinecone vectorsearch generator ## [0.1.1] - 2023-10-23 ## [0.1.0] - 2023-10-22 + - Initial release + diff --git a/Gemfile.lock b/Gemfile.lock index a35c67b..b3ec51b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -141,7 +141,6 @@ GEM matrix (0.4.2) method_source (1.0.0) mini_mime (1.1.5) - mini_portile2 (2.8.8) minitest (5.25.4) mutex_m (0.3.0) net-imap (0.4.18) @@ -154,12 +153,9 @@ GEM net-smtp (0.5.0) net-protocol nio4r (2.7.4) - nokogiri (1.17.2) - mini_portile2 (~> 2.8.2) + nokogiri (1.18.10-x86_64-darwin) racc (~> 1.4) - nokogiri (1.17.2-x86_64-darwin) - racc (~> 1.4) - nokogiri (1.17.2-x86_64-linux) + nokogiri (1.18.10-x86_64-linux-gnu) racc (~> 1.4) parallel (1.26.3) parser (3.3.6.0) @@ -279,7 +275,6 @@ GEM zeitwerk (2.6.18) PLATFORMS - ruby x86_64-darwin-19 x86_64-darwin-22 x86_64-linux diff --git a/lib/langchainrb_overrides/vectorsearch/pgvector.rb b/lib/langchainrb_overrides/vectorsearch/pgvector.rb index ed7c9a5..deddd5d 100644 --- a/lib/langchainrb_overrides/vectorsearch/pgvector.rb +++ b/lib/langchainrb_overrides/vectorsearch/pgvector.rb @@ -79,14 +79,16 @@ def destroy_default_schema # Search for similar texts in the index # @param query [String] The text to search for # @param k [Integer] The number of top results to return + # @param score_threshold [Float] The minimum similarity score (lower distance) to include in results # @return [Array] The results of the search # TODO - drop the named "query:" param so it is the same interface as #ask? - def similarity_search(query:, k: 4) + def similarity_search(query:, k: 4, score_threshold: nil) embedding = llm.embed(text: query).embedding similarity_search_by_vector( embedding: embedding, - k: k + k: k, + score_threshold: score_threshold ) end @@ -94,23 +96,33 @@ def similarity_search(query:, k: 4) # You must generate your own vector using the same LLM that generated the embeddings stored in the Vectorsearch DB. # @param embedding [Array] The vector to search for # @param k [Integer] The number of top results to return + # @param score_threshold [Float] The minimum similarity score (lower distance) to include in results # @return [Array] The results of the search # TODO - drop the named "embedding:" param so it is the same interface as #ask? - def similarity_search_by_vector(embedding:, k: 4) - model - .nearest_neighbors(:embedding, embedding, distance: operator) - .limit(k) + def similarity_search_by_vector(embedding:, k: 4, score_threshold: nil) + query = model.nearest_neighbors(:embedding, embedding, distance: operator) + + if score_threshold + # Fetch more results than needed and filter in Ruby to avoid depending on virtual columns + candidates = query.limit(k + 5) + filtered = candidates.select { |r| r.neighbor_distance <= score_threshold }.first(k) + ids = filtered.map(&:id) + model.where(id: ids).order(Arel.sql("array_position(ARRAY#{ids.inspect}, id)")) + else + query.limit(k) + end end # Ask a question and return the answer # @param question [String] The question to ask # @param k [Integer] The number of results to have in context + # @param score_threshold [Float] The minimum similarity score to include in results # @yield [String] Stream responses back one String at a time # @return [String] The answer to the question - def ask(question:, k: 4, &block) + def ask(question:, k: 4, score_threshold: nil, &block) # Noisy as the embedding column has a lot of data ActiveRecord::Base.logger.silence do - search_results = similarity_search(query: question, k: k) + search_results = similarity_search(query: question, k: k, score_threshold: score_threshold) context = search_results.map do |result| result.as_vector diff --git a/lib/langchainrb_rails/active_record/hooks.rb b/lib/langchainrb_rails/active_record/hooks.rb index fca1e39..e7af729 100644 --- a/lib/langchainrb_rails/active_record/hooks.rb +++ b/lib/langchainrb_rails/active_record/hooks.rb @@ -105,11 +105,13 @@ def embed! # # @param query [String] The query to search for # @param k [Integer] The number of results to return + # @param score_threshold [Float] The minimum similarity score to include in results # @return [ActiveRecord::Relation] The ActiveRecord relation - def similarity_search(query, k: 1) + def similarity_search(query, k: 1, score_threshold: nil) records = class_variable_get(:@@provider).similarity_search( query: query, - k: k + k: k, + score_threshold: score_threshold ) return records if LangchainrbRails.config.vectorsearch.is_a?(Langchain::Vectorsearch::Pgvector) @@ -123,13 +125,15 @@ def similarity_search(query, k: 1) # # @param question [String] The question to ask # @param k [Integer] The number of results to have in context + # @param score_threshold [Float] The minimum similarity score to include in results # @yield [String] Stream responses back one String at a time # @return [String] The answer to the question # standard:disable Style/ArgumentsForwarding - def ask(question, k: 4, &block) + def ask(question, k: 4, score_threshold: nil, &block) class_variable_get(:@@provider).ask( question: question, k: k, + score_threshold: score_threshold, &block ).chat_completion end diff --git a/lib/langchainrb_rails/version.rb b/lib/langchainrb_rails/version.rb index 786d6a1..c287871 100644 --- a/lib/langchainrb_rails/version.rb +++ b/lib/langchainrb_rails/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module LangchainrbRails - VERSION = "0.1.12" + VERSION = "0.1.13" end diff --git a/spec/langchainrb_overrides/vectorsearch/pgvector_spec.rb b/spec/langchainrb_overrides/vectorsearch/pgvector_spec.rb index 65df1d2..5b03f2d 100644 --- a/spec/langchainrb_overrides/vectorsearch/pgvector_spec.rb +++ b/spec/langchainrb_overrides/vectorsearch/pgvector_spec.rb @@ -1,27 +1,95 @@ # frozen_string_literal: true RSpec.describe Langchain::Vectorsearch::Pgvector do - let(:llm) { Langchain::LLM::OpenAI.new(api_key: "123") } + let(:llm) { double("LLM") } + let(:model) { double("Model") } subject { described_class.new(llm: llm) } - describe "#add_texts" do + before do + subject.model = model end - describe "#update_text" do - end - - describe "#create_default_schema" do - end + describe "#similarity_search" do + it "passes score_threshold to similarity_search_by_vector" do + allow(llm).to receive(:embed).and_return(double(embedding: [0.1, 0.2])) + allow(model).to receive(:nearest_neighbors).and_return(double(limit: [])) + allow(model).to receive(:where).and_return([]) - describe "#destroy_default_schema" do - end + expect(subject).to receive(:similarity_search_by_vector).with(embedding: [0.1, 0.2], k: 4, score_threshold: 0.5) - describe "#similarity_search" do + subject.similarity_search(query: "test", k: 4, score_threshold: 0.5) + end end describe "#similarity_search_by_vector" do + let(:query) { double("Query") } + let(:candidates) { double("Candidates") } + let(:filtered) { [double(id: 1, neighbor_distance: 0.3), double(id: 2, neighbor_distance: 0.4)] } + + before do + allow(model).to receive(:nearest_neighbors).and_return(query) + end + + context "without score_threshold" do + it "returns query.limit(k)" do + allow(query).to receive(:limit).with(4).and_return(:result) + + result = subject.similarity_search_by_vector(embedding: [0.1, 0.2], k: 4) + + expect(result).to eq(:result) + end + end + + context "with score_threshold" do + it "filters candidates and returns ordered results" do + allow(query).to receive(:limit).with(9).and_return(candidates) # k + 5 = 9 + allow(candidates).to receive(:select).and_return(filtered) + allow(filtered).to receive(:first).with(4).and_return(filtered) + allow(model).to receive(:where).with(id: [1, 2]).and_return(double(order: :ordered_result)) + + result = subject.similarity_search_by_vector(embedding: [0.1, 0.2], k: 4, score_threshold: 0.5) + + expect(result).to eq(:ordered_result) + end + end end describe "#ask" do + it "passes score_threshold to similarity_search and processes results" do + # Mock embedding + allow(llm).to receive(:embed).and_return(double(embedding: [0.1, 0.2])) + + # Mock nearest_neighbors and query chain + query = double("Query") + allow(model).to receive(:nearest_neighbors).and_return(query) + allow(query).to receive(:limit).and_return([]) + + # Mock search results + record1 = double("Record1", as_vector: "Vector 1") + record2 = double("Record2", as_vector: "Vector 2") + search_results = [record1, record2] + + # Mock similarity_search to return the results + allow(subject).to receive(:similarity_search).and_return(search_results) + + # Mock logger silence + logger = double("Logger") + allow(ActiveRecord::Base).to receive(:logger).and_return(logger) + allow(logger).to receive(:silence).and_yield + + # Mock generate_rag_prompt + allow(subject).to receive(:generate_rag_prompt).and_return("Mocked prompt") + + # Mock llm.chat + chat_response = double("ChatResponse", chat_completion: "Mocked answer") + allow(llm).to receive(:chat).and_return(chat_response) + + result = subject.ask(question: "question", k: 4, score_threshold: 0.5) + + expect(subject).to have_received(:similarity_search).with(query: "question", k: 4, score_threshold: 0.5) + expect(subject).to have_received(:generate_rag_prompt).with(question: "question", context: "Vector 1\n---\nVector 2") + expect(llm).to have_received(:chat).with(messages: [{role: "user", content: "Mocked prompt"}]) + expect(result.chat_completion).to eq("Mocked answer") + end end end From b1d94ac49bcbed00829881332286b75256e633f9 Mon Sep 17 00:00:00 2001 From: rodrigo toledo Date: Wed, 10 Dec 2025 07:30:48 -0300 Subject: [PATCH 2/3] Add langchainrb_rails-0.1.13.gem binary file --- Gemfile.lock | 2 +- langchainrb_rails-0.1.13.gem | Bin 0 -> 21504 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 langchainrb_rails-0.1.13.gem diff --git a/Gemfile.lock b/Gemfile.lock index b3ec51b..c5273bd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - langchainrb_rails (0.1.12) + langchainrb_rails (0.1.13) langchainrb (>= 0.19) GEM diff --git a/langchainrb_rails-0.1.13.gem b/langchainrb_rails-0.1.13.gem new file mode 100644 index 0000000000000000000000000000000000000000..1d86f975764cdd5d815b13ac46cab29f3a7bd4de GIT binary patch literal 21504 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 literal 0 HcmV?d00001 From 44f0a213eaf38d6928a29698aabcacc9c51dfe48 Mon Sep 17 00:00:00 2001 From: rodrigo toledo Date: Wed, 10 Dec 2025 07:44:59 -0300 Subject: [PATCH 3/3] Fix gemspec to exclude .gem files from file list - Added exclusion for .gem files to prevent circular reference error - Fixes 'contains itself' validation error when building/installing gem --- langchainrb_rails.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/langchainrb_rails.gemspec b/langchainrb_rails.gemspec index 3c20f8d..37d4af9 100644 --- a/langchainrb_rails.gemspec +++ b/langchainrb_rails.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |spec| # The `git ls-files -z` loads the files in the RubyGem that have been added into git. spec.files = Dir.chdir(__dir__) do `git ls-files -z`.split("\x0").reject do |f| - (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|circleci)|appveyor)}) + (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|circleci)|appveyor)}) || f.match?(/\.gem$/) end end spec.bindir = "exe"