chore(sync): upstream v0.8.1 — probe tokens, B.AI gateway, FLUX payloads, proxy Test button #2

Closed
epistemophiliac wants to merge 0 commits from upstream-v0.8.1 into main

Upstream release v0.8.1 (tashfeenahmed/freellmapi). Synced via branch+PR per ci skill — never stash-and-restore. Conflicts expected in shared server files (our fork diverged); Aexora-layer files untouched.

Upstream release v0.8.1 (tashfeenahmed/freellmapi). Synced via branch+PR per ci skill — never stash-and-restore. Conflicts expected in shared server files (our fork diverged); Aexora-layer files untouched.
Adds a self-maintaining test guard: the modules declared pure in lib/ must stay free of value imports, and any new import-free module fails the suite until it is classified as guarded or not. Catches the fusion↔proxy import cycle regressing, and the detector covers dynamic imports, re-exports and require().
Three fixes to how the CLI resolves a pinned model:

- `--model` was parsed into CliOptions and then dropped before it reached the setup generators, so `setup-<tool> --model X` silently wrote whatever primaryModel() preferred.
- `--model` was checked against the `?available=true` roster only, so a model that exists but is out of quota was reported as an unknown id. It is now validated against the unfiltered catalog, and a registered-but-unavailable model launches with a warning instead of an error. A failed unfiltered fetch degrades to the filtered roster and says so.
- A model with no published context window had one invented as 128_000 for CLAUDE_CODE_AUTO_COMPACT_WINDOW. The variable is now left unset so Claude Code applies its own default.
Adds `freellmapi doctor [tool…]`, which resolves a coding agent's effective base URL the way that tool resolves it — in a process inheriting the user's session environment — and then probes /livez.

The failure it diagnoses leaves nothing in any server-side log, because the request never arrives: a launcher that writes ANTHROPIC_BASE_URL can be outranked by a settings.json env block (or vice versa), and nothing says so. Verdicts separate the cases that look alike: routed, shadowed (a lower layer set a different URL and something outranks it), elsewhere (a different endpoint, or the right port answering something that is not this gateway), degraded (this gateway, reporting that it cannot serve), unreachable, unknown. Nonzero exit unless every tool is routed, so it is usable as a script precondition.

Includes two follow-up fixes: the stray-positional check now runs before the setup-* dispatch (it had moved below it, so 'setup-claude typo' silently ignored the word), and the managed-settings scan now reads the managed-settings.d/ drop-in directory, ranked above the base file with the alphabetically-last drop-in first.
Adds a Test button beside Save in Settings → Outbound proxy. It probes a draft proxy URL without saving it, falls back to the saved URL when the field is empty, and runs direct when no proxy is configured at all, so the button is useful before anything has been set up. Any HTTP response counts as success — a 401 or 403 without a key still proves the route connected; only a network-level failure is reported as a proxy failure. SOCKS URLs route through socksFetch, HTTP(S) through an undici dispatcher, and an unbuildable agent comes back as a structured error rather than a throw.

Fixed before merge: the probe was hardcoded to https://api.openai.com/v1/models, which made the verdict wrong in both directions — an install that never calls OpenAI pinged it on every click, and a network where that host is blocked reported a working proxy as broken, which is the population most likely to be configuring a proxy. The target is now chosen by the caller: the /models endpoint of a provider the operator holds an enabled key for, preferring one not already bypassing the proxy so the probe exercises the proxy path. PROXY_TEST_URL overrides it, and only an install with no keys falls back to a neutral, non-vendor reachability endpoint. The probed target is returned with the result and its host is shown next to the verdict.
Adds an opt-in Claude Code PreToolUse hook that reads CONTRIBUTING.md at fire time and reminds the agent to check its diff against the repo's rules before `git commit` / `git push`. It reminds; it does not block.

Nothing is wired up by this change: no .claude/settings.json is added, so cloning the repo changes no behavior. Contributors who want it paste the snippet from the new CONTRIBUTING.md section into their own .claude/settings.local.json. `node .claude/hooks/contributing-check.mjs --preview` shows what it would say.

9 tests, no new dependencies, wired into `npm test` as `test:hooks`.
The model detail page never displayed the unified model id — the id a caller
actually puts in a request body. It was only reachable via the hover copy
button in the Models table (#708).

The summary chips now lead with `canonicalId` plus a copy button, and the
per-provider member id in ProviderSettingsRow gets the same pill treatment and
its own copy button. Both keep `min-w-0 truncate` so long ids elide instead of
stretching the row.

No new i18n keys — reuses `models.copyModelId`, so all 60 locales stay in
parity (check-i18n: 60 locales / 820 keys).

Closes #725. Refs #708.
probeEndpointModel pinned max_tokens to 1 to keep the probe cheap. Several relays enforce a floor above that and reject the request outright: b.ai's deepseek-v4-flash 400s with "max_tokens must be greater than 2", so a working endpoint was reported as broken and kept its cooldown.

Raise it to 4 via an exported PROBE_MAX_TOKENS. That clears every floor seen so far and still costs a rounding error per probe. Fixed generally rather than special-cased per provider, since the floor is not unique to one relay.

The existing test pinned the literal 1, so it now asserts the constant, and a new case drives a mock upstream that 400s anything at or below 2.

providers/modelscope.ts keeps max_tokens: 1 for key validation, where it works, so this change stays single-purpose.

Closes #903.
chore(release): v0.8.1 (#928)
Some checks failed
Docker / Merge manifest and push (pull_request) Has been cancelled
CI / Test & build (Node 22) (pull_request) Has been cancelled
Desktop release / Build (ubuntu-latest) (pull_request) Has been cancelled
Desktop release / Build (macos-latest) (pull_request) Has been cancelled
Desktop release / Build (windows-2022) (pull_request) Has been cancelled
CI / Test & build (Node 20) (pull_request) Has been cancelled
Docker / Build (linux/amd64) (pull_request) Has been cancelled
Docker / Build (linux/arm64) (pull_request) Has been cancelled
d8ba550c57
App 0.8.0 -> 0.8.1. CLI 0.3.0 -> 0.4.0, unblocking the npm publish of freellmapi doctor (#862) and the --model catalog fix (#861), which had been on main but unpublished since 2026-07-27.
epistemophiliac closed this pull request 2026-08-25 10:38:56 +00:00
Some checks failed
Docker / Merge manifest and push (pull_request) Has been cancelled
CI / Test & build (Node 22) (pull_request) Has been cancelled
Desktop release / Build (ubuntu-latest) (pull_request) Has been cancelled
Desktop release / Build (macos-latest) (pull_request) Has been cancelled
Desktop release / Build (windows-2022) (pull_request) Has been cancelled
CI / Test & build (Node 20) (pull_request) Has been cancelled
Docker / Build (linux/amd64) (pull_request) Has been cancelled
Docker / Build (linux/arm64) (pull_request) Has been cancelled

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
epistemophiliac/LLMAPI!2
No description provided.