ai

package
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 13, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package ai contains the AI SRE analyzer that turns Unknown / Spike AgentResults into structured AIFindings. The implementation is a plain net/http client against any OpenAI-compatible /chat/completions endpoint (vanilla OpenAI, Azure OpenAI, vLLM, LM Studio, Ollama, OpenRouter — anything that speaks the same JSON shape).

The system prompt is split across the prompts/ subdirectory in the OpenClaw / Versus DevOps Guidelines multi-file style — one file per concern (SOUL, INPUTS, OUTPUT, RULES). Each file is embedded at build time via go:embed and concatenated in a fixed order to form the `system` message. Operators tune the prompt by editing the Markdown files and rebuilding — no Go changes required.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildPrompt

func BuildPrompt(r core.AgentResult, source, service string, samples []string) (system, user string)

BuildPrompt builds the (system, user) messages for a single AgentResult. Pure function: no I/O, no time. Easy to golden-test.

func ParseFinding

func ParseFinding(raw string) (*core.AIFinding, error)

ParseFinding accepts the model's reply and returns an AIFinding. Tolerates the most common deviations: leading/trailing prose, ```json fences, prepended "Here is the JSON:" preamble.

func SystemPrompt

func SystemPrompt() string

SystemPrompt returns the assembled system prompt sent on every AI call. Exposed so the admin API can render it for operators.

Types

type OpenAI

type OpenAI struct {

	// SampleFn extracts the sample lines passed to the model from an
	// AgentResult. Defaults to "first up to 3 messages". Exposed so
	// the worker can swap in a redaction-aware extractor without the
	// AI package depending on pkg/agent.
	SampleFn func(core.AgentResult) []string
	// contains filtered or unexported fields
}

OpenAI is an AISRE backed by OpenAI's /chat/completions endpoint. One call per AgentResult; no streaming, no tool use.

func NewOpenAI

func NewOpenAI(cfg config.AgentAIConfig, client *http.Client) *OpenAI

NewOpenAI constructs the analyzer. httpClient may be nil — a sane default (30s timeout, no proxy) is used.

func (*OpenAI) Analyze

func (o *OpenAI) Analyze(ctx context.Context, r core.AgentResult) (*core.AICallResult, error)

Analyze sends the result to the configured chat endpoint and parses the structured AIFinding from the response. The returned AICallResult also carries the user prompt sent, the raw model response, the wall-clock duration, and the model id — used by the detect log to render an audit trail in the UI.

func (*OpenAI) Name

func (o *OpenAI) Name() string

Name implements core.AISRE.

type RateLimiter

type RateLimiter struct {
	// contains filtered or unexported fields
}

RateLimiter caps the number of AI calls per rolling hour. Pure in-memory: a process restart resets the window, which is acceptable because the goal is to bound cost per running process — not to enforce a quota across a fleet (we run one agent worker per replica).

max <= 0 disables the limit (Allow always returns true).

func NewRateLimiter

func NewRateLimiter(maxPerHour int) *RateLimiter

NewRateLimiter constructs a limiter with the configured per-hour cap.

func (*RateLimiter) Allow

func (r *RateLimiter) Allow() bool

Allow returns true and records a use when the caller is under the per-hour cap, false otherwise. Thread-safe.

func (*RateLimiter) Stats

func (r *RateLimiter) Stats() (bucket string, used, max int)

Stats returns the current hour bucket and how many calls have been allowed in it. Used by the admin status endpoint.

type ResultCache

type ResultCache struct {
	// contains filtered or unexported fields
}

ResultCache memoises AI findings by pattern_id so a recurring pattern inside one cache window does not pay for a repeat LLM call.

State is held in-memory and (best effort) mirrored to storage.Provider under config.AICacheBlobName so it survives restarts. A storage write failure logs but does not block the caller — the cache still works for the lifetime of the process.

func NewResultCache

func NewResultCache(ttl time.Duration, store storage.Provider) *ResultCache

NewResultCache builds a cache with the configured TTL. ttl <= 0 disables caching (Get always misses, Put is a no-op).

store may be nil — in that case the cache is in-memory only.

func (*ResultCache) Flush

func (c *ResultCache) Flush()

Flush wipes every entry and persists. Used by the admin endpoint.

func (*ResultCache) Get

func (c *ResultCache) Get(patternID string) (*core.AIFinding, bool)

Get returns a cached finding when one exists and has not expired.

func (*ResultCache) Len

func (c *ResultCache) Len() int

Len returns the number of live entries (including expired-but-not-evicted).

func (*ResultCache) Persist

func (c *ResultCache) Persist() error

Persist writes the cache to storage. No-op when store is nil or the cache hasn't changed since the last persist.

func (*ResultCache) Put

func (c *ResultCache) Put(patternID string, f *core.AIFinding)

Put inserts or replaces a finding for patternID. Safe to call with a nil receiver or zero-TTL cache (becomes a no-op).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL