1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
//! Which sessions a query is *about*, as opposed to which contain its words.
//!
//! The second tier of search. The first is literal — [`crate::session::search`]
//! reads transcripts for the terms — and it fails in two ways this one answers:
//! the words may simply not be there, and a query long enough to be a sentence
//! is asking about a subject whether or not one of its words happened to
//! appear. See [`worth_widening`] for when it is worth the index at all.
//!
//! # Why this is a module rather than a function in each caller
//!
//! Both surfaces widen: the terminal through `crate::ui::worker`, the browser
//! through `crate::serve::search`. This existed in both of them, twice over —
//! the same three constants, the same lazily-loaded model, the same
//! refresh-and-save, the same floor and limit — and the two copies agreed only
//! because a doc comment in each pointed at the other and said so. Nothing
//! failed when they drifted; the two surfaces just answered the same question
//! differently, which is the kind of disagreement nobody reports as a bug.
//!
//! What genuinely differed between them was the shape each collected into, and
//! that stayed with the callers, where it belongs.
use crateTarget;
/// A word count at which a query is a question, not a keyword.
///
/// Two words is `vast.ai nemotron` — a pair of names, which the literal tier is
/// better at. Four is "where did I price out GPUs", which it cannot answer at
/// all unless those words happen to be in the transcript.
const WORDS: usize = 4;
/// How close a chunk has to be to count as being about the query.
///
/// Cosine over mean-pooled static vectors does not reach the high scores a
/// contextual model would; on a real corpus a right answer sits around 0.35 to
/// 0.5 and unrelated text around 0.05. This sits below the answers and well
/// above the noise, and is a floor rather than a ranking — everything above it
/// is still ordered by score.
const FLOOR: f32 = 0.22;
/// Checked where it cannot drift from the value: a floor at or below the noise
/// admits every session, and one at or above the answers admits none. Both
/// bounds are from scoring a real corpus.
const _: = assert!;
/// At most this many sessions are added by the topical tier, so a vague query
/// widens the table rather than replacing it with everything on the machine.
const LIMIT: usize = 10;
/// Whether a query is worth going to the index for.
///
/// Two ways in, and they are different failures. An empty result means the
/// words are not in any transcript, which is when "what was it about" is the
/// only question left. A long query means it reads as a sentence rather than a
/// keyword, and those ask a topical question even when some literal match did
/// turn up — "where did I price out GPUs" matching a transcript that happens to
/// contain "where" is not an answer.
///
/// A short query that already matched is left alone deliberately: the literal
/// tier is the better answer for `vast.ai nemotron`, and widening it would add
/// vaguer sessions underneath a precise hit.
/// One session the index thinks is about the query.
/// The topical tier's state, built at most once per process that widens.
///
/// The model is a 30 MB read and the index a megabyte; both are loaded lazily
/// on the first widening query and then kept, since both surfaces outlive the
/// dozens of searches a session of looking produces.