0.9.0
LM Studio, a refusal that finally explains itself, and eight quiet defects.
LM Studio is back in the list
The preset was dropped in August after a symptom that looked like the server's fault: the
model list arrived, the question got no answer. Measured against a running instance, neither
half turned out to be mysterious.
The list travels through requestUrl, which goes around the browser, so it works. The stream
travels through fetch — and LM Studio sends no Access-Control-Allow-Origin at all, while
the preflight on /chat/completions answers 400. The POST never leaves the building. Ollama,
for contrast, echoes app://obsidian.md back, which is exactly why it streams.
So LM Studio returns with no stream, the same way ChadGPT has always worked: the answer is
fetched whole, and the streaming toggle hides itself rather than sitting there pretending. CORS
is a switch in LM Studio's own server settings, but it is off by default — and a setting that
only works after a trip into someone else's settings is not a default.
No key is asked for: the address is already recognised as local. An address that has been
sitting in your settings as "another one" becomes the preset on its own, model and all.
A refusal now says what went wrong
An OpenAI-shaped error carries its explanation in error.message, and that was the only shape
the plugin knew how to read. Plenty of servers answer with a bare string instead — LM Studio
uses the first shape for "no such model" and the second for everything else:
{"error":"The number of tokens to keep from the initial prompt is greater than the context
length (n_keep: 8637 >= n_ctx: 8192). Try to load the model with a larger context length,
or provide a shorter input."}
Every word of that used to be thrown away, and what reached you was "the provider rejected the
request", full stop. Both shapes are read now.
Where the complaint is about the context window, a line is added in your own language. This
one is worth knowing: a local model is loaded with the context it was given at load time, not
the one it is capable of — a model advertising 262 144 tokens is routinely holding 8 192, and
a note sent as context will not fit.
Two spaces at the end of a line
Every rewriting action carries a paragraph asking the model to leave the markup alone. It
listed markdown symbols, indentation, code blocks and wiki-links — and said nothing about the
one thing models actually destroy: the two trailing spaces that make a hard break. That is
what goes first, and a couplet comes back as a paragraph.
The paragraph now names the mechanism instead of assuming it. What went the other way was a
matter of taste: the old wording also declared a missing full stop or a lower-case letter
opening a line to be the author's device rather than a mistake. Not everyone writes that way
on purpose, and the models that ignore instructions ignored it anyway.
Eight quiet defects
Found by reading the whole plugin again after 0.8.0.
- Asking again, or retrying after an error, silently cleared the attachment chip. A picture
attached for the next question vanished without a word — and stayed in session memory for
good, since it never reached the feed and nothing was left to forget it. - Closing the panel lost the attachments. The data outlived the list naming it, which comes
to the same thing as not keeping it. - The copy button under an answer had nothing to catch. When the clipboard refused, nothing
appeared at all: the notice stands after the await and was never reached. - A rewrite that ran into the model's length limit threw away what it had cost. The first
pass is the expensive one, and continuing billed only for the second. Both are added now, and
the price appears straight away — it has been paid whether or not the answer is finished. - A single-page PDF longer than the limit travels whole, but was announced as a beginning —
on the chip and to the model, which would then decline to judge an "incomplete" text. - A document that did not survive a restart called itself an image.
- A saved conversation was signed with the model from the settings header, even when a
private chat or a one-off "ask again" had answered it. - Dragging promised more than it did. The frame lit up for any text while the drop handler
knew only pictures and PDFs. A note now lands on the chip as a fragment, and plain text lands
in the question field at the caret.
Under the hood
412 checks, up from 385. describeError is exported to the tests — there was no way to reach
it before, which is precisely why the refusal text went missing for so long.