feat(studio): system-ai editor — test-without-save + model picker (v0.13.0)
Three operator-facing improvements driven by Stefan's testing:
* **Test Connection** runs against the form's *current* values
and no longer requires saving first. Previously we save-then-
test which caused a "system AI is disabled" message when the
form's model field was still empty. Now the test prompt
travels with the request body so unsaved tweaks can be
validated.
* **Model picker** replaces the free-text Model field. A
Refresh button calls HubAdmin.ListSystemAiModels (provider's
`/v1/models`); installed models show up as clickable chips
that fill the field with one tap. The text input stays for
cases where the operator already knows the id.
* **Pull** button (Ollama only) downloads a model via
`/api/pull`. Type `gemma3:4b`, click Pull, spinner, then the
new model lights up in the chip list. No more "switch to a
terminal to ollama-pull" detour.
Helper text updates for the empty-model case ("Required: pick
from the list (Refresh) or type one. Use Pull to download from
Ollama.") so a fresh operator never wonders why Save / Test
errors out.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
88c5f2da1f
commit
5b6641567c
4 changed files with 266 additions and 23 deletions
|
|
@ -163,11 +163,24 @@ class HubService {
|
|||
);
|
||||
}
|
||||
|
||||
/// Probe the configured provider with a tiny ping. Same
|
||||
/// error-kind taxonomy as [askAi]; "Test connection" buttons
|
||||
/// use this to surface "is it working?".
|
||||
Future<AskAiResult> testSystemAi() async {
|
||||
final r = await _client.testSystemAi();
|
||||
/// Probe a System-AI configuration with a tiny ping. Pass
|
||||
/// the editor's current form values so "Test connection"
|
||||
/// works *before* save. Empty fields fall back to the live
|
||||
/// in-memory config.
|
||||
Future<AskAiResult> testSystemAi({
|
||||
String provider = '',
|
||||
String endpoint = '',
|
||||
String model = '',
|
||||
String apiKeyEnv = '',
|
||||
String privacyMode = '',
|
||||
}) async {
|
||||
final r = await _client.testSystemAi(
|
||||
provider: provider,
|
||||
endpoint: endpoint,
|
||||
model: model,
|
||||
apiKeyEnv: apiKeyEnv,
|
||||
privacyMode: privacyMode,
|
||||
);
|
||||
return AskAiResult(
|
||||
errorKind: r.errorKind,
|
||||
text: r.text,
|
||||
|
|
@ -175,6 +188,48 @@ class HubService {
|
|||
);
|
||||
}
|
||||
|
||||
/// List the models the provider exposes via `GET /v1/models`.
|
||||
/// Studio's editor uses this to populate a dropdown instead
|
||||
/// of forcing the operator to type the id manually.
|
||||
Future<({String errorKind, String text, List<String> ids})>
|
||||
listSystemAiModels({
|
||||
String provider = '',
|
||||
String endpoint = '',
|
||||
String apiKeyEnv = '',
|
||||
}) async {
|
||||
final r = await _client.listSystemAiModels(
|
||||
provider: provider,
|
||||
endpoint: endpoint,
|
||||
apiKeyEnv: apiKeyEnv,
|
||||
);
|
||||
return (
|
||||
errorKind: r.errorKind,
|
||||
text: r.text,
|
||||
ids: r.models.map((m) => m.id).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Pull (download) an Ollama model via /api/pull.
|
||||
/// Synchronous; can take minutes for large models. Errors
|
||||
/// come back as `errorKind`.
|
||||
Future<({String errorKind, String text, int elapsedMs})>
|
||||
pullSystemAiModel({
|
||||
required String endpoint,
|
||||
required String model,
|
||||
String apiKeyEnv = '',
|
||||
}) async {
|
||||
final r = await _client.pullSystemAiModel(
|
||||
endpoint: endpoint,
|
||||
model: model,
|
||||
apiKeyEnv: apiKeyEnv,
|
||||
);
|
||||
return (
|
||||
errorKind: r.errorKind,
|
||||
text: r.text,
|
||||
elapsedMs: r.elapsedMs,
|
||||
);
|
||||
}
|
||||
|
||||
/// Active channel + per-channel daemon status snapshot.
|
||||
Future<ChannelStatusSnapshot> channelStatus() async {
|
||||
final r = await _client.channelStatus();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue