smart_rag 0.2.2 → 0.2.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ed27c04c401121b0c7f8fc5650abf78602e3f930914e48bafb2749894c3f3c0
4
- data.tar.gz: e7a4f56e7a9a0cc7b9930fb39e097898c812ba9a3e6ce702a28a4b51da915551
3
+ metadata.gz: 126c6c21c530311732722fc39ddad72632a9fb64c6dcdb8a467665d92871f44e
4
+ data.tar.gz: '09cab17c6e5aa7cef64548ef95833e74af40ca45659e6142a4125a680c89426d'
5
5
  SHA512:
6
- metadata.gz: 8eb13a7acab801fa0db31dc74d7a86caa9bee9cb423fa8646e44581673e6122d7f3e3a58deaf74635393fe763b48208f84976fa2931b1eab5a814a45f4134d5a
7
- data.tar.gz: cf1857fc42076bfaab4a687364ed21172badef86c366e2919a4b3aad748ee4f45946e8c4549b221f69769aa22b8e9e828939c3c7228741b97e1f49c34d2afc8a
6
+ metadata.gz: 30102eda862fa9d04aec069b1a0454d2b7fdef78eb5a9684e82509f096af40369e4a20982b63d9cb2672635ba3be00401e90a7beebca36dbdd76825ef453e005
7
+ data.tar.gz: 6b2b5b5f58d4168349e36f9622aa36c12948c3cdc77b9194db2eb2badb98b3a4900ee009f668b4979c334bdea051ae3d59e79d71c2b1d610f89a90f846b2a548
@@ -321,8 +321,15 @@ module SmartRAG
321
321
  raise e
322
322
  end
323
323
 
324
+ # Read a text file as UTF-8.
325
+ #
326
+ # File.binread returns an ASCII-8BIT string, and transcoding that to UTF-8
327
+ # with `undef: :replace, replace: ''` silently deletes every high byte —
328
+ # i.e. it deletes all non-ASCII text (CJK included). Declare the encoding
329
+ # of the bytes instead of transcoding from binary; `scrub` then replaces
330
+ # only genuinely invalid byte sequences.
324
331
  def read_text_file(file_path)
325
- normalize_text_content(File.binread(file_path))
332
+ normalize_text_content(File.read(file_path, mode: 'rb').force_encoding(Encoding::UTF_8))
326
333
  rescue StandardError => e
327
334
  @logger.error "Failed to read text file #{file_path}: #{e.message}"
328
335
  raise e
@@ -333,10 +340,16 @@ module SmartRAG
333
340
 
334
341
  normalized = content.is_a?(String) ? content.dup : content.to_s
335
342
 
336
- begin
337
- normalized = normalized.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: '')
338
- rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError
339
- normalized = normalized.force_encoding(Encoding::UTF_8).scrub
343
+ if normalized.encoding == Encoding::ASCII_8BIT
344
+ # Bytes of unknown encoding: reinterpret as UTF-8 instead of dropping
345
+ # everything above 0x7F.
346
+ normalized = normalized.force_encoding(Encoding::UTF_8)
347
+ elsif normalized.encoding != Encoding::UTF_8
348
+ begin
349
+ normalized = normalized.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: '')
350
+ rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError
351
+ normalized = normalized.force_encoding(Encoding::UTF_8)
352
+ end
340
353
  end
341
354
 
342
355
  normalized.scrub
@@ -19,6 +19,7 @@ module SmartRAG
19
19
  def execute(plan:)
20
20
  @document_metadata_cache = {}
21
21
  @section_topic_cache = {}
22
+ @document_record_cache = {}
22
23
  normalized_plan = normalize_plan(plan)
23
24
  validate_plan!(normalized_plan)
24
25
  principal = normalized_plan.delete(:_principal)
@@ -459,6 +460,15 @@ module SmartRAG
459
460
  return section.document.url if section.document.respond_to?(:url)
460
461
  end
461
462
 
463
+ # Not every search backend returns source metadata: vector search results
464
+ # carry only similarity/rank/section, so this returned nil for them. A nil
465
+ # URI matches no `source_uri_prefix`, so every candidate was rejected by
466
+ # the scope filter and semantic/hybrid plans always came back empty. The
467
+ # document row is authoritative.
468
+ document = document_record_for(extract_document_id(result, section))
469
+ stored_uri = document && document[:source_uri]
470
+ return stored_uri unless stored_uri.to_s.empty?
471
+
462
472
  nil
463
473
  end
464
474
 
@@ -471,6 +481,12 @@ module SmartRAG
471
481
  end
472
482
  end
473
483
 
484
+ # Same reasoning as extract_source_uri: prefer what was stored on the
485
+ # document over guessing from a URI a backend may not have returned.
486
+ document = document_record_for(extract_document_id(result, extract_section(result)))
487
+ stored_type = document && document[:source_type]
488
+ return stored_type unless stored_type.to_s.empty?
489
+
474
490
  return 'url' if source_uri.to_s.start_with?('http://', 'https://')
475
491
  return 'file' if source_uri.to_s.start_with?('file://', '/')
476
492
 
@@ -578,6 +594,21 @@ module SmartRAG
578
594
  nil
579
595
  end
580
596
 
597
+ # Document rows are the authoritative source for scope metadata; search
598
+ # backends do not all echo it back to the caller.
599
+ def document_record_for(document_id)
600
+ return nil if document_id.nil?
601
+ return nil unless defined?(::SmartRAG) && ::SmartRAG.respond_to?(:db) && ::SmartRAG.db
602
+
603
+ @document_record_cache ||= {}
604
+ key = document_id.to_i
605
+ return @document_record_cache[key] if @document_record_cache.key?(key)
606
+
607
+ @document_record_cache[key] = ::SmartRAG.db[:source_documents].where(id: key).first
608
+ rescue StandardError
609
+ nil
610
+ end
611
+
581
612
  def extract_vector_score(result, mode)
582
613
  return numeric_or_nil(result[:vector_score]) if result.is_a?(Hash) && result.key?(:vector_score)
583
614
  return numeric_or_nil(result[:similarity]) if mode == 'semantic' && result.is_a?(Hash) && result.key?(:similarity)
@@ -1,3 +1,3 @@
1
1
  module SmartRAG
2
- VERSION = "0.2.2"
2
+ VERSION = "0.2.3"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_rag
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - SmartRAG Team
@@ -417,7 +417,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
417
417
  - !ruby/object:Gem::Version
418
418
  version: '0'
419
419
  requirements: []
420
- rubygems_version: 4.0.16
420
+ rubygems_version: 4.0.20
421
421
  specification_version: 4
422
422
  summary: A hybrid RAG (Retrieval-Augmented Generation) system with vector and full-text
423
423
  search