Help us shape the future of Go! Take the 2026 Go Developer Survey

livetest

package
v1.14.0 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2026 License: Apache-2.0 Imports: 27 Imported by: 0

Documentation

Overview

Package livetest is the shared live checklist every model plugin runs against its provider's real API.

The checklist cascades. This package is the first tier: the generate and agent journeys every model plugin must pass, plus RunEmbedder for plugins that serve embedders. A plugin family (the OpenAI-compatible plugins, the Anthropic plugins, the Google GenAI backends) wraps Suite in a suite of its own whose Run adds the family's gaps to Suite.Skip and its checks as a Group, and calls Run. A plugin's live test calls its family's runner, or Run directly, and keeps only the checks that are its alone as plain subtests.

Running

Live tests spend money, so they run only when GENKIT_LIVE is set:

GENKIT_LIVE=1 go test -run Live ./plugins/compat_oai/openai/

GENKIT_LIVE=all also runs the expensive cases (see Expensive). Without the variable or the provider's key, a live test skips, unless the -run pattern names live tests: then it fails and names what is missing, since a run that asked for a test and quietly skipped it would read as a pass. So GENKIT_LIVE=1 go test ./plugins/... runs every suite whose key is set.

A live test's name contains "Live", as in TestPluginLive, and a -run pattern names live tests when it contains "Live" too. A unit-test pattern that happens to match a live test, such as -run TestPlugin, skips it.

Capabilities

A case that needs a capability runs when the model's registered ai.ModelSupports claims it. A claim is a promise, so a claimed capability whose case fails is a defect in the plugin or in its catalog. A known gap, in the plugin or in the provider, is recorded in Suite.Skip with its reason; a key there that names no case fails the run, so a typo cannot hide a case.

Index

Constants

This section is empty.

Variables

View Source
var RedImage = func() string {
	img := image.NewRGBA(image.Rect(0, 0, 100, 100))
	draw.Draw(img, img.Bounds(), &image.Uniform{C: color.RGBA{R: 255, A: 255}}, image.Point{}, draw.Src)
	var buf bytes.Buffer
	if err := png.Encode(&buf, img); err != nil {
		panic(err)
	}
	return "data:image/png;base64," + base64.StdEncoding.EncodeToString(buf.Bytes())
}()

RedImage is a solid red 100x100 PNG as a data URL, big enough that every provider's minimum-size check accepts it. Plugin tiers use it for their own image cases.

View Source
var SecretPDF = func() string {
	content := fmt.Sprintf("BT /F1 24 Tf 72 700 Td (The secret word is %s.) Tj ET", secretWord)
	objects := []string{
		"<< /Type /Catalog /Pages 2 0 R >>",
		"<< /Type /Pages /Kids [3 0 R] /Count 1 >>",
		"<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R /Resources << /Font << /F1 5 0 R >> >> >>",
		fmt.Sprintf("<< /Length %d >>\nstream\n%s\nendstream", len(content), content),
		"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>",
	}
	var buf bytes.Buffer
	buf.WriteString("%PDF-1.4\n")
	offsets := make([]int, len(objects))
	for i, obj := range objects {
		offsets[i] = buf.Len()
		fmt.Fprintf(&buf, "%d 0 obj\n%s\nendobj\n", i+1, obj)
	}
	xref := buf.Len()
	fmt.Fprintf(&buf, "xref\n0 %d\n0000000000 65535 f \n", len(objects)+1)
	for _, off := range offsets {
		fmt.Fprintf(&buf, "%010d 00000 n \n", off)
	}
	fmt.Fprintf(&buf, "trailer\n<< /Size %d /Root 1 0 R >>\nstartxref\n%d\n%%%%EOF\n", len(objects)+1, xref)
	return "data:application/pdf;base64," + base64.StdEncoding.EncodeToString(buf.Bytes())
}()

SecretPDF is a one-page PDF whose only text names [secretWord], as a data URL.

View Source
var ToneAudio = func() string {
	const rate, seconds = 16000, 1
	samples := make([]int16, rate*seconds)
	for i := range samples {
		samples[i] = int16(math.MaxInt16 / 2 * math.Sin(2*math.Pi*440*float64(i)/rate))
	}
	dataSize := uint32(len(samples) * 2)
	var buf bytes.Buffer
	for _, field := range []any{
		[]byte("RIFF"), 36 + dataSize, []byte("WAVE"),

		[]byte("fmt "), uint32(16), uint16(1), uint16(1), uint32(rate), uint32(rate * 2), uint16(2), uint16(16),
		[]byte("data"), dataSize, samples,
	} {
		if err := binary.Write(&buf, binary.LittleEndian, field); err != nil {
			panic(err)
		}
	}
	return "data:audio/wav;base64," + base64.StdEncoding.EncodeToString(buf.Bytes())
}()

