Class: Assistant::TechnicalSupport::CaseCandidateQuery

Inherits:
Object
  • Object
show all
Includes:
ArelHelpers
Defined in:
app/queries/assistant/technical_support/case_candidate_query.rb

Overview

Reads rolling-window, non-narrative metadata for resolved Technical
Support cases. It never persists a second corpus: case evidence and
vectors remain attached to their existing SupportCase, Activity,
Communication, and CallRecord rows. Complete event-stream availability is
audited later by CaseEventStream; these counts describe narrative volume
and the searchable vector index only.

Defined Under Namespace

Classes: Candidate, Result

Constant Summary collapse

MIN_EVIDENCE_EVENTS =
3
MIN_EVIDENCE_CHARS =
500
NON_PRODUCT_ROOTS =

ProductLine flags cannot express this boundary: rental tools share the
public/main/support flags used by real product roots, while historical
product roots do not consistently carry those flags. Keep the explicit
catalog exceptions here and cover them in the family-query test.

%w[rental_tools services shipping].freeze

Instance Method Summary collapse

Constructor Details

#initialize(scope: SupportCase.all, embedding_query: CaseEmbeddingQuery.new, linked_call_record_query: CaseLinkedCallRecordQuery.new) ⇒ CaseCandidateQuery

Returns a new instance of CaseCandidateQuery.



68
69
70
71
72
73
74
75
76
# File 'app/queries/assistant/technical_support/case_candidate_query.rb', line 68

def initialize(
  scope: SupportCase.all,
  embedding_query: CaseEmbeddingQuery.new,
  linked_call_record_query: CaseLinkedCallRecordQuery.new
)
  @scope = scope
  @embedding_query = embedding_query
  @linked_call_record_query = linked_call_record_query
end

Instance Method Details

#call(since:) ⇒ Result

Returns rolling-window metadata and averaged existing vectors.

Parameters:

  • since (Time, DateTime)

    earliest case close time

Returns:

  • (Result)

    rolling-window metadata and averaged existing vectors



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'app/queries/assistant/technical_support/case_candidate_query.rb', line 80

def call(since:)
  ActiveRecord::Base.with_connection do
    corpus = corpus_scope(since)
    resolved = corpus.where(
      closed_reason: TechnicalSupportReplayCriteria::CLOSED_REASONS
    )
    rows = resolved_rows(resolved)
    candidates = build_candidates(rows, resolved.select(:id))
    narrative_ready = candidates.select(&:narrative_ready?)
    search_index_ready = narrative_ready.select(&:search_index_ready?)
    search_index_ready_ids = search_index_ready.map(&:support_case_id)
    vectors = embedding_query.averages(ids: search_index_ready_ids)
    families = product_families(search_index_ready_ids)
    vector_candidates = search_index_ready.filter_map do |candidate|
      embedding = vectors[candidate.support_case_id]
      if embedding.present?
        candidate.with(
          embedding:,
          product_families: families.fetch(candidate.support_case_id, [])
        )
      end
    end

    Result.new(
      corpus_size: corpus.count,
      resolved_size: candidates.size,
      narrative_ready_size: narrative_ready.size,
      search_index_ready_size: vector_candidates.size,
      candidates: vector_candidates
    )
  end
end