109 lines
5.0 KiB
Markdown
109 lines
5.0 KiB
Markdown
# P5a task 08: page citations, metrics, CLI flags, skill, unit, README
|
|
|
|
**Branch:** `p5a` (`git switch p5a`; `git status --short` must be empty, otherwise stop)
|
|
**Commit subject:** `P5a: digests cite pages; sift_pdf_* metrics; sift fetch --pages/--grep; skill, unit and README`
|
|
|
|
## Goal
|
|
|
|
Finish P5a:
|
|
- digests of PDFs cite `(p. N)`;
|
|
- the PDF path is visible in `/metrics`;
|
|
- `sift fetch` can ask for pages and grep;
|
|
- the agents' skill tells them PDFs work;
|
|
- the unit gets the memory the child needs;
|
|
- the README describes all of it.
|
|
|
|
## Files
|
|
|
|
- Copy: `internal/digest/pages_test.go`, `internal/metrics/pdf_test.go`, `cmd/sift/fetchpdf_test.go`
|
|
- Modify:
|
|
- `internal/digest/digest.go` (prompt line, `PromptVersion`)
|
|
- `internal/metrics/metrics.go`
|
|
- `internal/api/pdf.go` (the metric calls)
|
|
- `cmd/sift/fetchcmd.go`
|
|
- `skill/SKILL.md`
|
|
- `nix/module.nix` (**only** `MemoryMax`)
|
|
- `README.md`
|
|
- `docs/implementer-log.md`
|
|
|
|
## Rules
|
|
|
|
1. **Digest.**
|
|
- Add one sentence to `systemPrompt`: `When the document has "--- page N ---" markers, cite
|
|
the page of each fact as (p. N).`
|
|
- Set `PromptVersion = "3"`. The digest cache keys on it, so digests made with the old
|
|
prompt are not served.
|
|
- `Split` already keeps markers whole; the given test pins that.
|
|
2. **Metrics** (`metrics.go`):
|
|
- `func (s *Set) ObservePDF(outcome string, d time.Duration, pages int)`. It feeds:
|
|
- the counter `sift_pdf_extract_total{outcome}`;
|
|
- the histogram `sift_pdf_extract_seconds`;
|
|
- the counter `sift_pdf_pages_total`.
|
|
|
|
`outcome` must be one of `ok`, `no_text`, `encrypted`, `unreadable`, `timeout`,
|
|
`too_large`, `busy`; anything else is counted as `other`. The label never carries raw
|
|
text: the given test sends `blocked: evil.example`.
|
|
- `func (s *Set) ObservePDFWait(d time.Duration)` feeds the histogram
|
|
`sift_pdf_pool_wait_seconds`.
|
|
- `func (s *Set) PDFCacheHit()` feeds the counter `sift_pdf_cache_hits_total`.
|
|
- Register them on the Set's own registry, like `sift_requests_total`.
|
|
- In `internal/api/pdf.go`:
|
|
- call `ObservePDF` once per extraction attempt, with the outcome and the pages extracted;
|
|
- call `ObservePDFWait` with the time spent waiting in `PDFPool.Do` before the job
|
|
started (measure around `Do`: total minus the job's own time);
|
|
- call `PDFCacheHit` for every answer served from the cache, fresh or after a 304.
|
|
3. **CLI** (`fetchcmd.go`).
|
|
- Parsing becomes a struct, and today's tuple function stays as a wrapper so the existing
|
|
tests keep compiling:
|
|
```go
|
|
type fetchOpts struct {
|
|
URL, Mode, JQ, Question, Pages, Grep string
|
|
NoContext, Recover, JSON bool
|
|
}
|
|
func parseFetchOpts(args []string) (fetchOpts, error) // the parser; knows --pages and --grep (both take a value)
|
|
func parseFetchArgs(args []string) (…the same 8 results as today…) // calls parseFetchOpts
|
|
func fetchBody(o fetchOpts) map[string]any // the /v1/fetch request body
|
|
```
|
|
- `fetchBody` builds today's body: url, mode (defaulting to digest when there is a question,
|
|
else clean), jq, question, context (question set and not `--no-context`), recover. It adds
|
|
`"pages"` and `"grep"` **only when set**.
|
|
- The command uses `parseFetchOpts` and `fetchBody`.
|
|
- Add `[--pages a-b] [--grep RE]` to `fetchUsage`.
|
|
- The given `cmd/sift/fetchpdf_test.go` and the existing flag tests must pass.
|
|
4. **Skill** (`skill/SKILL.md`):
|
|
- Remove the `415 unsupported type` row's mention of PDF.
|
|
- Add a short "PDFs" section:
|
|
- PDFs work;
|
|
- a long one answers with a map (outline with page numbers, first pages);
|
|
- then use `--pages 41-60` or `--grep 'pattern'`, or `--question` for a digest that cites
|
|
pages;
|
|
- scanned PDFs return `no text layer`;
|
|
- never download a PDF and run `pdftotext` yourself.
|
|
5. **Unit** (`nix/module.nix`): `MemoryMax = "1G";` (was `"512M"`). Nothing else in the file
|
|
changes.
|
|
6. **README:**
|
|
- a "PDF documents" section covering: detection, limits (100 MB, 120 s download, 60 s
|
|
extraction, 2,000 pages), the child process, the pool (2 slots, 4 waiting, 20 s), the
|
|
cache (24 h, conditional GET), the answers (full / map / pages / grep / digest with
|
|
citations), the errors table from task 06, and the metrics;
|
|
- note that the engine is `go-pdfkit/extract` v0.3.0, chosen by the bake-off in `docs/P5a.md` §3.
|
|
|
|
## Steps
|
|
|
|
- [ ] **1.** `git switch p5a`; copy the three given tests. **2. See them fail.**
|
|
- [ ] **3.** digest, **4.** metrics and the calls in `pdf.go`, **5.** CLI, **6.** skill, unit,
|
|
README.
|
|
- [ ] **7.** `gofmt -w`; `make gate`.
|
|
- [ ] **8.** `make build && bin/sift fetch 2>&1 | grep -c -- '--pages'` prints `1`.
|
|
- [ ] **9.** Row `p5a/08-digest-cli-docs`; commit.
|
|
|
|
```sh
|
|
git add internal/digest internal/metrics internal/api/pdf.go cmd/sift skill nix/module.nix README.md docs/implementer-log.md
|
|
git commit -F .state/commit-msg.txt
|
|
```
|
|
|
|
## Done when
|
|
|
|
- All tests pass; `make gate` ok; given files byte-identical; the `nix/module.nix` diff is the
|
|
one line.
|