Files
stack/tests
kert 2ba846b39a feat(llm): search web UI at /ui/search (refs #578)
A minimal, framework-free search page served by the llm service itself,
in the same editorial style as the chat page, so the retrieval endpoints
(#570) are usable from the dashboard and not only via curl.

- src/llm/web/search.html: query box + collection/kind/docket/year
  filters (exactly the values the API validates), results with kind,
  linked label, date, docket/comment id, title and snippet, a "similar"
  toggle per result carrying a bib item_key (renders /similar/{key} in
  place), limit/offset pagination, and the search state mirrored into
  the query string so a search is shareable and survives a reload.
  Loading/error states surface the API's own 400/404 `detail`. All API
  data becomes text nodes — nothing is assigned to innerHTML — and the
  page carries no external script/style URL, so it renders behind
  oauth2-proxy with no outbound CDN access (fonts fall back to the
  local serif stack). An "llm:" tag filter is left as an HTML comment
  until the tagging chain (#575) makes chunks carry those tags.
- api.py: `GET /ui/search` (HTMLResponse, like `/` serves chat.html),
  registered with and without the trailing slash so the headless probe's
  readiness check gets a 200 instead of a 307 redirect.
- chat header → "Search", search header → "Chat".
- tests/llm/test_api.py::TestSearchPage: the route serves the form and
  every allowed filter value, the trailing-slash form works, the
  results/similar/pagination/error code is present, the page has no
  external script/style URL and no innerHTML assignment, and both nav
  links resolve to routes that return 200.

Headless probe, once the image is rolled (do not run before the roll —
it probes the live service):

    python dev/scripts/nb_fe_smoke.py \
        --url http://llm:8000/ui/search --network gateway

nb_fe_smoke's --url mode probes `<base>/` and `<base>/?file=<notebook>`
and fails on any console or page error; with the base above, both are
the search page (the unused ?file= query is ignored by the route), which
is why the route also answers the trailing-slash form.
2026-09-11 18:48:19 -04:00
..