Skip to content

Copy Pi model extras onto Custom without replacing Stem's Pi - #6

Open
TeleporterGuy wants to merge 2 commits into
join3r:mainfrom
TeleporterGuy:cursor/copy-pi-model-extras
Open

Copy Pi model extras onto Custom without replacing Stem's Pi#6
TeleporterGuy wants to merge 2 commits into
join3r:mainfrom
TeleporterGuy:cursor/copy-pi-model-extras

Conversation

@TeleporterGuy

Copy link
Copy Markdown
Contributor

Summary

  • Custom endpoint can take a pasted or linked Pi models.json overlay (reasoning, thinkingFormat, maxTokens, and the rest of that provider block) and merge it onto Stem's own custom catalog.
  • Stem's isolated Pi home stays Stem's: no pointing at ~/.pi, no wholesale models.json replace, ChatGPT/Grok auth and Pi settings untouched.
  • Catalog sync keeps those extras until you replace the overlay or disconnect (both warn first). vLLM does not advertise thinking flags, so without this Qwen/GLM stay reasoning: false after every spawn.

Test plan

  • Add a Custom endpoint, paste a Pi models.json (or a path), Read overlay, Copy into Stem; picker shows custom/<id> and reasoning effort for models with reasoning: true.
  • Restart Stem; extras still present (thinking format / max tokens still apply; effort control still shown).
  • Copy again while extras exist: confirm warning, then replace.
  • Disconnect Custom: confirm warning; extras gone; your source file unchanged.
  • Enable with typed IDs while extras are locked: confirm warning; catalog falls back to { id } stubs.
  • npx vitest run tests/unit/models-copy.test.ts tests/unit/models-config.test.ts tests/unit/settings.test.ts

vLLM does not advertise thinking flags, and Stem's catalog sync was stripping them to id-only models. A pasted or linked models.json overlay now merges onto the Custom endpoint and survives startup until you replace or disconnect it.
A single socket read can carry an addressed control frame and a later broadcast together, which made the pushTo test fail on CI when it expected exactly one block.
@join3r

join3r commented Aug 17, 2026

Copy link
Copy Markdown
Owner

Could you test it on your endpoint? That's the one place the wire format is
actually proven, and it's the reason I'd rather you drove it than me:

  1. git pull on main, npm install, npm run dev
  2. Settings → Models → select your Custom endpoint
  3. Import from a models.json → paste your real models.json → pick the provider
  4. Read what it filled in, adjust if you like, Save
  5. The effort control should appear in the composer for that model — and each
    level should actually change the reply

join3r added a commit that referenced this pull request Aug 17, 2026
The per-model overrides box asks for strings nobody guesses: which
thinkingFormat a server wants, which levels it answers to. The person who
reported the problem had all of them already, in a working pi models.json for
the same box — and the feature made them transcribe it by hand into a
differently-shaped object, which is the step most likely to be the thing that
goes wrong.

Import from a models.json takes the paste and does the reshaping. It lives
inside OverridesField, so the add form and the edit-in-place block both get it
with no wiring of their own, and it writes nothing: it calls onChange with
converted text and stops. Save remains the single write path, still validating
through parseModelOverrides and still refusing rather than writing — so an
import is a suggestion to read and edit, not a change to anything. That matters
more here than it looks: the input is a file the user did not necessarily write.

Provider-level compat and headers fold down into every model, the model's own
value winning any key it names. pi merges provider compat into each model
itself (mergeCompat in provider-composer.js) and reads modelOverrides[id]
.headers onto the request (rawModelHeaders), so this is what pi would have done
with the file rather than an approximation — and it is the only way the
provider-wide thinkingFormat of a typical vLLM config survives a trip through a
per-model box. Stem writes its own provider block into models.json, pinning
supportsReasoningEffort false, so a model that inherits everything and declares
nothing still needs an entry of its own.

id becomes the key; api and baseUrl are dropped. pi's ModelOverrideSchema has
no such fields, so they would ride along inert while READING as though the box
could repoint the endpoint — the one thing it deliberately cannot do.

Only the import is taken from PR #6. The rest of that branch writes the copied
block into the provider's models array and freezes the catalog, which (verified
by running it) replaces Stem's compat defaults wholesale, ends model discovery,
and writes unvalidated entries that make pi's loader fail models.json as a unit
— getProviderIds() returns [], so Ollama goes down alongside the endpoint being
edited. Its file-path field and IPC channel are not taken either: paste is
enough, and a path is a second thing to get wrong.

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.

2 participants