Skip to content

feat: paginate /models in UI + "Fetch All Pages" option - #20

Open
dawidope wants to merge 1 commit into
mainfrom
feat/models-pagination
Open

feat: paginate /models in UI + "Fetch All Pages" option#20
dawidope wants to merge 1 commit into
mainfrom
feat/models-pagination

Conversation

@dawidope

Copy link
Copy Markdown
Contributor

Problem

GET /models is paginated, and the tester had no way to reach past the first page.

The registry entry for List Models had params: [], so the form offered no fields at all — every call was a bare GET /models, which returns 25 models, page 1. There was no way to ask for page 2.

Raising the limit alone does not fix this. Verified against the live API:

Query Result
per_page=100 ignored → still per_page: 25
limit=100 clamped server-side → per_page: 50
limit=50 works — the hard ceiling
page=2 works

So 50 is a server-side cap, and reading more than 50 models always requires walking pages.

Changes

  • List Models endpoint — added page and limit fields, plus a _fetchAll ("Fetch All Pages") checkbox that walks every page and merges the results. page/limit hide via visibleWhen while it is on, since the walker owns them in that mode.
  • src/lib/pagination.ts (new) — extracted the page walker so it is not duplicated. /api/models already paginated with its own inline copy; it now uses the shared helper. The deAPI quirks above are documented there.
  • /api/proxy — handles _fetchAll generically (any GET endpoint, never a price-calc), not as a hardcoded special case for /models. If deAPI adds another paginated GET, it only needs a registry field. The flag is stripped from params so it is never forwarded as a query param.

Note on the merged response

It carries a marker so it is not mistaken for a verbatim API response:

"_tester": { "merged_pages": true, "pages_fetched": 6, "items": 27, "truncated": false }

This matters because meta in a merged response comes from the last page (so current_page === last_page), which would otherwise look like a bug. truncated flags a stop at MAX_PAGES (20 × 50 = 1000 items) rather than silently dropping items.

Scope note

The model dropdowns were never affected — /api/models has walked all pages since 97ed2c4. This PR fixes the manual tester endpoint, and de-duplicates the walker along the way.

Testing

At 27 models everything fits in one page of 50, so the multi-page path had never actually executed. I temporarily lowered PAGE_LIMIT to 5 to force a real 6-page walk, then restored it:

Case Result
_fetchAll=true, PAGE_LIMIT=5 27 models, 27 uniquepages_fetched=6, truncated=false
_fetchAll=true, PAGE_LIMIT=50 27 models, pages_fetched=1
_fetchAll=false, page=2&limit=10 10 models, meta.current_page=2, no _tester — old path untouched ✅
invalid _endpointId + _fetchAll 400, no crash ✅
/api/models (cache) 27 models ✅

tsc --noEmit clean, npm run lint reports no warnings or errors.

🤖 Generated with Claude Code

deAPI paginates /models and caps `limit` at 50 server-side (`limit=100`
still returns per_page=50, and `per_page` is accepted but silently
ignored), so more than 50 models can only be read by walking pages.

The registry entry for "List Models" had `params: []`, so the tester
could only ever issue a bare GET /models — 25 models, page 1, with no
way to request the next page.

- add `page` + `limit` fields to the List Models endpoint
- add a `_fetchAll` checkbox that walks every page and merges the result;
  `page`/`limit` hide via visibleWhen while it is on
- extract the page walker to lib/pagination.ts and reuse it in
  /api/models, which already paginated with its own inline copy
- handle `_fetchAll` generically in the proxy (any GET endpoint), not as
  a special case for /models

The merged response carries a `_tester.merged_pages` marker so it is not
mistaken for a verbatim API response — its `meta` comes from the last
page, and `truncated` flags a stop at MAX_PAGES rather than silently
dropping items.

Verified against the live API with PAGE_LIMIT temporarily lowered to 5 to
force a real multi-page walk: 6 pages merged to 27 unique models, no
duplicates or gaps. The single-page path is unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant