llm

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 14, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package llm talks to an OpenAI-compatible chat completions endpoint, and fences the untrusted text Hecate would put in a prompt.

Everything Hecate knows about a stuck promotion is attacker-influenced: Flux condition messages, git commit messages, pull request titles, image tags, a step's captured output. Any of it can contain text written to be read as an instruction. The fencing here is not a hardening pass to be done later — it is the reason this package exists rather than callers assembling prompts themselves.

One client, no provider interface. Ollama, llama.cpp, vLLM, LM Studio and the hosted vendors all speak /v1/chat/completions, so "pluggable" is a base URL, a model name and an optional key. If something genuinely does not fit, that is the day to write a second implementation.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Diagnose

func Diagnose(ctx context.Context, c *Client, ex *ops.Explanation) (string, error)

Diagnose asks a model to explain, in prose, a Gate that Hecate has already analysed.

It is given the finished Explanation rather than the raw resources, and that is the important part: the deterministic analysis has already decided what is blocking and what would fix it, so the model's job is to say it readably. It is not being asked what is wrong, because a model that concluded something different from the code would be reporting a second opinion nobody asked for.

The Explanation's own fields are Hecate's, but the strings inside them are not: a step message carries whatever a git host or a Flux controller said, so the whole thing goes inside the fence.

Types

type Client

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

Client is an OpenAI-compatible chat completions client.

func New

func New(cfg Config) (*Client, error)

New returns a client, or an error if the configuration cannot work.

func (*Client) Complete

func (c *Client) Complete(ctx context.Context, p Prompt) (string, error)

Complete sends one prompt and returns the model's reply.

func (*Client) Model

func (c *Client) Model() string

Model is the model this client will ask, for reporting.

type Config

type Config struct {
	// BaseURL is the API root — http://localhost:11434/v1 for Ollama,
	// https://api.openai.com/v1 for OpenAI. The /chat/completions path is
	// appended.
	BaseURL string
	// Model is the model name the endpoint knows.
	Model string
	// APIKey is optional: local runtimes do not want one.
	APIKey string
	// Timeout bounds a single completion. Zero uses 60s, which is generous
	// because a local model on CPU is slow and a diagnosis is not on anyone's
	// critical path.
	Timeout time.Duration
}

Config is what it takes to reach a model.

func FromEnv

func FromEnv() Config

FromEnv reads the standard variables, so Hecate drops into a setup that already has a model configured.

HECATE_LLM_* wins over OPENAI_* so a machine can point Hecate at a local model without disturbing whatever else uses the OpenAI variables.

func (Config) Configured

func (c Config) Configured() bool

Configured reports whether there is enough to talk to a model.

Hecate must work with no model at all: diagnosis is an assist, never a dependency, so every caller asks this rather than treating its absence as an error.

type Fence

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

Fence wraps untrusted content in delimiters the content cannot forge.

The delimiter carries a per-call token, and any occurrence of that token is stripped from the content first. A fixed delimiter is guessable: text that contained the closing marker would end the fence early and the rest would be read as trusted prompt, which is the whole attack.

func NewFence

func NewFence(token string) *Fence

NewFence returns a fence whose delimiters are unguessable for this prompt.

The token is derived from the caller rather than randomly, so a prompt is reproducible for a given input — which matters for testing, and for anyone trying to work out what the model was actually shown.

func (*Fence) Wrap

func (f *Fence) Wrap(label, content string) string

Wrap renders one labelled block of untrusted content.

Returns an empty string once the total budget is spent: silently dropping later fields is better than truncating in the middle of one, because a half-shown value invites the model to guess at the rest.

type Prompt

type Prompt struct {
	// Task is the trusted instruction: what to do with the data.
	Task string
	// Blocks are already-fenced untrusted sections, from Fence.Wrap.
	Blocks []string
}

Prompt assembles a system and user message from trusted instructions and fenced untrusted data.

func (Prompt) System

func (p Prompt) System() string

System is the message that carries the instructions and the warning.

func (Prompt) User

func (p Prompt) User() string

User is the message that carries the task and the fenced data.

Jump to

Keyboard shortcuts

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