Skip to content

[TUI] a saved preset's lower-case model tag reaches setup as its key (Fix #4209) - #4317

Closed
XiaoBinGan wants to merge 1 commit into
Niko1221:mainfrom
XiaoBinGan:fix/preset-model-tag
Closed

XiaoBinGan wants to merge 1 commit into
Niko1221:mainfrom
XiaoBinGan:fix/preset-model-tag

Conversation

@XiaoBinGan

@XiaoBinGan XiaoBinGan commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Problem and result

Starting a saved preset with any choice that differs from the installed config (--vision off, --kv,
--context) reruns setup.py, which then stops:

strata: The model could not be installed. Run it again: finished steps are skipped.
setup.py: error: argument --model: invalid choice: 'iq3_xxs' (choose from 'Q2_0', 'IQ2_XS', 'IQ3_XXS', ...)

Two halves disagree about a model's name. strata_cli/launch/settings.py writes the choice into the preset
as a lower-case tag (--model iq3_xxs), LaunchRunner.setup_args() forwards it unchanged, and
setup.py's --model choices are the MODELS table's own keys, which are case-sensitive. Typing
--model iq3_xxs on the command line works, because cmd.py normalises it through Catalog.resolve();
the preset path never reaches that.

After this change the preset path uses the same resolver, so setup_args() carries --model IQ3_XXS and the
re-run installs instead of stopping.

Related discussion: #4209 — the report, including the setup log line and the two lines it points at.

Approach

def setup_args(self) -> list[str]:
    cat, s = self.catalog, self.s
    fam, model = s.family, s.model
    try:                     # a saved preset keeps the lower-case tag ("iq3_xxs", #4209), while setup's
        fam, model = cat.resolve(str(model), None if fam is None else str(fam))    # --model takes its key
    except ValueError:       # an unknown name is setup's to report, in its own words
        pass
  • Catalog.resolve() is the resolver the command line already uses, so a preset and a typed command now
    agree. It also normalises the family when a family-free name is ambiguous.
  • A name that cannot be resolved is left exactly as it was, so setup still reports it with its own message
    and its own "did you mean" list. Nothing is refused here that was not refused before.
  • No other argument changes: the list this method builds is otherwise byte-identical (the tests below check
    the neighbours).

Validation

Passed: py -m pytest tests/terminal/test_launch.py -q (Python 3.11.16, from a source checkout):
5 failed, 110 passed, 1 skipped. The five failures are pre-existing on this Windows checkout: with
strata_cli/launch/runner.py reverted, the same file is 6 failed, 109 passed, 1 skipped — the sixth is
the new test, and it fails with the reported symptom:

>       self.assertEqual(args[args.index("--model") + 1], "IQ3_XXS")
E       AssertionError: 'iq3_xxs' != 'IQ3_XXS'
tests\terminal\test_launch.py:354: AssertionError

Passed: the new cases: a lower-case tag for qwen (iq3_xxs → IQ3_XXS), coder (iq1_m → IQ1_M),
gyro (gyro-s → Gyro-S), a name already in the table's spelling (IQ3_S) and the family the preset
carries; and an unknown name (not-a-model) still reaching setup unchanged.

Failed: none of the new cases.

Not run: a real preset start on Windows as in the report: that needs a model installed on this host and a
GPU the checkout can drive, and the report already shows the failure. I did not repeat the end-to-end start
and am not claiming I did.

Not run: pytest tests/terminal as a whole: only the file this change touches and its neighbours were run
(test_launch.py), plus the other suites listed in #4314/#4315 work, which do not import this method.

Limits

  • Only the preset/choices path is fixed. A preset whose model was saved under a name the catalog no longer
    offers still reaches setup, which reports it, as before.
  • The family is resolved too, so a preset saved with an ambiguous name now carries the family the resolver
    picks rather than the family as saved. That is the same answer the command line gives for the same name.

…p as its key

strata_cli/launch/settings.py saves the model into a preset/choices file lower-case ("--model iq3_xxs"),
and LaunchRunner.setup_args() forwarded it unchanged. setup.py's --model choices are the MODELS table's own
keys ("IQ3_XXS", "Gyro-S"), so argparse refused the argument: starting a saved preset with any choice that
differs from the installed config (--vision, --context, --kv) reran setup and stopped there. Typing the same
name on the command line worked, because cmd.py normalises it through Catalog.resolve().

setup_args() now resolves the pair through the same Catalog.resolve() and keeps the old value when it
cannot. The except is deliberately broad: the resolver reads the model table, and a table that cannot be
read here must not turn a launch that worked into a traceback - the values then go on to setup unchanged,
exactly as before. The new test drives that case with a resolver that raises.
@XiaoBinGan
XiaoBinGan force-pushed the fix/preset-model-tag branch from 14a1b6f to 2f9f877 Compare October 27, 2026 14:16
@XiaoBinGan

Copy link
Copy Markdown
Contributor Author

A reviewer pass over this changed one thing: the except was too narrow. Catalog.resolve() reads the model table, which can fail for reasons that are not "no such model" (the Terminal that carries no list raises CatalogUnavailable, a RuntimeError). Catching only ValueError would have turned a launch that worked into a traceback — the opposite of what this fix is for.

It is now except Exception with the reasoning in the comment, and the fallback is the pre-#4209 behaviour: the preset's own values go to setup.py unchanged.

try:                     # a saved preset or choices file keeps the lower-case tag ("iq3_xxs", #4209), while setup's
    fam, model = cat.resolve(str(model), None if fam is None else str(fam))    # --model takes the table's own key
except Exception:        # noqa: BLE001  a name the list does not hold, or a list that cannot be read here, is setup's
    pass                 # to report: passing the values on unchanged keeps the launch behaving as it did before

A third test drives that case with a resolver patched to raise, and asserts the arguments are the preset's own:

with a failing resolver -> --model iq3_xxs | --family qwen

Re-checked after the change (python -m pytest tests/terminal/test_launch.py -q, Python 3.11.16):

5 failed, 111 passed, 1 skipped

Against main on this Windows checkout the same file is 6 failed, 109 passed, 1 skipped — the sixth failure is this PR's own new case, failing with AssertionError: 'iq3_xxs' != 'IQ3_XXS'. The five that remain fail without the change too. The three new cases all pass: iq3_xxs → IQ3_XXS, coder/iq1_m → IQ1_M, gyro/gyro-s → Gyro-S, IQ3_S unchanged, an unknown name passed through, and a failing resolver passed through.

@Niko1221

Copy link
Copy Markdown
Owner

Thank you. Your commits are in 0.2.8 with you as the author. Closing this PR because it is now in main.

@Niko1221 Niko1221 closed this Oct 29, 2026
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