ToneAudio is one second of a 440 Hz sine tone as a 16 kHz mono 16-bit WAV data URL.

Functions

func Env

func Env(t *testing.T, names ...string) string

Env gates a live test and returns the value of the first of names that is set, typically the provider's API key under its accepted spellings. Call it first in every live test.

Without GENKIT_LIVE, or without any of names, the test skips, or fails when the -run pattern names live tests, since a run that asked for a test and quietly skipped it would read as a pass. With no names, Env only gates (for a local server that needs no key) and returns "". It fails a test whose name does not contain "Live", since -run could not name it.

func Expensive

func Expensive(t *testing.T)

Expensive skips t unless GENKIT_LIVE=all. Use it for a case that costs far more than a short generation: video, image or speech output, context caching, or a sweep over a whole catalog.

func Init

func Init(t *testing.T, plugins ...api.Plugin) *genkit.Genkit

Init returns a Genkit instance with plugins and the experimental surface the agent cases need. Plugin live tests build their instance with it.

func Run

func Run(t *testing.T, g *genkit.Genkit, s Suite, groups ...Group)

Run walks the plugin registered on g through the shared checklist and then through groups. Build g with Init. It defines the tools gablorken, transferFunds, lookupOrder, runDiagnostics and fetchSwatch and agents named livetestAgent1 onward on g, so call it once per Genkit instance and keep those names free in the plugin's own subtests.

func RunEmbedder

func RunEmbedder(t *testing.T, g *genkit.Genkit, s EmbedderSuite)

RunEmbedder walks the embedder through the shared checklist, as subtests of an "embedder" subtest.

Types

type Case

type Case struct {
	Name string
	// Needs returns why the case cannot run against the suite, or "" when
	// it can. Nil runs the case always.
	Needs func() string
	Run   func(t *testing.T)
}

Case is one check in a Group.

type EmbedderSuite

type EmbedderSuite struct {
	// Embedder is the embedder under test. Required.
	Embedder ai.EmbedderArg
	// Dimensions is the length every vector must have. Required.
	Dimensions int
	// Normalized reports whether the provider returns unit-length vectors.
	Normalized bool
}

EmbedderSuite describes how to drive one embedder through the shared checklist.

type Group

type Group struct {
	Name  string
	Cases []Case
}

Group is a set of checks a family or plugin tier adds to the checklist. Its cases run after the shared ones, as subtests of a subtest named Name, and Suite.Skip names them as "Name/case".

type Suite

type Suite struct {
	// Model answers the cheap prompts. Every case that needs no special
	// model runs against it. Required.
	Model ai.ModelArg
	// ReasoningModel emits thinking, usually an [ai.ModelRef] carrying the
	// plugin's thinking config. Nil skips the reasoning cases.
	ReasoningModel ai.ModelArg
	// ReasoningContent reports whether the provider returns the thinking
	// content itself. Endpoints that accept the knob but keep the content
	// server-side leave this false and still get the no-error checks.
	ReasoningContent bool
	// StreamOnlyReasoning skips the non-streaming reasoning cases, for
	// providers that only think on streaming calls.
	StreamOnlyReasoning bool
	// VisionModel accepts inline images. Nil falls back to Model when Model
	// claims media support, and skips the vision cases otherwise.
	VisionModel ai.ModelArg
	// AudioModel accepts audio input. Nil skips the audio case, as models
	// that read images often read no audio.
	AudioModel ai.ModelArg
	// DocumentModel accepts PDF input. Nil skips the document case.
	DocumentModel ai.ModelArg
	// LimitConfig is a request config for Model that caps output at a few
	// tokens, to check the length finish reason. Config types differ per
	// plugin, so the suite cannot build one. Nil skips the case.
	LimitConfig any
	// ToolResponseMedia reports whether the provider reads images returned
	// inside a tool response. Providers that cannot carry them drop them with
	// a warning, so the case only runs where they can.
	ToolResponseMedia bool
	// BadKeyPlugin is the plugin configured with an API key the provider
	// rejects, to check that the refusal classifies as UNAUTHENTICATED. Nil
	// skips the case, for plugins that authenticate ambiently.
	BadKeyPlugin api.Plugin
	// Skip maps a case name, as in "generate/tool choice none" or
	// "compat_oai/extra config passthrough", to the reason the plugin or the
	// provider cannot pass it.
	Skip map[string]string
}

Suite describes how to drive one provider through the shared checklist.

Jump to

Keyboard shortcuts

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