forked from WHC/Hermit
v0.8.10: settings dialog (provider, model, key), window starts on a provider
The window could be started, but it could not be pointed anywhere: a provider,
a model and a key only came in on the command line. The gear in the header now
opens a dialog that changes all three on the engine's live config.
AgentCore (app/src/core/AgentCore.*):
- providers(): the engine's preset table (cst_provider_count/at), with base
URL, the remembered or preset model, whether a key exists and where it comes
from (cst_creds_resolve: "saved", or the environment variable), the preset's
key variable and the source ("built-in" / "user"). A key is read into a local
buffer only to answer "is there one"; it never leaves the C++ side.
- modelChoices(provider): the provider's own list (cst_provider_models), the
fetched cache (cst_ps_cached -- which writes through its out-parameters, so
it takes a real int, not NULL: passing one cost a segfault) and the
remembered pick. No network. modelInfo() is cst_model_info + its "200K ·
text only" label; usage() is cst_agent_usage for the session and the turn.
- useProvider(provider, model, key) -> "" or a reason: a different provider
goes through cst_ps_switch_provider() (the key is verified against /models
before it is saved; offline it is kept unverified and the header says so),
a key for the current one through cst_ps_set_key(), and the model is
remembered in settings.json. syncConfig() takes the engine's config back
over the app's copy, so provider/model/approval read correctly afterwards.
The engine's session is never touched by a switch.
- open() no longer starts with an empty provider: it takes the one the engine
used last (settings.json), else the built-in default, plus that provider's
base URL and model. Without this the first turn could not be sent at all and
no entry in the dialog was "in use".
QML (app/qml/SettingsDialog.qml, Main.qml):
- a modal dialog with the themed surface the other dialogs use (the Basic
style's light panel washes out the dark theme), the provider list with its
key state, a model field with the info label and clickable candidates, a
password field for a new key, the session's token counters, and a note that
reports what the last "Use this" answered.
Tests: 8 more checks -- the presets are readable, an unknown provider is
refused, the counters are readable, the gear opens the panel, the rows exist, a
model typed into the field reaches the engine, the note says so, Close puts the
dialog away. 36 checks pass. --uitest also points XDG_CONFIG_HOME at its
scratch directory now, so a run cannot rewrite the user's settings.json or
credentials, and CST_APP_SHOT writes shot.png at the end plus
shot-settings.png while the dialog is open (that is how the dialog above was
reviewed on a machine without a screen).
Verified: 36/36 UI checks, --selftest, ctest cst-app-selftest, make check rc=0,
and a screenshot of the dialog.
Version 0.8.10.
This commit is contained in:
+54
-9
@@ -135,6 +135,24 @@ QString writeFixtureSession(const QString &ws, const QString &id) {
|
||||
return id;
|
||||
}
|
||||
|
||||
/* A picture of an interesting state on request: the only way to look at the
|
||||
* layout on a machine without a screen (CST_APP_SHOT=shot.png makes the run
|
||||
* write shot.png at the end and shot-settings.png while the settings dialog
|
||||
* is open). */
|
||||
void maybeShot(QQuickWindow *win, const QString &suffix) {
|
||||
if (!qEnvironmentVariableIsSet("CST_APP_SHOT"))
|
||||
return;
|
||||
const QString base = qEnvironmentVariable("CST_APP_SHOT");
|
||||
const QString path =
|
||||
suffix.isEmpty() ? base
|
||||
: base.endsWith(QLatin1String(".png"))
|
||||
? base.left(base.size() - 4) + suffix + QStringLiteral(".png")
|
||||
: base + suffix + QStringLiteral(".png");
|
||||
std::printf("%s %s\n",
|
||||
win->grabWindow().save(path) ? "note wrote" : "note cannot write",
|
||||
qPrintable(path));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int runUiTest(AgentCore &core, const QString &size) {
|
||||
@@ -278,6 +296,40 @@ int runUiTest(AgentCore &core, const QString &size) {
|
||||
check(core.sessionId() == fixture && list && list->property("count").toInt() == 5,
|
||||
QStringLiteral("switching back replays it again"));
|
||||
|
||||
/* --- provider, model, key: the settings dialog ------------------------ */
|
||||
check(core.providers().size() > 0 &&
|
||||
!core.providers().first().toMap().value(QStringLiteral("name"))
|
||||
.toString()
|
||||
.isEmpty(),
|
||||
QStringLiteral("the provider presets are readable"));
|
||||
check(core.useProvider(QStringLiteral("no-such-provider"), QString(), QString())
|
||||
.contains(QLatin1String("no such provider")),
|
||||
QStringLiteral("switching to an unknown provider is refused"));
|
||||
check(core.usage().contains(QStringLiteral("requests")),
|
||||
QStringLiteral("the token counters are readable"));
|
||||
|
||||
clickMouse(win, QStringLiteral("settingsButton"));
|
||||
QQuickItem *panel = item(win, QStringLiteral("settingsPanel"));
|
||||
check(panel && panel->isVisible(), QStringLiteral("the gear opens the settings dialog"));
|
||||
check(countNamed(win->contentItem(), QStringLiteral("providerRow:")) > 0,
|
||||
QStringLiteral("the dialog lists the providers"));
|
||||
maybeShot(win, QStringLiteral("-settings"));
|
||||
QQuickItem *mfield = item(win, QStringLiteral("settingsModel"));
|
||||
if (mfield && panel) {
|
||||
mfield->setProperty("text", QStringLiteral("cst-test-model"));
|
||||
clickMouse(win, QStringLiteral("settingsApply"));
|
||||
check(core.model() == QLatin1String("cst-test-model"),
|
||||
QStringLiteral("the model picked in the dialog reaches the engine"));
|
||||
QQuickItem *note = item(win, QStringLiteral("settingsNote"));
|
||||
check(note && note->property("text").toString().contains(
|
||||
QLatin1String("cst-test-model")),
|
||||
QStringLiteral("the dialog says what it is using now"));
|
||||
clickMouse(win, QStringLiteral("settingsClose"));
|
||||
check(!panel->isVisible(), QStringLiteral("Close puts the dialog away"));
|
||||
} else {
|
||||
check(false, QStringLiteral("the dialog has a model field"));
|
||||
}
|
||||
|
||||
/* --- a narrow window: dock -> drawer, and the button opens it --- */
|
||||
resizeTo(win, 460, 900);
|
||||
check(docked && !docked->isVisible(),
|
||||
@@ -295,15 +347,8 @@ int runUiTest(AgentCore &core, const QString &size) {
|
||||
check(docked && docked->isVisible(),
|
||||
QStringLiteral("widening again docks the sidebar back"));
|
||||
|
||||
/* A picture of the last state on request: the only way to look at the
|
||||
* layout on a machine without a screen
|
||||
* (CST_APP_SHOT=shot.png cst-agent-app --uitest). */
|
||||
if (qEnvironmentVariableIsSet("CST_APP_SHOT")) {
|
||||
const QString shot = qEnvironmentVariable("CST_APP_SHOT");
|
||||
std::printf("%s %s\n",
|
||||
win->grabWindow().save(shot) ? "note wrote" : "note cannot write",
|
||||
qPrintable(shot));
|
||||
}
|
||||
/* A picture of the last state on request (see maybeShot). */
|
||||
maybeShot(win, QString());
|
||||
|
||||
std::printf("--- %s\n", g_fail ? "FAILED" : "all UI checks passed");
|
||||
return g_fail ? 1 : 0;
|
||||
|
||||
Reference in New Issue
Block a user