100 lines
4.2 KiB
Go
100 lines
4.2 KiB
Go
// Package diag holds the strict diagnosis schema of experiment design 6.3 and the enum sources.
|
|
package diag
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Diagnosis is the submit_diagnosis payload (design 6.3). Every field is required under strict mode.
|
|
type Diagnosis struct {
|
|
RootCauseLabel string `json:"root_cause_label"`
|
|
Confidence float64 `json:"confidence"`
|
|
Evidence []string `json:"evidence"`
|
|
ProposedAction string `json:"proposed_action"`
|
|
Target string `json:"target"`
|
|
Rationale string `json:"rationale"`
|
|
}
|
|
|
|
// DefaultLabels mirrors the ground_truth_label values of the 13 scenario files (design 7); the agent refreshes the
|
|
// list from the harness GET /scenarios at start-up and falls back to this copy.
|
|
var DefaultLabels = []string{
|
|
"api_pod_failure", "api_pod_killed", "db_primary_down", "gitops_drift", "legacy_instance_lost", "legacy_process_killed",
|
|
"memory_pressure_api", "network_latency_api_to_db", "network_latency_legacy", "replication_paused", "schema_mismatch",
|
|
"transient_loss_legacy", "transient_partition",
|
|
}
|
|
|
|
// DefaultActions mirrors the layer-2 action_ids of design 2.3; refreshed from the harness GET /actions/catalog (layer contains "2").
|
|
var DefaultActions = []string{
|
|
"k8s.rollout_restart", "k8s.rollout_undo", "k8s.scale", "k8s.node_drain", "k8s.node_uncordon",
|
|
"gce.instance_reset", "mig.recreate_instance", "mig.resize", "db.replay_resume", "net.route_change",
|
|
}
|
|
|
|
// Enums are the closed sets the schema is built from.
|
|
type Enums struct {
|
|
Labels []string // + "unknown"
|
|
Actions []string // + "none", "escalate"
|
|
}
|
|
|
|
func (e Enums) LabelEnum() []string { return append(append([]string{}, e.Labels...), "unknown") }
|
|
func (e Enums) ActionEnum() []string {
|
|
return append(append([]string{}, e.Actions...), "none", "escalate")
|
|
}
|
|
|
|
// Schema is the JSON schema of the submit_diagnosis tool parameters (strict: additionalProperties false, all required).
|
|
func (e Enums) Schema() map[string]any {
|
|
return map[string]any{
|
|
"type": "object",
|
|
"properties": map[string]any{
|
|
"root_cause_label": map[string]any{"type": "string", "enum": e.LabelEnum(), "description": "Root cause label; unknown when the evidence does not support any label."},
|
|
"confidence": map[string]any{"type": "number", "description": "0.0 to 1.0. Below 0.5 the harness routes the case to layer 3 (human)."},
|
|
"evidence": map[string]any{"type": "array", "items": map[string]any{"type": "string"}, "description": "Quotations of tool results that support the label. At least one."},
|
|
"proposed_action": map[string]any{"type": "string", "enum": e.ActionEnum(), "description": "Catalogue action_id (layer 2), none when no action is needed, escalate when a human must decide."},
|
|
"target": map[string]any{"type": "string", "description": "Action target: deployment name, instance name, legacy:N, or empty."},
|
|
"rationale": map[string]any{"type": "string", "description": "Why this label and action; at most 200 characters."},
|
|
},
|
|
"required": []string{"root_cause_label", "confidence", "evidence", "proposed_action", "target", "rationale"},
|
|
"additionalProperties": false,
|
|
}
|
|
}
|
|
|
|
// Validate enforces the schema constraints on the model output (strict mode guarantees shape; this checks the values).
|
|
func (e Enums) Validate(d Diagnosis) error {
|
|
if !contains(e.LabelEnum(), d.RootCauseLabel) {
|
|
return fmt.Errorf("root_cause_label %q not in enum", d.RootCauseLabel)
|
|
}
|
|
if d.Confidence < 0 || d.Confidence > 1 {
|
|
return fmt.Errorf("confidence %v out of [0,1]", d.Confidence)
|
|
}
|
|
if len(d.Evidence) == 0 {
|
|
return fmt.Errorf("evidence must have at least one item")
|
|
}
|
|
if !contains(e.ActionEnum(), d.ProposedAction) {
|
|
return fmt.Errorf("proposed_action %q not in enum", d.ProposedAction)
|
|
}
|
|
if n := len([]rune(d.Rationale)); n > 200 {
|
|
return fmt.Errorf("rationale is %d characters, limit 200", n)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func Parse(args string, e Enums) (Diagnosis, error) {
|
|
var d Diagnosis
|
|
dec := json.NewDecoder(strings.NewReader(args))
|
|
dec.DisallowUnknownFields()
|
|
if err := dec.Decode(&d); err != nil {
|
|
return d, fmt.Errorf("submit_diagnosis arguments: %w", err)
|
|
}
|
|
return d, e.Validate(d)
|
|
}
|
|
|
|
func contains(xs []string, s string) bool {
|
|
for _, x := range xs {
|
|
if x == s {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|