P5b and P5c plans: long-document retrieval (BM25 first, background embedding) and /v1/extract + sift extract; given tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
# P5c task 01: `POST /v1/extract`
|
||||
|
||||
**Branch:** `p5c` (`git switch -c p5c master` if it does not exist, else `git switch p5c`; `git status --short` must be empty, otherwise stop)
|
||||
**Commit subject:** `api: POST /v1/extract answers an uploaded PDF like a fetched one (content-addressed cache)`
|
||||
|
||||
## Goal
|
||||
|
||||
`POST /v1/extract` with a PDF as the request body returns the `/v1/fetch` PDF envelope:
|
||||
text with page markers or the map, `pages`, `grep`, or a cited digest. Nothing is fetched.
|
||||
The bytes are hashed, the cache key is the hash, and the same file is extracted once.
|
||||
|
||||
## Files
|
||||
|
||||
- Copy: `internal/api/extract_test.go`
|
||||
- Create: `internal/api/extract.go` (the handler and its envelope)
|
||||
- Modify: `internal/api/api.go`: only the route in `New`, the `Server` field and constant
|
||||
below, and the two lines in `ServeHTTP` (rule 1); `docs/implementer-log.md`
|
||||
|
||||
## Interfaces
|
||||
|
||||
```go
|
||||
package api
|
||||
|
||||
const DefaultPDFMaxUpload = 100 << 20
|
||||
|
||||
// in Server:
|
||||
PDFMaxUpload int64 // 0 = DefaultPDFMaxUpload
|
||||
|
||||
// route: s.Mux.HandleFunc("POST /v1/extract", s.v1extract)
|
||||
// query parameters (all optional): pages, grep, question, mode ("clean" | "digest";
|
||||
// default digest when question is set, else clean)
|
||||
```
|
||||
|
||||
What exists and is reused, unchanged:
|
||||
- `s.caller`, `s.callerClass`;
|
||||
- `s.extractPDF(ctx, c, *fetch.Resource)`, `pdfError`, `s.renderPDF`, `s.storePDF(key, res, doc)`, `s.pdfFromEntry`;
|
||||
- `s.cache()`, `dcache.PDFKey`, `clean.Kind`, `pdfdoc.Full`;
|
||||
- `s.Digest.Run`, `s.Prom.PDFCacheHit`, `writeErr`, `writeJSON`.
|
||||
|
||||
## Rules
|
||||
|
||||
1. **`ServeHTTP`:**
|
||||
- today it wraps every request body in `http.MaxBytesReader(w, r.Body, maxReqBody)` (64 KiB). For `/v1/extract` use `PDFMaxUpload` instead;
|
||||
- add `/v1/extract` to the paths that get `requestDeadline`;
|
||||
- every other path keeps 64 KiB (the given test checks that).
|
||||
2. **Handler:**
|
||||
- Authenticate as the other endpoints do.
|
||||
- Read the whole body. Empty → `400 empty body`. Over the limit (`*http.MaxBytesError`) → `413 too large (>N MB)`.
|
||||
- `clean.Kind(Content-Type, body)` must be `"pdf"`; otherwise `415 extract accepts PDFs only`.
|
||||
3. **Cache:**
|
||||
- `hash := "sha256:" + hex(sha256(body))`, `key := dcache.PDFKey("upload:"+hash, s.callerClass(c))`.
|
||||
- An entry → the doc from it, `cached: true`, `PDFCacheHit`, no child. The content is addressed by its hash, so there is **no TTL and no revalidation**.
|
||||
- Otherwise build `&fetch.Resource{URL: "upload", FinalURL: "upload", Status: 200, ContentType: ct, Body: body, Hash: hash, FetchedAt: now}`, then `extractPDF`, then `storePDF(key, res, doc)`.
|
||||
- Errors go through `pdfError`, via the existing `writeFetchErr` (it adds `Retry-After`).
|
||||
4. **Answer:** the same fields as `/v1/fetch` for a PDF:
|
||||
- `content`, `mode`, `untrusted: true`, `warnings`, `sizes`, `timings`, `pdf`, and `digest` when a digest ran, including P5b's `retrieval` and `embedded`;
|
||||
- `source` is `{"kind": "upload", "hash", "bytes", "cached", "fetched_at"}`, with **no `url`** key;
|
||||
- bad `pages`/`grep` → 400 (as in `v1fetch`); the question limit → the same 422 as `v1fetch`.
|
||||
- Do not call Honcho context for uploads.
|
||||
5. **Logging:** one line, `"extract"`, with caller, bytes, `pdf_pages`, mode and cached. Never a title or any text. The server never sees a filename.
|
||||
|
||||
## Steps
|
||||
|
||||
- [ ] **1.** Branch; copy the given test. **2. See it fail.**
|
||||
- [ ] **3.** `api.go` hooks, **4.** `extract.go`.
|
||||
- [ ] **5.** `gofmt -w`; `go test -race -count=3 ./internal/api/` → `ok`. **6.** `make gate`.
|
||||
- [ ] **7.** Row `p5c/01-extract-api`; commit.
|
||||
|
||||
```sh
|
||||
git add internal/api docs/implementer-log.md
|
||||
git commit -F .state/commit-msg.txt
|
||||
```
|
||||
|
||||
## Done when
|
||||
|
||||
- All tests pass under `-race -count=3`; `make gate` ok; given file byte-identical.
|
||||
Reference in New Issue
Block a user