| Age | Commit message (Collapse) | Author | Files | Lines |
|
|
|
|
|
The convention was documented but not its consequence: because None means
keep, no caller can write NULL through this method, and that is invisible
from the signature. Unreachable today since every field is filled once
when a reply finishes and never cleared.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
A nan or inf in [providers.*] survived float() and reached the cost
arithmetic, rendering $nan or, for -inf, a negative cost. Worse than a
display wart: is_priced answered True, so the readout took the priced
branch and showed a poisoned figure in the one state the ? is there to
admit it cannot say.
Guarded at both ends. providers._number mirrors models._get, which
already covered the models.ini writer. is_priced states its own
predicate completely rather than trusting its callers, since the dialog
is a third writer and the file is documented for hand-editing.
Records why float money stays, and that exact-equality assertions in the
cost test hold for the chosen prices rather than in general.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
float() accepts "nan" and "inf", so a hand-edited models.ini could feed
either into the cost arithmetic and render "$nan". Rejected once in _get
rather than at each downstream call site.
A model whose id is literally DEFAULT wrote a [DEFAULT] section, which a
stock configparser reader treats as defaults inherited by every other
model. Since the file is documented for hand-editing, that reintroduces
the contamination the renamed default_section exists to prevent, so the
id is escaped on write and mapped back on read.
Also drops the marker branch in save(), leaving mark_skipped() as the
sole writer of the "marker present iff no real keys" invariant, and
prunes two test blocks that could not fail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
A malformed local entry was the one skip that said nothing, on the
reasoning that local still ends up working. It does, but the merge
rebuilds it from the bare base_url, so an api_key, filter or ctx_size
set on that entry is dropped without a word. The app then comes up
looking healthy, which is the strongest possible signal that nothing is
wrong, making this the case that most needs saying, not least.
Local surviving at all depends on DEFAULTS supplying base_url, since
that is what the rebuild reads. Noted in config.py so the key is not
removed as redundant, and pinned by a test.
The not-a-table wording now follows what was written: a list gets the
single-bracket fix by name, while a scalar entry does not, since telling
someone to fix a [[...]] they never typed points at the wrong line.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176tzAW6H1i2Kz8vm2XXVGV
|
|
Eight config shapes, all valid TOML, crashed config.load() before any
window existed, so the user got a traceback on a terminal a desktop
launch does not have. Skip malformed providers instead, keeping the
local path working, and say on stderr which one was dropped and why.
The guards are isinstance tests rather than a try/except because the
shapes raise three different exception types, and the local entry is
checked the same way: [[providers.local]] is a truthy list that raised
during the bare base_url merge, before the loop could skip it.
A config that cannot load at all now reports the file and exits rather
than half-starting on defaults the user never configured.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176tzAW6H1i2Kz8vm2XXVGV
|
|
A TimeoutExpired chained without from None already prints no secret,
since its __str__ shows only the command and the duration. The suppressed
display buys a tidier traceback, not a narrower leak, and the comments
claimed otherwise. The from None calls stay.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Raise KEY_TIMEOUT to 120s: a graphical pinentry plus a hardware token the
user has to find and touch makes 30s a plausible successful unlock, and
failing one converts a slow success into an error the user can only retry
against the same clock.
Point the timeout message at a pinentry that never appeared, which is the
silent failure, rather than at one the user is already looking at. Name
the binary when pass is missing instead of reporting a bare Errno 2.
Raise from None so a partial secret on the failed process stdout stays
out of printed tracebacks, and record on the cache that it is unlocked
only because every caller resolves on the GUI thread today.
Rename KeyError_ to KeyResolutionError before later tasks import it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
A provider name containing a colon builds ids that split back to a
different provider, and an empty name builds ':model'; both routed to the
local router under a nonsense name with no error, so parse() now skips
them. split() stays non-injective by design, since bare local ids are the
premise here, so the residual risk is recorded as a ponytail comment with
its upgrade path rather than hidden. Also pin the trailing-colon form Task
9 relies on, and the empty-needle filter, which were correct but untested.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
The synthesis guard tested key membership, so a [providers.local] that set
only an api_key claimed the slot, blocked the bare base_url from filling
it, then failed the URL check and vanished. Losing the local provider is
the one outcome this feature cannot have. The guard now tests the URL and
merges, so the table adds detail to the local provider rather than
replacing it.
A filter given as a bare string was iterated character-wise, turning
filter = "qwen" into four needles that match almost every model id. Both
failures were silent, which is what made them worth fixing now.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Sixteen TDD tasks. Providers, model-id namespacing and key resolution go
in a new providers.py; metadata and cost math in models.py; the settings
dialog in modeldialog.py, keeping ui.py from growing further.
The last task is manual: it needs a real key and spends real money, and
it is where cloud tool-calling behaviour gets found out rather than
guessed at.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|