feat(studio): "Explain this" + System AI in Settings (v0.11.0)
Two new operator affordances powered by `HubAdmin.AskAi`:
* **Audit drill-down → "Explain" button.** Tappable on every
failed event when System AI is configured; greyed out with
a "Configure in Settings" tooltip otherwise. Shows an
inline panel under the event fields with the LLM's plain-
language explanation, a "FIX" suggestion, the privacy-mode
badge (`redacted` / `full`), and round-trip latency. Errors
map to actionable fix hints (`env_missing` → "set $VAR in
your shell, then fai daemon restart"; `network` → "is your
provider running?"; etc.) — no raw HTTP errors surfaced.
* **Settings dialog → System AI panel.** Renders the live
HubAdmin.SystemAiStatus (provider, endpoint, model,
api_key_env name, privacy mode). When disabled, shows a
one-line explainer + pointer to docs/architecture/system-ai.md.
Read-only for now; editing the config still requires
~/.fai/config.yaml (Phase-1+ adds in-place editing).
Both surfaces follow the new zero-learning-curve UX rule:
the user never has to look up what the configurable means or
how to fix an error — every state self-explains in place.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
060c8003d5
commit
4c4e2bb1eb
6 changed files with 415 additions and 4 deletions
|
|
@ -106,6 +106,35 @@ class HubService {
|
|||
);
|
||||
}
|
||||
|
||||
/// System-AI status snapshot — used by the Settings dialog
|
||||
/// and the audit drill-down to decide whether the "Explain"
|
||||
/// affordance is active. Always returns a struct (no
|
||||
/// exceptions on disabled state).
|
||||
Future<SystemAiStatus> systemAiStatus() async {
|
||||
final r = await _client.systemAiStatus();
|
||||
return SystemAiStatus(
|
||||
enabled: r.enabled,
|
||||
provider: r.provider,
|
||||
endpoint: r.endpoint,
|
||||
model: r.model,
|
||||
privacyMode: r.privacyMode,
|
||||
apiKeyEnv: r.apiKeyEnv,
|
||||
);
|
||||
}
|
||||
|
||||
/// Send a one-shot prompt to the System AI. Returns the
|
||||
/// answer + latency on success or an [AskAiResult] with
|
||||
/// `errorKind` set on failure (never throws). Studio maps
|
||||
/// the error kind to its inline-fix copy.
|
||||
Future<AskAiResult> askAi(String prompt) async {
|
||||
final r = await _client.askAi(prompt);
|
||||
return AskAiResult(
|
||||
errorKind: r.errorKind,
|
||||
text: r.text,
|
||||
latencyMs: r.latencyMs,
|
||||
);
|
||||
}
|
||||
|
||||
/// Active channel + per-channel daemon status snapshot.
|
||||
Future<ChannelStatusSnapshot> channelStatus() async {
|
||||
final r = await _client.channelStatus();
|
||||
|
|
@ -498,6 +527,67 @@ class PendingApproval {
|
|||
});
|
||||
}
|
||||
|
||||
class SystemAiStatus {
|
||||
final bool enabled;
|
||||
final String provider;
|
||||
final String endpoint;
|
||||
final String model;
|
||||
/// off / redacted / full.
|
||||
final String privacyMode;
|
||||
final String apiKeyEnv;
|
||||
|
||||
const SystemAiStatus({
|
||||
required this.enabled,
|
||||
required this.provider,
|
||||
required this.endpoint,
|
||||
required this.model,
|
||||
required this.privacyMode,
|
||||
required this.apiKeyEnv,
|
||||
});
|
||||
}
|
||||
|
||||
class AskAiResult {
|
||||
/// "" on success; otherwise one of:
|
||||
/// disabled / env_missing / network / http / parse / empty_response.
|
||||
final String errorKind;
|
||||
/// On success: the assistant's reply.
|
||||
/// On failure: a human-readable diagnostic.
|
||||
final String text;
|
||||
/// Round-trip latency in ms (success only).
|
||||
final int latencyMs;
|
||||
|
||||
const AskAiResult({
|
||||
required this.errorKind,
|
||||
required this.text,
|
||||
required this.latencyMs,
|
||||
});
|
||||
|
||||
bool get isSuccess => errorKind.isEmpty;
|
||||
|
||||
/// One-line fix hint per error kind, mirroring
|
||||
/// `docs/architecture/system-ai.md`. Empty on success.
|
||||
String get fixHint {
|
||||
switch (errorKind) {
|
||||
case '':
|
||||
return '';
|
||||
case 'disabled':
|
||||
return 'Configure System AI in Settings (Cmd+,) → System AI panel.';
|
||||
case 'env_missing':
|
||||
return 'The API-key env var is empty. `export <var>=<key>` in your shell, then `fai daemon restart`.';
|
||||
case 'network':
|
||||
return 'Hub cannot reach the configured endpoint. Is your provider running?';
|
||||
case 'http':
|
||||
return 'Provider returned a non-2xx status. Verify endpoint, model name, and API key.';
|
||||
case 'parse':
|
||||
return 'Provider returned an unexpected response shape. Non-OpenAI providers may need a compatibility proxy.';
|
||||
case 'empty_response':
|
||||
return 'Provider answered with no content. Try a different model or rephrase the prompt.';
|
||||
default:
|
||||
return 'Unknown failure kind ($errorKind). See system-ai.md.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelInfo {
|
||||
final String name;
|
||||
final int port;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue