webfetch-llm 0.1.0__py3-none-any.whl
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.
- webfetch/__init__.py +48 -0
- webfetch/cache.py +347 -0
- webfetch/compress.py +360 -0
- webfetch/config.py +154 -0
- webfetch/data/volatility_centroids.json +1 -0
- webfetch/extract/__init__.py +35 -0
- webfetch/extract/base.py +180 -0
- webfetch/extract/claude.py +71 -0
- webfetch/extract/gemini.py +89 -0
- webfetch/extract/gpt.py +70 -0
- webfetch/extract/groq.py +83 -0
- webfetch/fetch/__init__.py +89 -0
- webfetch/fetch/base.py +42 -0
- webfetch/fetch/html.py +251 -0
- webfetch/fetch/pdf.py +246 -0
- webfetch/mcp.py +68 -0
- webfetch/pipeline.py +245 -0
- webfetch/rank/__init__.py +98 -0
- webfetch/rank/base.py +52 -0
- webfetch/rank/biencoder.py +91 -0
- webfetch/rank/bm25.py +70 -0
- webfetch/rank/chunker.py +78 -0
- webfetch/rank/crossencoder.py +73 -0
- webfetch/rank/hybrid.py +88 -0
- webfetch/rank/rrf.py +64 -0
- webfetch/receipts.py +123 -0
- webfetch/search/__init__.py +104 -0
- webfetch/search/base.py +56 -0
- webfetch/search/brave.py +91 -0
- webfetch/search/ddg.py +67 -0
- webfetch/search/multi.py +145 -0
- webfetch/search/resilience.py +153 -0
- webfetch/search/serper.py +90 -0
- webfetch/search/tavily.py +74 -0
- webfetch/semcache.py +270 -0
- webfetch/tool.py +190 -0
- webfetch/volatility.py +101 -0
- webfetch_llm-0.1.0.dist-info/METADATA +288 -0
- webfetch_llm-0.1.0.dist-info/RECORD +42 -0
- webfetch_llm-0.1.0.dist-info/WHEEL +4 -0
- webfetch_llm-0.1.0.dist-info/entry_points.txt +3 -0
- webfetch_llm-0.1.0.dist-info/licenses/LICENSE +21 -0
webfetch/compress.py
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Sentence-level extractive compression for tool results.
|
|
3
|
+
|
|
4
|
+
Within each top-ranked chunk, keep only the sentences relevant to the query.
|
|
5
|
+
Runs at tool-result FORMATTING time, after the cache read, so cached chunks
|
|
6
|
+
stay full-text and budget-agnostic - retuning compression never invalidates
|
|
7
|
+
the cache.
|
|
8
|
+
|
|
9
|
+
Why extractive (select sentences) rather than abstractive (rewrite):
|
|
10
|
+
abstractive needs an LLM call per result, which is against the library's
|
|
11
|
+
zero-marginal-cost design; extractive is a pure function of (query, chunks).
|
|
12
|
+
|
|
13
|
+
Parameters (scorer, selection policy, guards) are chosen by the eval harness
|
|
14
|
+
(evals/run_compression_eval.py) which imports THIS module - the shipped code
|
|
15
|
+
is exactly what was measured. Config defaults in webfetch/config.py hold the
|
|
16
|
+
winning configuration: cross-encoder scored, ratio 0.5, all guards on -
|
|
17
|
+
measured ZERO recall drop (29/29 answer survival) at 50% of baseline tokens
|
|
18
|
+
on 50 captured production results. The cross-encoder is what makes lossless
|
|
19
|
+
compression possible here: bi-encoder selection at the same token level
|
|
20
|
+
loses 3-4 answers of 29.
|
|
21
|
+
|
|
22
|
+
Guards against the known failure modes of naive sentence selection:
|
|
23
|
+
- anaphora: a kept sentence starting with a pronoun/connective would be
|
|
24
|
+
unreadable alone, so its preceding sentence is retained with it
|
|
25
|
+
- table-like lines (markdown rows, digit-dense spec lines) are not prose -
|
|
26
|
+
sentence scoring misjudges them, so they bypass scoring and are kept
|
|
27
|
+
- cross-chunk dedup: the chunker's 10% overlap re-emits boundary sentences
|
|
28
|
+
in the next chunk; duplicates waste budget
|
|
29
|
+
|
|
30
|
+
Requires the optional rerank extra for the bi-encoder scorer; without it the
|
|
31
|
+
scorer degrades to the zero-dep lexical scorer (never crashes the tool path).
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
import logging
|
|
37
|
+
import math
|
|
38
|
+
import re
|
|
39
|
+
import threading
|
|
40
|
+
|
|
41
|
+
from webfetch.config import (
|
|
42
|
+
BIENCODER_MODEL,
|
|
43
|
+
COMPRESS_ANAPHORA_GUARD,
|
|
44
|
+
COMPRESS_DEDUP,
|
|
45
|
+
COMPRESS_PARAM,
|
|
46
|
+
COMPRESS_POLICY,
|
|
47
|
+
COMPRESS_SCORER,
|
|
48
|
+
COMPRESS_TABLE_GUARD,
|
|
49
|
+
CROSSENCODER_MODEL,
|
|
50
|
+
)
|
|
51
|
+
from webfetch.rank.base import Chunk
|
|
52
|
+
|
|
53
|
+
logger = logging.getLogger(__name__)
|
|
54
|
+
|
|
55
|
+
# Sentence boundary: terminal punctuation, whitespace, then something that
|
|
56
|
+
# looks like a sentence start. Deliberately regex-based and zero-dep (same
|
|
57
|
+
# philosophy as the chunker) - abbreviation mistakes cost a split, and the
|
|
58
|
+
# eval measures the net effect rather than assuming splitter perfection.
|
|
59
|
+
_SENT_BOUNDARY_RE = re.compile(r"(?<=[.!?])\s+(?=[A-Z0-9\"'(\[])")
|
|
60
|
+
|
|
61
|
+
# First words that signal the sentence leans on its predecessor.
|
|
62
|
+
_ANAPHORIC_STARTS = frozenset({
|
|
63
|
+
"it", "its", "this", "that", "these", "those", "they", "them", "their",
|
|
64
|
+
"he", "she", "his", "her", "him", "however", "but", "also", "moreover",
|
|
65
|
+
"additionally", "furthermore", "meanwhile", "instead", "still", "yet",
|
|
66
|
+
"so", "such", "both", "neither", "otherwise", "consequently", "thus",
|
|
67
|
+
"therefore", "here", "there",
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
_FIRST_WORD_RE = re.compile(r"[a-zA-Z]+")
|
|
71
|
+
|
|
72
|
+
# Digit share of non-space chars above which a line is treated as data,
|
|
73
|
+
# not prose (spec lines, score lines, table rows).
|
|
74
|
+
_TABLE_DIGIT_RATIO = 0.20
|
|
75
|
+
|
|
76
|
+
# Sentences shorter than this are never deduped - short strings ("Yes.",
|
|
77
|
+
# a bare number) can legitimately repeat with different meanings.
|
|
78
|
+
_DEDUP_MIN_CHARS = 30
|
|
79
|
+
|
|
80
|
+
_bi_model = None
|
|
81
|
+
_ce_model = None
|
|
82
|
+
_models_unavailable = False
|
|
83
|
+
# Concurrent tool calls racing lazy model construction crash inside torch -
|
|
84
|
+
# same locking pattern as rank/biencoder.py.
|
|
85
|
+
_init_lock = threading.Lock()
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def split_sentences(text: str) -> list[tuple[int, str]]:
|
|
89
|
+
"""Split text into (line_index, sentence) pairs.
|
|
90
|
+
|
|
91
|
+
Newlines are hard boundaries (extracted pages use them structurally:
|
|
92
|
+
headings, list items, table rows); prose lines are further split on
|
|
93
|
+
sentence punctuation. Chunk-edge fragments come through as ordinary
|
|
94
|
+
sentences and are scored like any other.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
text: Chunk text.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
List of (line_index, sentence) pairs in document order.
|
|
101
|
+
"""
|
|
102
|
+
out: list[tuple[int, str]] = []
|
|
103
|
+
for line_idx, line in enumerate(text.split("\n")):
|
|
104
|
+
line = line.strip()
|
|
105
|
+
if not line:
|
|
106
|
+
continue
|
|
107
|
+
for sent in _SENT_BOUNDARY_RE.split(line):
|
|
108
|
+
sent = sent.strip()
|
|
109
|
+
if sent:
|
|
110
|
+
out.append((line_idx, sent))
|
|
111
|
+
return out
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def is_table_like(sentence: str) -> bool:
|
|
115
|
+
"""True for lines that are data rather than prose (skip scoring)."""
|
|
116
|
+
if "|" in sentence:
|
|
117
|
+
return True
|
|
118
|
+
compact = sentence.replace(" ", "")
|
|
119
|
+
if not compact:
|
|
120
|
+
return False
|
|
121
|
+
digits = sum(ch.isdigit() for ch in compact)
|
|
122
|
+
return digits / len(compact) >= _TABLE_DIGIT_RATIO
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def starts_anaphoric(sentence: str) -> bool:
|
|
126
|
+
"""True if the sentence's first word leans on the preceding sentence."""
|
|
127
|
+
m = _FIRST_WORD_RE.search(sentence[:24])
|
|
128
|
+
return bool(m) and m.group(0).lower() in _ANAPHORIC_STARTS
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _tokenize(text: str) -> list[str]:
|
|
132
|
+
return re.sub(r"[^\w\s]", " ", text.lower()).split()
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def score_lexical(query: str, sentences: list[str]) -> list[float]:
|
|
136
|
+
"""IDF-weighted query-term coverage per sentence, in [0, 1].
|
|
137
|
+
|
|
138
|
+
IDF is computed over the sentences of THIS call (the per-result corpus):
|
|
139
|
+
a query term appearing in every sentence carries no selection signal,
|
|
140
|
+
while a rare one dominates. Zero-dep fallback scorer.
|
|
141
|
+
"""
|
|
142
|
+
q_terms = list(dict.fromkeys(_tokenize(query)))
|
|
143
|
+
if not q_terms or not sentences:
|
|
144
|
+
return [0.0] * len(sentences)
|
|
145
|
+
sent_tokens = [set(_tokenize(s)) for s in sentences]
|
|
146
|
+
n = len(sentences)
|
|
147
|
+
idf = {t: math.log(1 + n / (1 + sum(t in st for st in sent_tokens)))
|
|
148
|
+
for t in q_terms}
|
|
149
|
+
total = sum(idf.values()) or 1.0
|
|
150
|
+
return [sum(idf[t] for t in q_terms if t in st) / total
|
|
151
|
+
for st in sent_tokens]
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _deps_available() -> bool:
|
|
155
|
+
global _models_unavailable
|
|
156
|
+
if _models_unavailable:
|
|
157
|
+
return False
|
|
158
|
+
try:
|
|
159
|
+
import sentence_transformers # noqa: F401
|
|
160
|
+
except ImportError:
|
|
161
|
+
logger.warning(
|
|
162
|
+
"sentence-transformers not installed - compression degrades to "
|
|
163
|
+
"the lexical scorer. Install webfetch-llm[rerank]."
|
|
164
|
+
)
|
|
165
|
+
_models_unavailable = True
|
|
166
|
+
return False
|
|
167
|
+
return True
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def score_biencoder(query: str, sentences: list[str]) -> list[float] | None:
|
|
171
|
+
"""Cosine similarity of each sentence to the query; None without [rerank]."""
|
|
172
|
+
global _bi_model
|
|
173
|
+
if not _deps_available():
|
|
174
|
+
return None
|
|
175
|
+
from sentence_transformers import SentenceTransformer
|
|
176
|
+
with _init_lock:
|
|
177
|
+
if _bi_model is None:
|
|
178
|
+
_bi_model = SentenceTransformer(BIENCODER_MODEL)
|
|
179
|
+
q_vec = _bi_model.encode([query], normalize_embeddings=True,
|
|
180
|
+
show_progress_bar=False)[0]
|
|
181
|
+
s_vecs = _bi_model.encode(sentences, normalize_embeddings=True,
|
|
182
|
+
show_progress_bar=False)
|
|
183
|
+
return [float(v @ q_vec) for v in s_vecs]
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def score_crossencoder(query: str, sentences: list[str]) -> list[float] | None:
|
|
187
|
+
"""Joint (query, sentence) relevance scores; None without [rerank].
|
|
188
|
+
|
|
189
|
+
Same ms-marco model as the ranking cascade's stage 3. O(N) forward
|
|
190
|
+
passes, but N here is the ~15-25 sentences of one tool result - tens of
|
|
191
|
+
milliseconds, not the cost profile that keeps cross-encoders out of
|
|
192
|
+
full-list ranking.
|
|
193
|
+
"""
|
|
194
|
+
global _ce_model
|
|
195
|
+
if not _deps_available():
|
|
196
|
+
return None
|
|
197
|
+
from sentence_transformers import CrossEncoder
|
|
198
|
+
with _init_lock:
|
|
199
|
+
if _ce_model is None:
|
|
200
|
+
_ce_model = CrossEncoder(CROSSENCODER_MODEL)
|
|
201
|
+
scores = _ce_model.predict([(query, s) for s in sentences],
|
|
202
|
+
show_progress_bar=False)
|
|
203
|
+
return [float(s) for s in scores]
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _select_indices(scores: list[float], sentences: list[str],
|
|
207
|
+
policy: str, param: float) -> set[int]:
|
|
208
|
+
"""Pick sentence indices for one chunk under the selection policy.
|
|
209
|
+
|
|
210
|
+
Policies:
|
|
211
|
+
ratio: best-scoring sentences until ~param of the chunk's chars kept.
|
|
212
|
+
topk: best int(param) sentences.
|
|
213
|
+
threshold: every sentence scoring >= param.
|
|
214
|
+
|
|
215
|
+
Always returns at least one index (the best-scoring sentence) so a chunk
|
|
216
|
+
can never compress to nothing.
|
|
217
|
+
"""
|
|
218
|
+
order = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)
|
|
219
|
+
keep: set[int] = set()
|
|
220
|
+
if policy == "ratio":
|
|
221
|
+
total = sum(len(s) for s in sentences)
|
|
222
|
+
used = 0
|
|
223
|
+
for i in order:
|
|
224
|
+
if keep and used + len(sentences[i]) > param * total:
|
|
225
|
+
continue # try smaller later sentences - greedy knapsack
|
|
226
|
+
keep.add(i)
|
|
227
|
+
used += len(sentences[i])
|
|
228
|
+
elif policy == "topk":
|
|
229
|
+
keep = set(order[: max(1, int(param))])
|
|
230
|
+
elif policy == "threshold":
|
|
231
|
+
keep = {i for i in order if scores[i] >= param}
|
|
232
|
+
else:
|
|
233
|
+
raise ValueError(f"unknown compression policy: {policy!r}")
|
|
234
|
+
if not keep:
|
|
235
|
+
keep = {order[0]}
|
|
236
|
+
return keep
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def compress_chunks(
|
|
240
|
+
query: str,
|
|
241
|
+
chunks: list[Chunk],
|
|
242
|
+
scorer: str = COMPRESS_SCORER,
|
|
243
|
+
policy: str = COMPRESS_POLICY,
|
|
244
|
+
param: float = COMPRESS_PARAM,
|
|
245
|
+
anaphora_guard: bool = COMPRESS_ANAPHORA_GUARD,
|
|
246
|
+
table_guard: bool = COMPRESS_TABLE_GUARD,
|
|
247
|
+
dedup: bool = COMPRESS_DEDUP,
|
|
248
|
+
) -> list[Chunk]:
|
|
249
|
+
"""Compress each chunk to its query-relevant sentences.
|
|
250
|
+
|
|
251
|
+
Returns NEW Chunk objects - inputs are never mutated, because cached
|
|
252
|
+
chunk instances may be shared with other readers. Kept sentences stay in
|
|
253
|
+
document order; sentences from the same source line rejoin with a space,
|
|
254
|
+
different lines with a newline (preserves table/list structure).
|
|
255
|
+
|
|
256
|
+
On any unexpected failure the original chunks are returned unchanged -
|
|
257
|
+
this runs on the tool path, where a formatting bug must never turn a
|
|
258
|
+
good search into an error.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
query: The search query driving relevance.
|
|
262
|
+
chunks: Ranked chunks (best first) from the pipeline.
|
|
263
|
+
scorer: "biencoder", "crossencoder", "lexical", or "lead"
|
|
264
|
+
(position baseline).
|
|
265
|
+
policy: Selection policy - "ratio", "topk", or "threshold".
|
|
266
|
+
param: Policy parameter (ratio fraction, k, or score cutoff).
|
|
267
|
+
anaphora_guard: Retain the predecessor of pronoun-initial keeps.
|
|
268
|
+
table_guard: Keep table-like lines unconditionally.
|
|
269
|
+
dedup: Drop sentences already emitted by an earlier chunk.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
Compressed chunks, same order; chunks fully deduplicated away are
|
|
273
|
+
dropped. Empty input returns [].
|
|
274
|
+
"""
|
|
275
|
+
if not chunks:
|
|
276
|
+
return []
|
|
277
|
+
try:
|
|
278
|
+
return _compress(query, chunks, scorer, policy, param,
|
|
279
|
+
anaphora_guard, table_guard, dedup)
|
|
280
|
+
except Exception:
|
|
281
|
+
logger.warning("compression failed - returning chunks uncompressed",
|
|
282
|
+
exc_info=True)
|
|
283
|
+
return chunks
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _compress(query: str, chunks: list[Chunk], scorer: str, policy: str,
|
|
287
|
+
param: float, anaphora_guard: bool, table_guard: bool,
|
|
288
|
+
dedup: bool) -> list[Chunk]:
|
|
289
|
+
per_chunk = [split_sentences(c.text) for c in chunks]
|
|
290
|
+
|
|
291
|
+
# Score every sentence across all chunks in ONE batch (matters for the
|
|
292
|
+
# bi-encoder: one encode call instead of one per chunk).
|
|
293
|
+
flat = [s for sents in per_chunk for (_, s) in sents]
|
|
294
|
+
if scorer == "biencoder":
|
|
295
|
+
flat_scores = score_biencoder(query, flat)
|
|
296
|
+
if flat_scores is None:
|
|
297
|
+
flat_scores = score_lexical(query, flat)
|
|
298
|
+
elif scorer == "crossencoder":
|
|
299
|
+
flat_scores = score_crossencoder(query, flat)
|
|
300
|
+
if flat_scores is None:
|
|
301
|
+
flat_scores = score_lexical(query, flat)
|
|
302
|
+
elif scorer == "lexical":
|
|
303
|
+
flat_scores = score_lexical(query, flat)
|
|
304
|
+
elif scorer == "lead":
|
|
305
|
+
# Position baseline: earlier in chunk = higher. Eval control arm.
|
|
306
|
+
flat_scores = []
|
|
307
|
+
for sents in per_chunk:
|
|
308
|
+
flat_scores.extend(1.0 / (1 + i) for i in range(len(sents)))
|
|
309
|
+
else:
|
|
310
|
+
raise ValueError(f"unknown compression scorer: {scorer!r}")
|
|
311
|
+
|
|
312
|
+
out: list[Chunk] = []
|
|
313
|
+
seen_norm: set[str] = set()
|
|
314
|
+
pos = 0
|
|
315
|
+
for chunk, sents in zip(chunks, per_chunk):
|
|
316
|
+
scores = flat_scores[pos: pos + len(sents)]
|
|
317
|
+
pos += len(sents)
|
|
318
|
+
if not sents:
|
|
319
|
+
continue
|
|
320
|
+
if len(sents) == 1:
|
|
321
|
+
keep = {0}
|
|
322
|
+
else:
|
|
323
|
+
texts = [s for (_, s) in sents]
|
|
324
|
+
keep = _select_indices(scores, texts, policy, param)
|
|
325
|
+
if table_guard:
|
|
326
|
+
keep |= {i for i, t in enumerate(texts) if is_table_like(t)}
|
|
327
|
+
if anaphora_guard:
|
|
328
|
+
# Walk each kept sentence's predecessors so chains resolve
|
|
329
|
+
# ("It ... " preceded by "This ..." pulls in both).
|
|
330
|
+
for i in sorted(keep):
|
|
331
|
+
j = i
|
|
332
|
+
while (j > 0 and starts_anaphoric(texts[j])
|
|
333
|
+
and (j - 1) not in keep):
|
|
334
|
+
keep.add(j - 1)
|
|
335
|
+
j -= 1
|
|
336
|
+
|
|
337
|
+
kept = [sents[i] for i in sorted(keep)]
|
|
338
|
+
if dedup:
|
|
339
|
+
deduped = []
|
|
340
|
+
for line_idx, s in kept:
|
|
341
|
+
norm = " ".join(_tokenize(s))
|
|
342
|
+
if len(s) >= _DEDUP_MIN_CHARS and norm in seen_norm:
|
|
343
|
+
continue
|
|
344
|
+
seen_norm.add(norm)
|
|
345
|
+
deduped.append((line_idx, s))
|
|
346
|
+
kept = deduped
|
|
347
|
+
if not kept:
|
|
348
|
+
continue # chunk was pure overlap-duplicate content
|
|
349
|
+
|
|
350
|
+
parts = [kept[0][1]]
|
|
351
|
+
for (prev_line, _), (line_idx, s) in zip(kept, kept[1:]):
|
|
352
|
+
parts.append(("\n" if line_idx != prev_line else " ") + s)
|
|
353
|
+
out.append(Chunk(text="".join(parts), url=chunk.url,
|
|
354
|
+
title=chunk.title, score=chunk.score))
|
|
355
|
+
return out
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
__all__ = ["compress_chunks", "split_sentences", "score_lexical",
|
|
359
|
+
"score_biencoder", "score_crossencoder", "is_table_like",
|
|
360
|
+
"starts_anaphoric"]
|
webfetch/config.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Central constants and defaults for the webfetch pipeline.
|
|
3
|
+
|
|
4
|
+
All tuneable knobs live here so nothing is hardcoded in module logic.
|
|
5
|
+
Change a value here and it propagates everywhere - no grep-and-replace needed.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# --- Search ---
|
|
9
|
+
# How many URLs to retrieve from the search adapter per query.
|
|
10
|
+
DEFAULT_N_RESULTS: int = 10
|
|
11
|
+
|
|
12
|
+
# Which search provider to use when none is specified by the caller.
|
|
13
|
+
# "ddg" = DuckDuckGo (free, no key); see search/__init__.py for all.
|
|
14
|
+
DEFAULT_SEARCH_PROVIDER: str = "ddg"
|
|
15
|
+
|
|
16
|
+
# --- Fetch ---
|
|
17
|
+
# Seconds before an HTTP fetch is abandoned. Keeps slow pages from
|
|
18
|
+
# blocking the pipeline.
|
|
19
|
+
FETCH_TIMEOUT_SECS: int = 10
|
|
20
|
+
|
|
21
|
+
# Thread pool size for concurrent URL fetching. Fetching is IO-bound (network
|
|
22
|
+
# waits, not CPU), so more workers than cores is fine and cuts wall time a lot.
|
|
23
|
+
DEFAULT_FETCH_WORKERS: int = 8
|
|
24
|
+
|
|
25
|
+
# Extracted text shorter than this suggests a JS-rendered SPA whose real
|
|
26
|
+
# content never reached the prose extractor - triggers the playwright
|
|
27
|
+
# fallback even though extraction technically "succeeded".
|
|
28
|
+
MIN_EXTRACTED_CHARS: int = 300
|
|
29
|
+
|
|
30
|
+
# --- Ranking ---
|
|
31
|
+
# Default cascade: hybrid fusion (full-list BM25 + bi-encoder rankings fused
|
|
32
|
+
# via RRF) -> cross-encoder. Fusion replaced the BM25-first gate after the
|
|
33
|
+
# gap-1 experiment measured recall 46% -> 54% at identical token cost - the
|
|
34
|
+
# lexical gate was dropping semantically-relevant chunks it could not see.
|
|
35
|
+
HYBRID_FUSION_TOP_K: int = 30 # fused chunks passed to the cross-encoder
|
|
36
|
+
|
|
37
|
+
# Used by the degraded (no-embeddings) path and by callers composing the
|
|
38
|
+
# old-style cascade manually.
|
|
39
|
+
BM25_TOP_K: int = 20 # after BM25: keep best 20 chunks
|
|
40
|
+
BIENCODER_TOP_K: int = 10 # after bi-encoder: keep best 10
|
|
41
|
+
CROSSENCODER_TOP_K: int = 5 # after cross-encoder: keep best 5
|
|
42
|
+
|
|
43
|
+
# Default models for optional reranking stages.
|
|
44
|
+
BIENCODER_MODEL: str = "all-MiniLM-L6-v2"
|
|
45
|
+
CROSSENCODER_MODEL: str = "cross-encoder/ms-marco-MiniLM-L-6-v2"
|
|
46
|
+
|
|
47
|
+
# --- Chunking ---
|
|
48
|
+
# Characters per text chunk fed into the ranker.
|
|
49
|
+
# 400 chars ~ 100 tokens - small enough for precise reranking, big
|
|
50
|
+
# enough for context.
|
|
51
|
+
DEFAULT_CHUNK_SIZE: int = 400
|
|
52
|
+
|
|
53
|
+
# Overlap between consecutive chunks to avoid splitting a key sentence
|
|
54
|
+
# at a boundary.
|
|
55
|
+
CHUNK_OVERLAP_RATIO: float = 0.10 # 10% overlap
|
|
56
|
+
|
|
57
|
+
# --- Token budget ---
|
|
58
|
+
# Maximum characters of ranked text sent to the LLM extraction call.
|
|
59
|
+
# ~6000 chars / 4 chars-per-token ~ 1500 tokens of context - cheap for
|
|
60
|
+
# haiku-class models.
|
|
61
|
+
DEFAULT_TOKEN_BUDGET: int = 6000
|
|
62
|
+
|
|
63
|
+
# --- Cache ---
|
|
64
|
+
# Default path for the sqlite3 cache database.
|
|
65
|
+
DEFAULT_CACHE_DB: str = "~/.webfetch/cache.db"
|
|
66
|
+
|
|
67
|
+
# How long cached results are kept before expiry. Acts as the hard ceiling
|
|
68
|
+
# for all rows (pages, legacy query rows without a freshness class).
|
|
69
|
+
DEFAULT_CACHE_TTL_DAYS: int = 90
|
|
70
|
+
|
|
71
|
+
# Volatility-aware TTLs, resolved at READ time so retuning these applies to
|
|
72
|
+
# already-cached rows. Effective TTL = min(TTL of the row's stored class,
|
|
73
|
+
# TTL of the caller's freshness hint, the ttl_days ceiling).
|
|
74
|
+
TTL_BY_FRESHNESS: dict[str, int] = {
|
|
75
|
+
"realtime": 15 * 60, # prices, scores, breaking news
|
|
76
|
+
"recent": 7 * 86400, # things that change over weeks/months
|
|
77
|
+
"stable": 90 * 86400, # historical/definitional facts, specs
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# Class used when there is no model hint and the classifier is unsure,
|
|
81
|
+
# and for legacy rows cached before freshness existed.
|
|
82
|
+
DEFAULT_FRESHNESS: str = "recent"
|
|
83
|
+
|
|
84
|
+
# --- Search resilience ---
|
|
85
|
+
# Circuit breaker: an engine failing this many CONSECUTIVE times (errors,
|
|
86
|
+
# or silent-block empties) is benched for the cooldown, then given one
|
|
87
|
+
# half-open probe. Stops burning latency on a dead/blocking engine.
|
|
88
|
+
SEARCH_BREAKER_THRESHOLD: int = 3
|
|
89
|
+
SEARCH_BREAKER_COOLDOWN_SECS: float = 300.0
|
|
90
|
+
|
|
91
|
+
# In fusion, an engine's empty response only counts as a breaker failure
|
|
92
|
+
# when a peer engine returned at least this many results for the SAME
|
|
93
|
+
# query - distinguishes a silent block from a genuinely hard query.
|
|
94
|
+
FUSION_PEER_EMPTY_MIN: int = 3
|
|
95
|
+
|
|
96
|
+
# --- Cost receipts ---
|
|
97
|
+
# Baseline for savings estimates (webfetch/receipts.py). Both Anthropic and
|
|
98
|
+
# OpenAI charge $10/1k hosted searches (verified 2026-07); the hosted tools
|
|
99
|
+
# also inject ~17.4k content tokens per call vs our ~3.5k compressed
|
|
100
|
+
# (measured, Layer 3 eval 2026-07-14). Token price defaults to Opus-class
|
|
101
|
+
# input - override per call for cheaper models.
|
|
102
|
+
RECEIPT_HOSTED_SEARCH_FEE: float = 10.00 / 1000
|
|
103
|
+
RECEIPT_HOSTED_TOKENS_PER_CALL: int = 17400
|
|
104
|
+
RECEIPT_TOKEN_PRICE_PER_MTOK: float = 5.00
|
|
105
|
+
|
|
106
|
+
# --- LLM extraction ---
|
|
107
|
+
# Default model for the extraction step. Cheapest capable model by default.
|
|
108
|
+
DEFAULT_EXTRACT_MODEL: str = "claude-haiku-4-5-20251001"
|
|
109
|
+
|
|
110
|
+
# --- Semantic query cache ---
|
|
111
|
+
# Thresholds picked by the eval harness (evals/run_matcher_eval.py, 247-pair
|
|
112
|
+
# factoid-enriched set): bi >= 0.60 keeps paraphrase recall at 0.98 as a
|
|
113
|
+
# shortlist gate; NLI bidirectional entailment >= 0.97 gives precision 0.955
|
|
114
|
+
# with zero trusted-negative false positives. See matcher_recommendation.json.
|
|
115
|
+
SEMCACHE_BI_THRESHOLD: float = 0.60
|
|
116
|
+
SEMCACHE_CE_THRESHOLD: float = 0.97
|
|
117
|
+
|
|
118
|
+
# NLI verifier won the 4-model bake-off: only model to clear the 0.95
|
|
119
|
+
# precision bar, natively rejects entity/number swaps (Portland trap: 0.000).
|
|
120
|
+
SEMCACHE_CE_MODEL: str = "cross-encoder/nli-deberta-v3-base"
|
|
121
|
+
|
|
122
|
+
# Verify only the top cosine candidate - precision-first; Layer 2 measured
|
|
123
|
+
# zero wrong-target matches, so deeper candidate lists buy nothing.
|
|
124
|
+
SEMCACHE_MAX_CANDIDATES: int = 1
|
|
125
|
+
|
|
126
|
+
# --- Tool mode ---
|
|
127
|
+
# Max characters of ranked context returned per web_search tool call.
|
|
128
|
+
# Larger than DEFAULT_TOKEN_BUDGET because in tool mode the calling model is
|
|
129
|
+
# the extractor and benefits from a bit more surrounding context.
|
|
130
|
+
DEFAULT_TOOL_RESULT_BUDGET: int = 8000
|
|
131
|
+
|
|
132
|
+
# --- Sentence-level compression (tool-result formatting) ---
|
|
133
|
+
# Applied AFTER the cache read, so cached rows stay full-text and retuning
|
|
134
|
+
# these never invalidates the cache. Values picked by the eval sweep
|
|
135
|
+
# (evals/run_compression_eval.py, 50 captured production results): this
|
|
136
|
+
# config measured recall 29/50 vs 29/50 uncompressed (zero drop, 29/29
|
|
137
|
+
# answer survival) at 50% of baseline tokens (332 vs 665 mean). The
|
|
138
|
+
# cross-encoder scorer is what preserves recall - biencoder at the same
|
|
139
|
+
# tokens loses 3-4 answers. Without [rerank] it degrades to the lexical
|
|
140
|
+
# scorer (measured: 27/29 survival at 51%).
|
|
141
|
+
COMPRESSION_ENABLED: bool = True
|
|
142
|
+
COMPRESS_SCORER: str = "crossencoder" # | "biencoder" | "lexical" | "lead"
|
|
143
|
+
COMPRESS_POLICY: str = "ratio" # "ratio" | "topk" | "threshold"
|
|
144
|
+
COMPRESS_PARAM: float = 0.5 # keep best sentences, <= 50% of chunk chars
|
|
145
|
+
COMPRESS_ANAPHORA_GUARD: bool = True # +14 tokens, keeps pronouns resolvable
|
|
146
|
+
COMPRESS_TABLE_GUARD: bool = True # ablation: protects 1 answer of 29
|
|
147
|
+
COMPRESS_DEDUP: bool = True # ablation: -13 tokens free (overlap dupes)
|
|
148
|
+
|
|
149
|
+
# Tool-result context format (build_context). Same eval: merging same-URL
|
|
150
|
+
# chunks under one header + hostname-only headers cut the fixed header
|
|
151
|
+
# overhead (26% of an uncompressed result) with zero measured recall cost -
|
|
152
|
+
# titles stay because answers live in them (extraction-hardening eval).
|
|
153
|
+
TOOL_MERGE_SOURCES: bool = True
|
|
154
|
+
TOOL_HEADER_STYLE: str = "domain" # "full" | "domain"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"model": "all-MiniLM-L6-v2", "source": "FreshQA (CC), full 377 rows", "classes": {"realtime": [0.012145215645432472, -0.0018631262937560678, -0.06274841725826263, -0.007661583833396435, -0.027888312935829163, 0.0621718093752861, -0.021171551197767258, 0.02851240523159504, -0.011449315585196018, 0.02597236819565296, -0.04086684435606003, -0.006424089893698692, -0.02788771130144596, 0.02362537570297718, -0.03769982233643532, 0.017427198588848114, -0.017687330022454262, -0.08234846591949463, 0.08868501335382462, -0.10261496156454086, -0.007088787388056517, 0.002663521096110344, 0.0016332603991031647, 0.00045282961218617857, 0.08852767199277878, -0.03599361702799797, -0.056443117558956146, -0.022277243435382843, -0.07022085040807724, -0.016778215765953064, -0.06464487314224243, -0.02756372094154358, 0.027233166620135307, 0.03248134255409241, -0.04783632978796959, -0.04881690815091133, 0.02403809316456318, 0.0007506250985898077, 0.02884712442755699, 0.012483729049563408, -0.02234613709151745, -0.10250423848628998, 0.016286777332425117, 0.012731242924928665, 0.03766198456287384, -0.025340810418128967, -0.016598522663116455, 0.02768656052649021, 0.054936714470386505, 0.11487270146608353, -0.03534339368343353, 0.03725761920213699, -0.017243774607777596, 0.009950782172381878, 0.06690368056297302, 0.005692985374480486, -0.044698603451251984, 0.0004932482843287289, 0.04355696961283684, 0.01979084312915802, -0.007639622315764427, -0.03338771313428879, -0.10322568565607071, 0.03135615959763527, 0.01046554371714592, -0.04580345377326012, -0.026506880298256874, -0.07263809442520142, -0.014567894861102104, -0.02869809791445732, 0.01659376546740532, 0.010415657423436642, 0.006484807003289461, -0.08891996741294861, 0.023524625226855278, 0.01739215850830078, 0.035195961594581604, 0.03747803717851639, 0.003209460061043501, -0.0376424640417099, 0.007335755508393049, -0.08569755405187607, -0.015708794817328453, -0.021274028345942497, 0.056080762296915054, 0.03369034081697464, -0.032607536762952805, 0.07464656978845596, -0.056957997381687164, 0.014871630817651749, -0.05218343064188957, 0.029951659962534904, -0.01689605414867401, 0.052715156227350235, -0.1119815930724144, -0.0018735149642452598, -0.028360188007354736, -0.004249660298228264, -0.04698532447218895, 0.11000624299049377, -0.021992405876517296, 0.07617287337779999, 0.042818401008844376, 0.0037722475826740265, 0.056841593235731125, 0.030398523434996605, 0.06313391029834747, 0.028064215555787086, -0.033619530498981476, -0.026942992582917213, -0.05855797603726387, -0.009410723112523556, -0.023611513897776604, -0.0013498563785105944, -0.004817285109311342, -0.04733685776591301, 0.04180905967950821, 0.0655769407749176, 0.06571927666664124, 0.029141250997781754, 0.06606484204530716, 0.04993269219994545, 0.0006714511546306312, 0.010644162073731422, -0.09988965839147568, 0.016641344875097275, -0.017963502556085587, -7.996425952427355e-33, 0.04056042432785034, -0.08298078179359436, 0.0798611044883728, 0.03384505212306976, -0.0005813181633129716, -0.013754554092884064, 0.008965825662016869, -0.018057813867926598, -0.055044565349817276, -0.07378089427947998, -0.019387418404221535, -0.06616166234016418, -0.041721466928720474, -0.11676525324583054, 0.0884120985865593, -0.0005623944452963769, -0.08393930643796921, 0.045821357518434525, -0.03545466437935829, 0.02201877534389496, 0.02102014608681202, 0.03818574175238609, 0.005251807626336813, -0.046063873916864395, 0.0017609883798286319, -0.012767121195793152, 0.016234265640378, -0.03738025575876236, 0.03839366137981415, 0.017702419310808182, 0.02254652976989746, -0.011753003112971783, -0.008132238872349262, 0.021470986306667328, 0.026390258222818375, 0.031572166830301285, -0.02854541316628456, -0.04545816779136658, 0.05124926194548607, 0.032728858292102814, 0.014105470851063728, -0.03141537681221962, 0.005319760646671057, -0.0634508728981018, -0.012997179292142391, 0.016193777322769165, -0.0289461612701416, 0.013243574649095535, 0.07796746492385864, 0.019404128193855286, -0.018053743988275528, -0.00757976621389389, -0.08551377803087234, -0.017113754525780678, 0.01956339366734028, -0.0217848252505064, 0.028527241200208664, -0.052100133150815964, 0.08539976924657822, 0.0727059468626976, -0.00771735142916441, 0.10828250646591187, -0.0010062677320092916, -0.0017656820127740502, -0.07601210474967957, 0.03872839733958244, 0.06104473024606705, -0.016915468499064445, -0.054150838404893875, 0.04271630197763443, 0.07039860635995865, -0.0031229224987328053, 0.010230539366602898, -0.0991160199046135, 0.017362629994750023, 0.013642113655805588, 0.0605914480984211, -0.0418471023440361, 0.02950374223291874, 0.060241036117076874, -0.047129079699516296, 0.007117462810128927, 0.07195363193750381, 0.028437813743948936, -0.06897237151861191, 0.02679777517914772, -0.042234260588884354, 0.026559779420495033, 0.012453213334083557, -0.025323128327727318, -0.062185730785131454, -0.07120354473590851, 0.03637091815471649, -0.04062645882368088, -0.06572014838457108, 3.756538684524308e-33, -0.019561057910323143, 0.0010238690301775932, 0.03263934329152107, 0.06091630086302757, 0.03326853737235069, -0.05022892355918884, 0.02662106789648533, 0.20079641044139862, 0.02190878428518772, 0.03251644968986511, 0.10060176253318787, -0.005975282751023769, 0.11721522361040115, 0.03066098317503929, 0.07437632977962494, 0.054939668625593185, 0.058271657675504684, -0.047429993748664856, -0.06029954552650452, -0.03828994557261467, -0.024580322206020355, 0.0824195146560669, -0.05107038468122482, 0.029964735731482506, 0.02194763906300068, -0.013980412855744362, 0.053490251302719116, 0.00391295924782753, -0.13898153603076935, -0.08299368619918823, 0.007824414409697056, -0.012302936986088753, -0.1006881594657898, 0.04606233164668083, -0.03258052095770836, 0.09304725378751755, 0.006648424081504345, -0.00026814319426193833, 0.028638750314712524, 0.07307504117488861, 0.05531078577041626, -0.017754772678017616, -0.012831241823732853, 0.16821259260177612, 0.05074983090162277, 0.03985459730029106, -0.05292412266135216, 0.08841066062450409, 0.05655524879693985, -0.008059504441916943, -0.07679089158773422, 0.026545094326138496, -0.07387138158082962, 0.01698480173945427, 0.01564045250415802, 0.03352392092347145, -0.07063072174787521, 0.03391892462968826, -0.013085504062473774, -0.018996283411979675, 0.008864144794642925, 0.026843387633562088, -0.04298478364944458, -0.009842624887824059, -0.013877437449991703, 7.742519665043801e-05, 0.01939546875655651, 0.03087269701063633, -0.06924799829721451, -0.010178822092711926, 0.05894096940755844, 0.006799302063882351, -0.06124420464038849, -0.021927375346422195, -0.09395230561494827, 0.10161103308200836, 0.05908716842532158, 0.04067505523562431, 0.03260904178023338, -0.020806023851037025, -0.0950707197189331, 0.053077954798936844, -0.056930337101221085, 0.044653572142124176, -0.012820999138057232, -0.003900255775079131, 0.06577984988689423, -0.1083097755908966, -0.018936049193143845, 0.04814876988530159, -0.03550277650356293, -0.0239479448646307, -0.004242108669131994, -0.09287764877080917, -0.017485903576016426, -4.011367948919542e-08, 0.05853461101651192, 0.14321114122867584, -0.09642613679170609, -0.068077951669693, 0.05938224494457245, 0.06424376368522644, 0.06816771626472473, 0.02329518087208271, 0.024788212031126022, 0.0216663908213377, 0.10322057455778122, -0.04315197840332985, 0.01926328055560589, -0.01207323744893074, -0.0005831048474647105, -0.04060975834727287, -0.025956114754080772, 0.04761594533920288, -0.008457469753921032, -0.041826892644166946, 0.026088401675224304, 0.031063469126820564, 0.07961023598909378, -0.09879054129123688, 0.0010286689503118396, 0.004794402047991753, -0.013164789415895939, 0.05569709464907646, -0.015505952760577202, -0.005806891713291407, 0.019580019637942314, 0.016695119440555573, -0.08401018381118774, -0.14084604382514954, -0.024840427562594414, 0.0046034264378249645, -0.04590464383363724, 0.02744842879474163, -0.03233516216278076, -0.08428578823804855, 0.010429917834699154, 0.06057283282279968, 0.010982648469507694, 0.06134016811847687, 0.04124429449439049, 0.029994413256645203, -0.023568596690893173, -0.037896428257226944, 0.020653579384088516, -0.08995361626148224, -0.07378672063350677, 0.02678094245493412, 0.054825007915496826, -0.020050644874572754, 0.019060198217630386, 0.002838355489075184, -0.025905828922986984, -0.046560149639844894, -0.06630369275808334, -0.03418434411287308, 0.11502895504236221, -0.12107022851705551, 0.04983608052134514, 0.08930587023496628], "recent": [0.037740834057331085, -0.025897791609168053, -0.07063736766576767, -0.0030858858954161406, -0.0385655052959919, 0.027679670602083206, -0.005790873430669308, 0.04536600410938263, 0.009580287151038647, 0.041824787855148315, 0.002868938259780407, -0.0330912210047245, 0.010563128627836704, 0.024402238428592682, -0.03492194414138794, 0.01100903283804655, -0.04160817340016365, -0.07459341734647751, 0.0861305445432663, -0.11681894958019257, 0.000607187335845083, -0.025019336491823196, 0.04239147529006004, -0.030158009380102158, 0.07918523997068405, -0.026850827038288116, -0.06338309496641159, -0.008769571781158447, -0.05210849270224571, -0.0670437440276146, -0.05640832707285881, -0.03528883308172226, 0.04560999572277069, 0.04130649194121361, -0.011238071136176586, -0.028030933812260628, 0.025195254012942314, 0.008335919119417667, 0.06000549718737602, -0.007748901378363371, 0.00048418165533803403, -0.07644284516572952, 0.03349863365292549, -0.007790022064000368, 0.0558280423283577, -0.0007756453705951571, -0.0057226321659982204, 0.02470877580344677, 0.020756904035806656, 0.07881532609462738, -0.005602407269179821, 0.02508022077381611, -0.016782615333795547, 0.03592527285218239, 0.07109124958515167, -0.04779466986656189, -0.04691481590270996, -0.0009210046846419573, 0.025139687582850456, 0.053828924894332886, -0.002145996317267418, -0.017301000654697418, -0.11765002459287643, 0.01028557401150465, -0.011623680591583252, -0.020329630002379417, -0.033269159495830536, -0.0458010695874691, -0.03787567839026451, 0.0015074169496074319, 0.05620766058564186, -0.018920497968792915, 0.010189317166805267, -0.03809700906276703, -0.0008899683598428965, -0.01289956271648407, 0.05275963246822357, 0.09183822572231293, -0.0025698968674987555, -0.05026788264513016, 0.03196341544389725, -0.04640628397464752, -0.02241269126534462, -0.01151073258370161, 0.05161017179489136, 0.0334722064435482, -0.028133969753980637, 0.060756903141736984, -0.05072283744812012, -0.0039917235262691975, -0.0464647077023983, 0.04364056885242462, 0.02522849477827549, 0.0595581941306591, -0.10544005036354065, 0.040252745151519775, -0.035451192408800125, 0.03506895899772644, -0.06946254521608353, 0.09570952504873276, -0.015216988511383533, 0.049334000796079636, 0.07752926647663116, 0.013933182694017887, -0.01580444537103176, 0.03478824347257614, 0.04229752719402313, 0.07602357864379883, -0.015588724054396152, 0.002205786295235157, -0.07989031821489334, -0.008642822504043579, -0.05011498183012009, -0.005334169138222933, -0.005515052936971188, -0.05893829092383385, 0.060996122658252716, 0.04934447258710861, 0.04784965515136719, 0.013765772804617882, 0.06403610110282898, 0.018437998369336128, -0.004653866868466139, 0.017492491751909256, -0.10154706239700317, 0.030901899561285973, -0.056882862001657486, -9.263546414476393e-33, 0.032071489840745926, -0.0631452277302742, 0.08019828796386719, 0.046986088156700134, -0.041777417063713074, 0.019012004137039185, 0.0045827883295714855, -0.0044275070540606976, -0.048470113426446915, -0.04983275383710861, -0.01822718232870102, -0.056053366512060165, -0.01828877255320549, -0.08254925161600113, 0.09881918132305145, 0.007796112447977066, -0.06468319147825241, 0.0760025680065155, -0.060067180544137955, 0.013709616847336292, 0.03374325856566429, 0.08879794180393219, 0.024127846583724022, -0.00841542985290289, 0.02462981455028057, 0.021595649421215057, 0.006472499575465918, -0.04018458351492882, 0.056987080723047256, 0.027295934036374092, 0.043269768357276917, -0.035261642187833786, -0.0553387887775898, 0.0012502741301432252, 0.022229338064789772, 0.03574218973517418, -0.030987700447440147, -0.05300057306885719, 0.03411632776260376, 0.03243895620107651, 0.01464895997196436, -0.03079126589000225, 0.012365912087261677, -0.023611238226294518, -0.019613271579146385, 0.020287813618779182, -0.05531042069196701, 0.007631160318851471, 0.04959418624639511, -0.0028270920738577843, -0.03970704972743988, -0.0012680422514677048, -0.10289273411035538, -0.016428861767053604, 0.051626332104206085, -0.024168673902750015, 0.01413167454302311, 0.004920460749417543, 0.06266112625598907, 0.08981794118881226, -0.03504524007439613, 0.10948540270328522, -0.030017409473657608, 0.05491165444254875, -0.028453201055526733, 0.03521046042442322, 0.03874867781996727, -0.028874889016151428, -0.029068557545542717, 0.009280937723815441, 0.03872179985046387, -0.013307967223227024, 0.025576138868927956, -0.059037335216999054, -0.02324475534260273, -0.018154621124267578, 0.05024878308176994, -0.03812074288725853, -0.0005742490175180137, 0.0959213599562645, -0.08256079256534576, 0.0028051540721207857, 0.07616480439901352, 0.0471491701900959, -0.032695621252059937, 0.047330521047115326, -0.03588801249861717, -0.001659886329434812, 0.010642332956194878, -0.006945728324353695, -0.09805209934711456, -0.05619858577847481, 0.027882959693670273, -0.05604751035571098, -0.10592898726463318, 4.5949629689841814e-33, -0.02613542228937149, -0.006271560676395893, 0.0457206629216671, 0.03334523364901543, 0.03378026932477951, -0.026292745023965836, 0.04472527652978897, 0.17767280340194702, -0.03315893933176994, 0.004677652381360531, 0.09173073619604111, -0.016673699021339417, 0.1146891638636589, 0.04301730915904045, 0.06296279281377792, 0.06419584900140762, 0.08006896078586578, -0.0630093514919281, -0.04692116379737854, -0.0552690327167511, -0.051715556532144547, 0.07841462641954422, 7.64712312957272e-05, 0.07047218084335327, -0.029466819018125534, 0.0026049974840134382, 0.010392696596682072, -0.02673671767115593, -0.12546704709529877, -0.05680812895298004, -0.004450392443686724, -0.011376120150089264, -0.11601763963699341, 0.055631838738918304, -0.030161255970597267, 0.05621485784649849, -0.009667915292084217, 0.014201724901795387, 0.011887979693710804, 0.11088187992572784, 0.054768797010183334, -0.011493510566651821, 0.015432603657245636, 0.15411017835140228, 0.02968861348927021, 0.01719019003212452, -0.04063909873366356, 0.06752496212720871, 0.016008097678422928, -0.02203967794775963, -0.0654606819152832, 0.018488537520170212, -0.09390315413475037, 0.0002816113119479269, 0.036626268178224564, -0.0027083519380539656, -0.07971291989088058, 0.04305737838149071, -0.005723634269088507, -0.017149874940514565, 0.03781665116548538, 0.036178670823574066, -0.03408880531787872, 0.04199240356683731, -0.011985711753368378, 0.0028120034839957952, -0.03526388108730316, 0.06770520657300949, -0.08036894351243973, -0.042860716581344604, 0.08275144547224045, -0.0190457571297884, -0.06447432190179825, -0.0013508021365851164, -0.11597707122564316, 0.09675540775060654, 0.025637278333306313, 0.03058444894850254, 0.04587564244866371, -0.0248523261398077, -0.09393557906150818, -0.0026686012279242277, -0.019358353689312935, 0.014417261816561222, -0.029305467382073402, 0.004780230578035116, 0.06614120304584503, -0.11459170281887054, 0.030194466933608055, 0.044906068593263626, -0.043565358966588974, -0.02789449878036976, 0.029291005805134773, -0.08918238431215286, -0.021739071235060692, -4.051126722970366e-08, 0.050828926265239716, 0.09618842601776123, -0.04176934435963631, -0.04011300578713417, 0.05205736681818962, 0.03308804705739021, 0.06941086053848267, 0.03233039379119873, 0.019226500764489174, 0.028578905388712883, 0.08651749789714813, -0.026446888223290443, -0.006831962149590254, -0.010019184090197086, -0.025466298684477806, -0.07011319696903229, -0.03966142237186432, 0.09581855684518814, -0.010219096206128597, -0.010008341632783413, -0.006578824482858181, 0.047231514006853104, 0.06408018618822098, -0.09780948609113693, 0.01514657773077488, -0.02015356533229351, 0.004760416690260172, 0.01761423423886299, 0.010450637899339199, -0.014002174139022827, 0.0002521428687032312, -0.008097398094832897, -0.06720700114965439, -0.13718897104263306, -0.01689615473151207, 0.014587732031941414, -0.028353476896882057, -0.02413005568087101, -0.034959759563207626, -0.11913619935512543, -0.00029614943196065724, 0.05826498195528984, 0.01299167051911354, 0.0897693783044815, 0.07465356588363647, 0.00587701378390193, -0.03150487691164017, -0.05592454597353935, 0.03574699908494949, -0.1141921803355217, -0.105095773935318, 0.04447728767991066, 0.0626031756401062, -0.01961544156074524, -0.007970171049237251, -0.0004538485372904688, -0.01703469268977642, -0.05490802973508835, -0.050680164247751236, -0.035791438072919846, 0.09396317601203918, -0.14849130809307098, 0.08407998085021973, 0.06755759567022324], "stable": [-0.009878501296043396, 0.0660642459988594, -0.05964508652687073, 0.07210763543844223, -0.03596775606274605, 0.02193950116634369, 0.010246319696307182, 0.01852239854633808, -0.04952670633792877, 0.020290177315473557, 0.023801632225513458, -0.05430629104375839, -0.007544808555394411, 0.012260775081813335, -0.06389766931533813, 0.03241700306534767, -0.053984079509973526, -0.07338772714138031, 0.05813191086053848, -0.06823553144931793, -0.005922270938754082, -0.011817307211458683, 0.03510754182934761, -0.008638807572424412, 0.06499254703521729, -0.01711813174188137, -0.024374794214963913, 0.007834694348275661, -0.015754472464323044, -0.03877580538392067, -0.041879672557115555, -0.052929844707250595, 0.030507778748869896, 0.03479808196425438, 0.0003642712254077196, -0.0018468601629137993, 0.09256058931350708, 0.01781170256435871, 0.03229816257953644, 0.0028201970271766186, -0.04097898676991463, -0.05748302862048149, 0.058425866067409515, 0.035652417689561844, 0.017063038423657417, -0.00208694557659328, 1.945298572536558e-05, 0.020170750096440315, 0.0525299571454525, 0.06233098730444908, 0.01996224746108055, -0.007750102784484625, -0.05245819315314293, -0.031384505331516266, 0.07049878686666489, 0.01562911458313465, -0.025653736665844917, 0.005772517994046211, 0.033708617091178894, 0.0173160582780838, -0.038299381732940674, -0.023969877511262894, -0.08515048772096634, 0.03904426097869873, 0.0337010882794857, -0.01881220191717148, -0.022112177684903145, -0.0830058604478836, -0.01255874801427126, -0.04907488450407982, 0.058949124068021774, -0.021205447614192963, 0.025199897587299347, -0.06132317706942558, -0.004188921302556992, -0.014213490299880505, 0.045258406549692154, 0.0980139970779419, -0.03531423583626747, -0.030920423567295074, -0.03895387426018715, -0.06878765672445297, -0.01498069055378437, 0.041184403002262115, 0.024011759087443352, 0.024984082207083702, -0.02330908738076687, 0.04412960261106491, -0.03279155492782593, -0.009370105341076851, -0.04633424058556557, 0.00010355184349464253, -0.007409134414047003, 0.05980905145406723, -0.10712308436632156, -0.0016119017964228988, -0.02891424112021923, 0.05164550617337227, -0.01999475061893463, 0.08253007382154465, 0.013118532486259937, 0.0412047877907753, 0.06558967381715775, 0.04939449205994606, 0.012494037859141827, 0.03203149884939194, 0.021725697442889214, 0.04889465123414993, -0.005762160290032625, -0.006175832357257605, -0.05037158355116844, -0.03631116822361946, 0.022419270128011703, 0.05585220083594322, 0.05395171791315079, -0.019489850848913193, 0.023703837767243385, 0.05224357917904854, 0.036405641585588455, -0.04141676053404808, 0.054783858358860016, 0.011990341357886791, -0.026010261848568916, 0.002385602332651615, -0.16738146543502808, 0.053104545921087265, -0.04563811421394348, -1.1385195433591571e-32, 0.03814498707652092, -0.0689789205789566, 0.04041396453976631, 0.08191869407892227, 0.014672595076262951, 0.005293590947985649, -0.006078558508306742, 0.019997579976916313, -0.040207281708717346, -0.0526622049510479, -0.009530546143651009, -0.1254967600107193, -0.05126592144370079, -0.12994910776615143, 0.06820761412382126, 0.03678153455257416, -0.08823713660240173, 0.02777552604675293, -0.03936967998743057, -0.020527733489871025, 0.011278019286692142, 0.0680217295885086, -0.0006382729043252766, -0.05814585089683533, -0.023165149614214897, 0.015882395207881927, 0.017337651923298836, -0.050064388662576675, 0.03945594280958176, 0.03115176595747471, 0.026074353605508804, -0.03448919579386711, -0.030477076768875122, 0.009733759798109531, 0.06363176554441452, 0.04730990156531334, -0.038697969168424606, -0.09644670784473419, 0.005985030438750982, 0.06498679518699646, 0.07189147919416428, -0.013101630844175816, 0.007560222875326872, -0.009547903202474117, -0.010277465917170048, -0.022786013782024384, -0.028719522058963776, 0.023598508909344673, 0.11158104240894318, -0.007700982037931681, -0.04821554571390152, -0.005352020729333162, -0.07801919430494308, -0.02650800719857216, 0.04496317356824875, 0.011938092298805714, 0.026169680058956146, -0.0013595058117061853, 0.05485756695270538, 0.06567436456680298, 0.0012906172778457403, 0.13648147881031036, 0.023734988644719124, 0.08998269587755203, -0.0095582390204072, 0.026485173031687737, 0.02947866916656494, -0.03661210834980011, -0.04421375319361687, -0.017298301681876183, 0.02297912910580635, -0.0008622432942502201, 0.043219152837991714, -0.07702449709177017, -0.048244915902614594, -0.0011753002181649208, -0.007110916543751955, -0.053615424782037735, -0.02565167471766472, 0.03301990404725075, -0.0672312006354332, 0.02777446247637272, 0.033486258238554, 0.055901262909173965, -0.06408447027206421, -0.010863428935408592, -0.015546686016023159, -0.001255270792171359, -0.008552140556275845, -0.014277186244726181, -0.11752666532993317, -0.03857119381427765, 0.036908265203237534, -0.10304670035839081, -0.1406712532043457, 5.338419432183107e-33, -0.035239845514297485, -0.024217309430241585, 0.05473233014345169, 0.022820761427283287, 0.028393885120749474, -0.04210122302174568, 0.035068243741989136, 0.15622800588607788, 0.0221007838845253, 0.03761698678135872, 0.07671965658664703, -0.02328198216855526, 0.10679496824741364, 0.025215674191713333, 0.073450468480587, 0.05074753239750862, 0.06479737162590027, -0.003941272385418415, -0.04551118239760399, -0.049933552742004395, -0.07364815473556519, 0.02552993968129158, -0.0714607834815979, 0.015462617389857769, -0.012316695414483547, 0.054023656994104385, 0.06723211705684662, -0.042197808623313904, -0.13952405750751495, -0.04436447471380234, -0.002935767639428377, 0.024541763588786125, -0.10958870500326157, 0.03318606689572334, -0.03618679940700531, 0.09100988507270813, 0.0005205267225392163, -0.0015193012077361345, 0.02488815411925316, 0.06314726918935776, 0.01987849734723568, -0.008496267721056938, 0.004493177868425846, 0.2061968892812729, 0.02684602700173855, 0.020799856632947922, -0.0240657776594162, 0.10361810028553009, 0.031669653952121735, 0.011405418626964092, -0.08602043241262436, 0.03416803479194641, -0.006508660968393087, -0.018249591812491417, 0.01674700528383255, -0.013997111469507217, -0.05165862664580345, 0.04553484916687012, 0.028897026553750038, -0.022380631417036057, 0.01289576105773449, -0.008146153762936592, -0.014556516893208027, 0.018003376200795174, -0.004316835198551416, 0.01513249333947897, -0.04365140199661255, 0.12434182316064835, -0.0656566470861435, -0.03079024702310562, 0.12046895921230316, 0.04114476218819618, -0.034990835934877396, 0.007664693985134363, -0.09582898765802383, 0.05962927266955376, 0.029368393123149872, 0.02580225095152855, 0.007645400706678629, -0.027397513389587402, -0.1363711804151535, 0.022010598331689835, -0.03838039189577103, 0.038143340498209, -0.02666621282696724, -0.015414954163134098, 0.030951106920838356, -0.06612234562635422, 0.007744719740003347, -0.0015180594054982066, -0.04261331632733345, -0.010056674480438232, 0.027605708688497543, -0.09087405353784561, -0.024939894676208496, -4.877212234077888e-08, 0.02553410269320011, 0.1265815794467926, -0.04538703337311745, -0.025004295632243156, 0.014260372146964073, 0.025149479508399963, 0.07109005749225616, 0.03147389367222786, -0.009967900812625885, 0.044358670711517334, -0.0068642375990748405, 0.014319314621388912, 0.02237335965037346, -0.004660268314182758, -0.003350135637447238, -0.05754431337118149, -0.027631275355815887, 0.04403224214911461, -0.024214953184127808, 0.03008132055401802, 0.047187186777591705, -0.018362415954470634, 0.04596714302897453, -0.08293260633945465, -0.00023648078786209226, -0.01700695976614952, 0.02500813640654087, 0.036679912358522415, -0.0171666257083416, -0.012540695257484913, -0.010196696035563946, 0.013239305466413498, -0.07839847356081009, -0.10489100217819214, -0.029697928577661514, 0.004295755177736282, 0.0014388053677976131, -0.013160772621631622, -0.0455457828938961, -0.15142354369163513, 0.012144980952143669, 0.14141105115413666, 0.0033308325801044703, 0.09230928868055344, 0.09918193519115448, 0.02047782950103283, -0.0018556228606030345, -0.01753951981663704, 0.013949376530945301, -0.06404218077659607, -0.09226547181606293, 0.06893295794725418, 0.06536483019590378, -0.020992908626794815, -0.019392572343349457, -0.04842235520482063, -0.004990136716514826, -0.04778695106506348, -0.07618878036737442, 0.020379342138767242, 0.08492814004421234, -0.06385879963636398, 0.06381313502788544, 0.023798275738954544]}}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Extract stage public interface.
|
|
3
|
+
|
|
4
|
+
Takes the top chunks from the ranking stage, concatenates them into a
|
|
5
|
+
token-budgeted context, calls an LLM, and returns structured values for the
|
|
6
|
+
requested extraction keys.
|
|
7
|
+
|
|
8
|
+
Pick an adapter explicitly:
|
|
9
|
+
|
|
10
|
+
from webfetch.extract import ClaudeExtractor
|
|
11
|
+
extractor = ClaudeExtractor()
|
|
12
|
+
values = extractor.extract(
|
|
13
|
+
chunks=top_chunks,
|
|
14
|
+
keys={
|
|
15
|
+
"accuracy": "measurement accuracy including units and conditions",
|
|
16
|
+
"range": "full measurement range with units",
|
|
17
|
+
"resolution": "smallest measurable increment with units",
|
|
18
|
+
},
|
|
19
|
+
)
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from webfetch.extract.base import AbstractExtractor, build_context
|
|
23
|
+
from webfetch.extract.claude import ClaudeExtractor
|
|
24
|
+
from webfetch.extract.gemini import GeminiExtractor
|
|
25
|
+
from webfetch.extract.gpt import GPTExtractor
|
|
26
|
+
from webfetch.extract.groq import GroqExtractor
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"AbstractExtractor",
|
|
30
|
+
"ClaudeExtractor",
|
|
31
|
+
"GPTExtractor",
|
|
32
|
+
"GeminiExtractor",
|
|
33
|
+
"GroqExtractor",
|
|
34
|
+
"build_context",
|
|
35
|
+
]
|