Commit 46fcb31
authored
feat(cli): support
* feat(cli): support 'openkb add <URL>' with content-type sniffing
`openkb add` now accepts http(s) URLs in addition to local paths.
URLs are fetched into raw/ first, then handed to the existing
add_single_file pipeline — so PageIndex / markitdown / hash dedup /
remove all work unchanged.
Routing is decided by the HTTP Content-Type header, validated
against magic bytes (so a CDN that mislabels a PDF as
'application/octet-stream' still routes correctly):
PDF response → urllib chunked download → raw/<sanitized>.pdf
→ existing convert_document → PageIndex (≥20 pages)
or markitdown (short)
HTML response → trafilatura.fetch_url + extract main content
→ raw/<title-slug>.md
→ existing markitdown short-doc path
Anything else → clear error, returns None
Filename derivation: Content-Disposition header (RFC 5987 / quoted /
unquoted forms) → URL basename → URL host fallback. Sanitization
preserves identifier dots like arxiv's "2509.11420" (only strips an
extension when it matches the target extension), replaces shell-
unsafe chars (space / parens / etc.) with '-', collapses repeats,
and caps the stem at 80 chars.
Failure modes covered with stderr messages, no crashes:
- HTTPError (404, 403, etc.) → "[ERROR] HTTP <code>"
- URLError (DNS, refused) → "[ERROR] Network error"
- trafilatura returns None → "[ERROR] No main content extracted"
- <300 chars extracted → "[WARN] only N chars — page may be JS-rendered"
(still saved so user can inspect)
- Unsupported content type → "[ERROR] Unsupported content type"
Architecturally lives in openkb/url_ingest.py — peer to
openkb/converter.py and openkb/indexer.py rather than bloating
cli.py. cli.py only adds a 4-line intercept at the top of `add`.
Live-tested against four URL shapes:
- arxiv.org/abs/<id> → trafilatura → "Trading-R1-...md" (3 KB)
- arxiv.org/pdf/<id> → urllib → "2509.11420v1.pdf" (2.1 MB)
(server's Content-Disposition gives the
version suffix for free — no special
arxiv handling needed)
- claude.com/blog/... → trafilatura → ".md" (1.5 KB)
- CDN .pdf with %20 → urllib → sanitized filename (465 KB)
31 new tests cover all routing branches, magic-byte override paths,
Content-Disposition parsing (3 forms), filename sanitization edge
cases (arxiv ID, spaces, long names, empty), and graceful failure
on HTTP/network errors. 360 total tests pass (329 prior + 31 new).
* fix(url-ingest): address PR #55 self-review findings
- _unique_path() prevents silent overwrites in raw/ when two URLs
sanitize to the same filename (applied at both PDF and HTML write
sites)
- PDF filename now derives from response.geturl() (the post-redirect
URL), so DOI/shortlink resolvers produce sensible names
- add_single_file() returns bool; URL branch unlinks the fetched
raw/* file when the downstream add is skipped (dedup), preventing
orphan files that re-resurrect on every retry
* fix(url-ingest): preserve raw file on pipeline failure, fix HTML echo
Round-2 review on commit 47e5ec2 surfaced two issues:
1. add_single_file returned bool, so the URL branch couldn't tell
"dedup skip" from "mid-pipeline failure". A transient LLM error
during compile_long_doc would delete the just-downloaded PDF and
leave a dangling PageIndex entry (hash registration only happens
on full success, so `openkb remove` couldn't recover it). Now the
function returns Literal["added","skipped","failed"]; the URL
branch only unlinks on "skipped" — failures keep the raw file so
the user can retry without re-downloading.
2. _extract_html echoed the pre-collision filename instead of
target.name, so on a title collision the user was told the file
was saved to a path occupied by the previous document. PDF branch
already used target.name correctly; HTML branch now matches.
* chore(tests): drop unused 'from pathlib import Path' import
Flagged by github-code-quality bot on PR #55. The import was left over
from an earlier draft; nothing in the file references Path.openkb add <URL> with content-type sniffing (#55)1 parent 50d5b1a commit 46fcb31
5 files changed
Lines changed: 948 additions & 9 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
72 | 72 | | |
73 | 73 | | |
74 | 74 | | |
75 | | - | |
| 75 | + | |
| 76 | + | |
76 | 77 | | |
77 | 78 | | |
78 | 79 | | |
| |||
148 | 149 | | |
149 | 150 | | |
150 | 151 | | |
151 | | - | |
| 152 | + | |
152 | 153 | | |
153 | 154 | | |
154 | 155 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
| |||
130 | 131 | | |
131 | 132 | | |
132 | 133 | | |
133 | | - | |
| 134 | + | |
134 | 135 | | |
135 | 136 | | |
136 | 137 | | |
137 | 138 | | |
138 | 139 | | |
139 | 140 | | |
140 | 141 | | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
141 | 150 | | |
142 | 151 | | |
143 | 152 | | |
| |||
156 | 165 | | |
157 | 166 | | |
158 | 167 | | |
159 | | - | |
| 168 | + | |
160 | 169 | | |
161 | 170 | | |
162 | 171 | | |
163 | | - | |
| 172 | + | |
164 | 173 | | |
165 | 174 | | |
166 | 175 | | |
| |||
174 | 183 | | |
175 | 184 | | |
176 | 185 | | |
177 | | - | |
| 186 | + | |
178 | 187 | | |
179 | 188 | | |
180 | 189 | | |
| |||
192 | 201 | | |
193 | 202 | | |
194 | 203 | | |
195 | | - | |
| 204 | + | |
196 | 205 | | |
197 | 206 | | |
198 | 207 | | |
| |||
206 | 215 | | |
207 | 216 | | |
208 | 217 | | |
209 | | - | |
| 218 | + | |
210 | 219 | | |
211 | 220 | | |
212 | 221 | | |
| |||
225 | 234 | | |
226 | 235 | | |
227 | 236 | | |
| 237 | + | |
228 | 238 | | |
229 | 239 | | |
230 | 240 | | |
| |||
395 | 405 | | |
396 | 406 | | |
397 | 407 | | |
398 | | - | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
399 | 416 | | |
400 | 417 | | |
401 | 418 | | |
402 | 419 | | |
403 | 420 | | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
404 | 440 | | |
405 | 441 | | |
406 | 442 | | |
| |||
0 commit comments