record

package
v0.1.0 Latest Latest
Warning

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

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

Documentation

Overview

Package record is the record/replay cassette layer.

Every outbound call — search, fetch, academic, LLM — goes through this package. On first run it records request/response pairs to disk; thereafter it replays them. Three consequences, all of which the project needs before the first actor is written:

  • Orchestrator and eval tests are deterministic.
  • They run in CI without network access.
  • They cost nothing, so the eval harness can run on every commit.

Building this after the actors exist means auditing every call site to thread a transport through, which is why it belongs in M0 rather than M2.

Index

Constants

View Source
const (
	EnvMode = "MOLE_RECORD"
	EnvDir  = "MOLE_CASSETTE_DIR"
)

EnvMode and EnvDir configure recording.

Variables

This section is empty.

Functions

func RequestKey

func RequestKey(method string, u *url.URL, body []byte) string

RequestKey derives the cassette key. It deliberately excludes headers: matching on them would make every recording depend on the exact SDK version, user-agent, and auth scheme in use when it was made.

func Slug

func Slug(name string) string

Slug turns a run name into a stable, readable filename.

Readability is the point: a cassette is reviewed in a pull request, and "what-is-the-consensus-on-byte-level-llms.json" is reviewable in a way that a hash is not. Same question in, same file out, so a re-record replaces the recording it supersedes rather than accumulating beside it.

Types

type Cassette

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

Cassette is a set of interactions backed by one JSON file.

func LoadCassette

func LoadCassette(path string) (*Cassette, error)

LoadCassette reads a cassette, returning an empty one if the file is absent.

func (*Cassette) Len

func (c *Cassette) Len() int

Len reports how many interactions are stored.

func (*Cassette) Save

func (c *Cassette) Save() error

Save writes the cassette if anything changed. Interactions are sorted by key so the file has a stable diff.

type ErrCassetteMiss

type ErrCassetteMiss struct {
	Key     string
	Summary string
}

ErrCassetteMiss is returned in ModeReplay when no interaction matches.

func (*ErrCassetteMiss) Error

func (e *ErrCassetteMiss) Error() string

type Interaction

type Interaction struct {
	Key      string       `json:"key"`
	Request  ReqSnapshot  `json:"request"`
	Response RespSnapshot `json:"response"`
}

Interaction is one recorded request/response pair.

type Mode

type Mode string
const (
	// ModeOff passes everything through untouched.
	ModeOff Mode = "off"
	// ModeRecord always hits the network and overwrites stored interactions.
	ModeRecord Mode = "record"
	// ModeReplay never hits the network; a miss is an error.
	ModeReplay Mode = "replay"
	// ModeAuto replays what exists and records what does not. Convenient
	// locally, but do not use it in CI: a cassette miss would silently make a
	// real network call and a real charge.
	ModeAuto Mode = "auto"
)

func ModeFromEnv

func ModeFromEnv() (Mode, error)

ModeFromEnv reports the configured mode without opening anything.

Separate from FromEnv because some checks have to happen BEFORE a cassette is touched. Refusing a worker pool is one: in replay, FromEnv fails on a missing cassette first, so a guard placed after it never runs and the caller is told the recording is absent rather than that the request was never allowed.

func ParseMode

func ParseMode(s string) (Mode, error)

ParseMode validates a mode string. An empty value is ModeOff.

type Recorder

type Recorder struct {
	Mode     Mode
	Cassette *Cassette
	Path     string
	// contains filtered or unexported fields
}

Recorder is a cassette plus the client that reads and writes it.

func FromEnv

func FromEnv(name string) (*Recorder, error)

FromEnv builds a Recorder for a named run from MOLE_RECORD and MOLE_CASSETTE_DIR. With recording off it returns a Recorder that passes everything through, so callers need no conditional.

func Open

func Open(mode Mode, dir, name string) (*Recorder, error)

Open loads (or creates) the cassette for a named run.

func (*Recorder) Client

func (r *Recorder) Client() *http.Client

Client returns the HTTP client every provider should use.

func (*Recorder) Close

func (r *Recorder) Close() error

Close persists the cassette if anything changed.

func (*Recorder) Enabled

func (r *Recorder) Enabled() bool

Enabled reports whether anything is being recorded or replayed.

func (*Recorder) Wrap

func (r *Recorder) Wrap(base http.RoundTripper) http.RoundTripper

Wrap adapts a transport that a caller has already built.

The fetcher is the one provider that cannot simply be handed a client: its transport carries the egress guard's DialContext, and replacing it would bypass SSRF protection. Wrapping instead keeps the guard underneath, so a recording still goes through it and a replay never reaches a socket at all.

type ReqSnapshot

type ReqSnapshot struct {
	Method  string              `json:"method"`
	URL     string              `json:"url"`
	Headers map[string][]string `json:"headers,omitempty"`
	Body    string              `json:"body,omitempty"`
}

type RespSnapshot

type RespSnapshot struct {
	Status  int                 `json:"status"`
	Headers map[string][]string `json:"headers,omitempty"`
	Body    string              `json:"body,omitempty"`
}

type Transport

type Transport struct {
	Mode     Mode
	Cassette *Cassette
	Base     http.RoundTripper
}

Transport is an http.RoundTripper that records or replays. One transport covers search, fetch, academic providers, and any LLM SDK that accepts a custom *http.Client — which is most of the outbound surface.

func NewTransport

func NewTransport(mode Mode, cassette *Cassette, base http.RoundTripper) *Transport

NewTransport wraps base. A nil base uses http.DefaultTransport.

func (*Transport) Client

func (t *Transport) Client() *http.Client

Client returns an *http.Client using this transport.

func (*Transport) RoundTrip

func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error)

Jump to

Keyboard shortcuts

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