Documentation
¶
Overview ¶
Package probes provides instrumented dependency wrappers and operation contracts for SDK integration tests.
The package addresses a class of bug that PromptKit's existing test suite could not detect: stages whose element flow is correct (1 in → 1 out) but whose external side effects (Load, Save, render, emit) multiply with input cardinality. Without explicit operation counters, "did X happen" tests pass while "did X happen exactly N times" remains unenforced.
Contracts declare per-Send operation budgets ("renderer.RenderDetailed must execute exactly once per Send, regardless of history depth"). Probes capture the actual count. Tests run a fixture matrix that varies the dimensions which drive multiplication and assert contracts hold across all of them.
Index ¶
- type AutoSummarizeOpts
- type Bound
- type Op
- type Ops
- type PipelineInvariants
- type Probes
- func (p *Probes) Bus() *events.EventBus
- func (p *Probes) Events(et events.EventType) []*events.Event
- func (p *Probes) ResetCounters()
- func (p *Probes) Snapshot() Snapshot
- func (p *Probes) SummarizerProvider() providers.Provider
- func (p *Probes) WaitForCount(op Op, atLeast int, timeout time.Duration) bool
- type RunOptions
- type Snapshot
- type StageContract
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AutoSummarizeOpts ¶
AutoSummarizeOpts configures probed auto-summarization.
type Bound ¶
Bound describes the acceptable count range for an operation per Send. A Max of -1 means unbounded.
type Op ¶
type Op string
Op is the name of a counted operation, e.g. "store.Load" or "events.template.rendered".
type PipelineInvariants ¶
type PipelineInvariants struct {
// Label is a free-form name used in failure messages.
Label string
// PerSend lists pipeline-wide operations whose count is Send-scoped.
PerSend Ops
}
PipelineInvariants declares per-Send budgets that hold across the entire pipeline (cross-stage), not for any single stage.
func (PipelineInvariants) AssertHolds ¶
func (p PipelineInvariants) AssertHolds(t *testing.T, snap Snapshot)
AssertHolds checks that the snapshot satisfies every clause.
type Probes ¶
type Probes struct {
// contains filtered or unexported fields
}
Probes captures observed operation counts for a single Send window.
Probes is wired into the SDK via Run: store calls are counted via a wrapping statestore.Store; events are counted via a SubscribeAll listener on the bus. Renderer / tool / provider counts are derived from existing event types rather than wrapping each interface — the lifecycle events on the bus are already authoritative for "this operation happened once."
For tests that need to inspect event payloads (e.g. assert tokens entering the provider stay within budget), the full event records are retained and exposed via Events(eventType).
A separately-counted Provider wrapper is exposed via SummarizerProvider for auto-summarize tests, since the LLMSummarizer calls Predict on its provider directly without going through the pipeline's ProviderStage (so no provider.call.* events fire for summarization).
Counters are reset by ResetCounters; Run calls it after sdk.Open completes so that init-time Loads/Saves do not appear in Send-scoped snapshots.
func Run ¶
func Run(tb testing.TB, ro RunOptions) (*Probes, *sdk.Conversation)
Run wires probes around a fresh sdk.Conversation and returns both. Counters are reset before Run returns, so the caller observes only post-Open traffic.
Cleanup (closing the conv and the bus) is registered with tb.Cleanup.
Accepts testing.TB so both *testing.T (unit / integration tests) and *testing.B (benchmarks) can use the helper.
RunOptions is intentionally passed by value: tests benefit from the inline struct-literal call site (Run(t, RunOptions{SeedHistory: 5})), and the per-call cost of an 80-byte copy is negligible against the work Run does.
func (*Probes) Bus ¶
Bus returns the event bus the probes are listening on. Tests can use it for additional subscriptions, but should not close it — Run wires cleanup.
func (*Probes) Events ¶
Events returns a snapshot of all collected events of the given type, in the order they were observed. Useful for inspecting payload data — for example, asserting that tokens entering the provider stay within budget.
func (*Probes) ResetCounters ¶
func (p *Probes) ResetCounters()
ResetCounters zeroes every counter. Run calls this after sdk.Open so that initialisation traffic does not leak into per-Send observations.
func (*Probes) SummarizerProvider ¶
SummarizerProvider returns a counted Provider wrapper suitable to pass as the auto-summarize provider via sdk.WithAutoSummarize. Predict calls on this provider are counted under op "summarizer.Predict".
func (*Probes) WaitForCount ¶
WaitForCount blocks until the count for op reaches at least atLeast or the timeout expires. Returns true if the threshold was met, false on timeout.
Counts grow monotonically inside a single observation window (we do not emit anti-events), so a successful WaitForCount guarantees the snapshot holds at least the requested count. Callers asserting an *exact* count should pair WaitForCount with a brief settle period and an Equal check to catch overshoot.
type RunOptions ¶
type RunOptions struct {
// PackJSON is the pack definition. If empty, a minimal default is used.
PackJSON string
// PromptName selects the prompt within the pack. Defaults to "chat".
PromptName string
// ConversationID is passed to sdk.WithConversationID. Defaults to
// "probes-conv".
ConversationID string
// SeedHistory pre-populates the conversation with N prior messages
// (alternating user/assistant). Used to exercise stages whose work
// scales with conversation length.
SeedHistory int
// SDKOptions are appended after the probe-default options. Caller-supplied
// options can override defaults (for example, providing a non-mock
// provider or a different state store), but doing so will defeat the
// corresponding probe.
SDKOptions []sdk.Option
// AutoSummarize, when non-nil, wires sdk.WithAutoSummarize using the
// probes' counted summarizer provider, so summarizer.Predict shows up
// in Snapshot.Count.
AutoSummarize *AutoSummarizeOpts
}
RunOptions configures a probed conversation.
Zero values give a sensible default: a fresh in-memory store, the minimal pack, the "chat" prompt, a deterministic conversation ID, and a mock provider. Tests override only what they need.
type Snapshot ¶
type Snapshot struct {
// contains filtered or unexported fields
}
Snapshot is an immutable point-in-time view of probe counters.
Operation names follow the convention "<dependency>.<method>" for direct wraps (e.g. "store.Load") and "events.<event_type>" for events (e.g. "events.prompt.template.rendered").
type StageContract ¶
type StageContract struct {
// Stage is a free-form label used in failure messages.
Stage string
// PerSend lists operations whose count is expected to be Send-scoped.
PerSend Ops
}
StageContract declares the per-Send operation budget for a single stage. Used by tests as a first-class fixture: assert that a stage performs each declared operation within bounds across a matrix of inputs.
func (StageContract) AssertHolds ¶
func (c StageContract) AssertHolds(t *testing.T, snap Snapshot)
AssertHolds checks that the snapshot satisfies every clause of the contract. Each violation produces a separate t.Errorf so all failures surface at once.