package pluginapi // Action is what a hook asks GoModel to do with the request or response. type Action string const ( // ActionAllow continues, with any edits already applied to the Exchange. ActionAllow Action = "allow" // ActionBlock rejects the request with Decision.Status, Code, and Message // rendered in the endpoint's native error dialect. ActionBlock Action = "block" // ActionRespond short-circuits: Decision.Response is sent to the client as // the completion with HTTP 200 (as a single-chunk stream when streaming). ActionRespond Action = "respond" // ActionWarn continues and records Decision.Detail in the audit trail and // the X-GoModel-Guardrail response headers. ActionWarn Action = "warn" ) // Decision is the result of a synchronous hook. type Decision struct { // Action selects what happens next. The zero value is treated as // [ActionAllow]. Action Action // Status is the HTTP status for [ActionBlock], 400 to 599. Zero, or a // value outside that range, means the phase default: 400 in request // phases, 502 in response phases. Status int // Code is a machine-readable reason such as "content_policy". Code string // Message is the human-readable reason sent to the client on block. Message string // Response is the synthetic completion for [ActionRespond]. Response *Completion // Detail is a JSON-serializable summary stored in the audit trail. It // must not contain secrets. Detail any // NoStore asks GoModel not to store the response of this request in the // response cache (exact or semantic), so a later request with the same // or a similar body runs the plugins again instead of replaying it. Set // it when the reply carries request-specific data a plugin puts back on // the way out, such as de-anonymized PII. It is honoured with any // Action, from any phase, and from any instance of the request. NoStore bool } // Allow returns a Decision that lets the exchange continue. func Allow() Decision { return Decision{Action: ActionAllow} } // Block returns a Decision that rejects the exchange with the given HTTP // status (0 for the phase default), machine-readable code, and message. func Block(status int, code, message string) Decision { return Decision{Action: ActionBlock, Status: status, Code: code, Message: message} } // Respond returns a Decision that answers the request with a one-choice // assistant completion containing text, keeping agent loops alive instead of // surfacing an error. func Respond(text string) Decision { return Decision{ Action: ActionRespond, Response: &Completion{ Choices: []Choice{{ Index: 0, Message: TextMessage(RoleAssistant, text), FinishReason: "stop", }}, }, } } // Warn returns a Decision that lets the exchange continue while recording // code, message, and detail in the audit trail and response headers. func Warn(code, message string, detail any) Decision { return Decision{Action: ActionWarn, Code: code, Message: message, Detail: detail} } // Blocks reports whether the decision stops the exchange from reaching the // provider or the client as-is: [ActionBlock] or [ActionRespond]. func (d Decision) Blocks() bool { return d.Action == ActionBlock || d.Action == ActionRespond }