Documentation
¶
Overview ¶
Package engine orchestrates test execution across scenarios, providers, and configurations.
The engine package is the core execution layer of the Arena testing tool. It manages:
- Conversation lifecycle and message flow
- Provider and model configuration
- Telemetry and metrics collection
- Result aggregation and validation
- Concurrent test execution across multiple scenarios
Key types:
- Engine: Main orchestration struct that executes test runs
- RunResult: Contains execution results, metrics, and conversation history
- RunFilters: Filters for selective test execution
Example usage:
eng, _ := engine.NewEngine(cfg, providerRegistry, promptRegistry)
results, _ := eng.Execute(ctx, filters)
for _, result := range results {
fmt.Printf("Scenario %s: %s\n", result.ScenarioID, result.Status)
}
Index ¶
- Constants
- func AggregateTrialResults(store *statestore.ArenaStateStore, runIDs []string, combos []RunCombination) []string
- func ApplyPerturbation(turns []config.TurnDefinition, variant PerturbationVariant) []config.TurnDefinition
- func FormatFileSize(bytes int64) string
- func FormatMediaType(mediaType string) string
- type A2AAgentInfo
- type A2ASkillInfo
- type AgentInfo
- type AssertionTrialStats
- type AssertionsSummary
- type AudioMonitorHook
- type CompositeConversationExecutor
- func (ce *CompositeConversationExecutor) ExecuteConversation(ctx context.Context, req ConversationRequest) *ConversationResult
- func (ce *CompositeConversationExecutor) ExecuteConversationStream(ctx context.Context, req ConversationRequest) (<-chan ConversationStreamChunk, error)
- func (ce *CompositeConversationExecutor) GetDefaultExecutor() *DefaultConversationExecutor
- func (ce *CompositeConversationExecutor) GetDuplexExecutor() *DuplexConversationExecutor
- func (ce *CompositeConversationExecutor) GetEvalExecutor() *EvalConversationExecutor
- type CompositionMetadataProvider
- type ConversationExecutor
- type ConversationRequest
- type ConversationResult
- type ConversationStreamChunk
- type DefaultConversationExecutor
- type DuplexConversationExecutor
- func (de *DuplexConversationExecutor) ExecuteConversation(ctx context.Context, req ConversationRequest) *ConversationResult
- func (de *DuplexConversationExecutor) ExecuteConversationStream(ctx context.Context, req ConversationRequest) (<-chan ConversationStreamChunk, error)
- func (de *DuplexConversationExecutor) RunInteractiveVoice(ctx context.Context, req *ConversationRequest, mic <-chan []byte, ...) error
- type Engine
- func (e *Engine) Agents() []AgentInfo
- func (e *Engine) AudioMonitorEnabled() bool
- func (e *Engine) AudioMonitorOptions() arenaaudio.Options
- func (e *Engine) Close() error
- func (e *Engine) ConfigureSessionRecordingFromConfig() error
- func (e *Engine) EnableAudioMonitor(opts arenaaudio.Options) error
- func (e *Engine) EnableMessageEvents()
- func (e *Engine) EnableMockProviderMode(mockConfigPath string) error
- func (e *Engine) EnableSessionRecording(recordingDir string) error
- func (e *Engine) EnableSessionRecordingWithStore(store events.EventStore) error
- func (e *Engine) ExecuteRuns(ctx context.Context, plan *RunPlan, concurrency int) ([]string, error)
- func (e *Engine) GenerateRunPlan(regionFilter, providerFilter, scenarioFilter, evalFilter []string) (*RunPlan, error)
- func (e *Engine) GetConfig() *config.Config
- func (e *Engine) GetDuplexExecutor() *DuplexConversationExecutor
- func (e *Engine) GetRecordingDir() string
- func (e *Engine) GetRecordingPath(runID string) string
- func (e *Engine) GetStateStore() statestore.Store
- func (e *Engine) HasConfigEvals() bool
- func (e *Engine) ListProviders() []ProviderInfo
- func (e *Engine) ListScenarios() []ScenarioInfo
- func (e *Engine) MissingRequiredVars(taskType string, provided map[string]string) ([]string, error)
- func (e *Engine) NewInteractiveSession(opts InteractiveSessionOptions) (*InteractiveSession, error)
- func (e *Engine) ProviderIDs() []string
- func (e *Engine) RegisterAudioMonitorHook(hook AudioMonitorHook)
- func (e *Engine) RegisterRunCompletedHook(hook RunCompletedHook)
- func (e *Engine) SetEventBus(bus events.Bus, opts ...EventBusOption)
- func (e *Engine) SetMetrics(collector *metrics.Collector, instanceLabels map[string]string)
- func (e *Engine) SetTracerProvider(tp trace.TracerProvider)
- func (e *Engine) WithArtifactStore(s artifacts.Store)
- func (e *Engine) WithOutputDir(dir string)
- func (e *Engine) WithSessionHooks(reg *hooks.Registry)
- type EvalConversationExecutor
- type EvalOrchestrator
- func (h *EvalOrchestrator) Clone() *EvalOrchestrator
- func (h *EvalOrchestrator) HasEvals() bool
- func (h *EvalOrchestrator) RunAssertionsAsConversationResults(ctx context.Context, assertionConfigs []assertions.AssertionConfig, ...) []assertions.ConversationValidationResult
- func (h *EvalOrchestrator) RunAssertionsAsEvals(ctx context.Context, assertionConfigs []assertions.AssertionConfig, ...) []evals.EvalResult
- func (h *EvalOrchestrator) RunConversationEvals(ctx context.Context, messages []types.Message, sessionID string) []assertions.ConversationValidationResult
- func (h *EvalOrchestrator) RunSessionEvals(ctx context.Context, messages []types.Message, sessionID string) []evals.EvalResult
- func (h *EvalOrchestrator) RunTurnEvals(ctx context.Context, messages []types.Message, turnIndex int, sessionID string) []assertions.ConversationValidationResult
- func (h *EvalOrchestrator) SetClassifyRegistry(r *classify.Registry)
- func (h *EvalOrchestrator) SetCompositionMetadataProvider(provider CompositionMetadataProvider)
- func (h *EvalOrchestrator) SetEventBus(bus events.Bus)
- func (h *EvalOrchestrator) SetMetadata(metadata map[string]any)
- func (h *EvalOrchestrator) SetWorkflowMetadataProvider(provider WorkflowMetadataProvider)
- type EventBusOption
- type InteractiveSession
- func (s *InteractiveSession) ConversationID() string
- func (s *InteractiveSession) Cost(ctx context.Context) (types.CostInfo, error)
- func (s *InteractiveSession) Messages(ctx context.Context) ([]types.Message, error)
- func (s *InteractiveSession) Provider() providers.Provider
- func (s *InteractiveSession) RunEvals(ctx context.Context) ([]evals.EvalResult, error)
- func (s *InteractiveSession) RunEvalsEnabled() bool
- func (s *InteractiveSession) SendUserMessage(ctx context.Context, text string) (<-chan turnexecutors.MessageStreamChunk, error)
- type InteractiveSessionOptions
- type MediaOutput
- type MediaOutputStats
- type PerturbationVariant
- type ProviderInfo
- type RunCombination
- type RunCompletedHook
- type RunPlan
- type RunResult
- type ScenarioInfo
- type SelfPlayRoleInfo
- type SkillFilterer
- type StateStoreConfig
- type TrialGroupKey
- type TrialResults
- type WorkflowMetadataProvider
Constants ¶
const DefaultRunTimeout = 5 * time.Minute
DefaultRunTimeout is the default maximum duration for a single run execution. If a provider call hangs, the run will be canceled after this timeout, freeing up the semaphore slot for other runs.
Variables ¶
This section is empty.
Functions ¶
func AggregateTrialResults ¶ added in v1.3.10
func AggregateTrialResults(store *statestore.ArenaStateStore, runIDs []string, combos []RunCombination) []string
AggregateTrialResults groups trial run results by scenario+provider+region, computes statistical metrics, and updates the first run in each group with the aggregated TrialResults. Returns the run IDs that represent trial groups (i.e., the first run ID of each group that now carries the summary).
func ApplyPerturbation ¶ added in v1.3.10
func ApplyPerturbation(turns []config.TurnDefinition, variant PerturbationVariant) []config.TurnDefinition
ApplyPerturbation substitutes perturbation variables in turn content. Placeholders use {key} syntax (e.g., "Book a flight from {city}" with city=NYC becomes "Book a flight from NYC").
func FormatFileSize ¶ added in v1.1.0
FormatFileSize formats bytes as human-readable size
func FormatMediaType ¶ added in v1.1.0
FormatMediaType returns a human-readable label for media type
Types ¶
type A2AAgentInfo ¶ added in v1.3.1
type A2AAgentInfo struct {
Name string `json:"name"`
Description string `json:"description"`
Skills []A2ASkillInfo `json:"skills,omitempty"`
}
A2AAgentInfo contains metadata about an A2A agent for report rendering.
type A2ASkillInfo ¶ added in v1.3.1
type A2ASkillInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Tags []string `json:"tags,omitempty"`
}
A2ASkillInfo contains metadata about a single A2A agent skill.
type AgentInfo ¶ added in v1.5.2
AgentInfo describes one selectable agent in an Arena config. An "agent" is a prompt config, keyed by its task_type, carrying the system prompt and tools.
type AssertionTrialStats ¶ added in v1.3.10
type AssertionTrialStats struct {
// PassRate is the fraction of trials where this assertion passed.
PassRate float64 `json:"pass_rate"`
// PassCount is the number of trials where this assertion passed.
PassCount int `json:"pass_count"`
// FailCount is the number of trials where this assertion failed.
FailCount int `json:"fail_count"`
// FlakinessScore for this specific assertion.
FlakinessScore float64 `json:"flakiness_score"`
}
AssertionTrialStats holds per-assertion statistics across trial runs.
type AssertionsSummary ¶ added in v1.1.3
type AssertionsSummary struct {
Failed int `json:"failed"`
Passed bool `json:"passed"`
Results []assertions.ConversationValidationResult `json:"results"`
Total int `json:"total"`
}
AssertionsSummary matches the structure used for turn-level assertions in message meta
type AudioMonitorHook ¶ added in v1.4.7
type AudioMonitorHook func(runID string, router *arenaaudio.AudioRouter, rate int)
AudioMonitorHook is invoked when the engine constructs a per-run AudioRouter. Hooks can use the router to attach additional consumers (web SSE relay, TUI level meter). The router lifecycle is the run; hooks must not retain the pointer after the corresponding run completes and must not call Close (the engine owns lifecycle).
runID is the unique identifier for this run; rate is the canonical sample rate of the router.
type CompositeConversationExecutor ¶ added in v1.1.6
type CompositeConversationExecutor struct {
// contains filtered or unexported fields
}
CompositeConversationExecutor routes conversation execution to the appropriate executor based on scenario configuration. It selects between: - EvalConversationExecutor for evaluation mode (recording replay with assertions) - DefaultConversationExecutor for standard turn-based conversations - DuplexConversationExecutor for bidirectional streaming scenarios
func NewCompositeConversationExecutor ¶ added in v1.1.6
func NewCompositeConversationExecutor( defaultExecutor *DefaultConversationExecutor, duplexExecutor *DuplexConversationExecutor, evalExecutor *EvalConversationExecutor, ) *CompositeConversationExecutor
NewCompositeConversationExecutor creates a new composite executor.
func (*CompositeConversationExecutor) ExecuteConversation ¶ added in v1.1.6
func (ce *CompositeConversationExecutor) ExecuteConversation( ctx context.Context, req ConversationRequest, ) *ConversationResult
ExecuteConversation routes to the appropriate executor based on scenario config. If the request is for eval mode, uses EvalConversationExecutor. If the scenario has duplex configuration, uses DuplexConversationExecutor. Otherwise, uses DefaultConversationExecutor.
func (*CompositeConversationExecutor) ExecuteConversationStream ¶ added in v1.1.6
func (ce *CompositeConversationExecutor) ExecuteConversationStream( ctx context.Context, req ConversationRequest, ) (<-chan ConversationStreamChunk, error)
ExecuteConversationStream routes streaming execution to the appropriate executor.
func (*CompositeConversationExecutor) GetDefaultExecutor ¶ added in v1.1.6
func (ce *CompositeConversationExecutor) GetDefaultExecutor() *DefaultConversationExecutor
GetDefaultExecutor returns the default executor for direct access if needed.
func (*CompositeConversationExecutor) GetDuplexExecutor ¶ added in v1.1.6
func (ce *CompositeConversationExecutor) GetDuplexExecutor() *DuplexConversationExecutor
GetDuplexExecutor returns the duplex executor for direct access if needed.
func (*CompositeConversationExecutor) GetEvalExecutor ¶ added in v1.1.9
func (ce *CompositeConversationExecutor) GetEvalExecutor() *EvalConversationExecutor
GetEvalExecutor returns the eval executor for direct access if needed.
type CompositionMetadataProvider ¶ added in v1.5.0
CompositionMetadataProvider is implemented by types that provide per-turn composition observability (step outputs, branch-taken, parallel status) for injection into the eval context, so composition_* assertions can target intermediate steps. Set per-run for composition scenarios (RFC 0010).
type ConversationExecutor ¶
type ConversationExecutor interface {
// ExecuteConversation runs a complete conversation based on scenario
ExecuteConversation(ctx context.Context, req ConversationRequest) *ConversationResult
// ExecuteConversationStream runs a conversation with streaming
ExecuteConversationStream(ctx context.Context, req ConversationRequest) (<-chan ConversationStreamChunk, error)
}
ConversationExecutor orchestrates full conversation flows
func BuildEngineComponents ¶ added in v1.1.9
func BuildEngineComponents(cfg *config.Config, providerFilter []string) ( providerRegistry *providers.Registry, promptRegistry *prompt.Registry, mcpRegistry *mcp.RegistryImpl, convExecutor ConversationExecutor, adapterReg *adapters.Registry, a2aCleanup func(), toolReg *tools.Registry, skillExec *skills.Executor, err error, )
BuildEngineComponents builds all engine components from a loaded Config object. This function creates and initializes: - MCP registry and tools (if configured) - Provider registry (for main assistant) - Prompt registry (if configured) - Tool registry (static + MCP tools) - Turn executor - Self-play provider registry (if enabled) - Self-play registry (if enabled) - Conversation executor
This function is exported to enable programmatic creation of Arena engines without requiring file-based configuration. Users can construct a *config.Config programmatically and pass it to this function to get all required registries for use with NewEngine.
Returns all components needed to construct an Engine, or an error if any component fails to build.
type ConversationRequest ¶
type ConversationRequest struct {
// Required fields
Provider providers.Provider
Scenario *config.Scenario
Eval *config.Eval // Eval configuration (mutually exclusive with Scenario)
Config *config.Config
Region string
// Optional overrides (for future use)
Temperature *float64 // Override scenario temperature
MaxTokens *int // Override scenario max tokens
Timeout *int // Timeout in seconds
// For distributed execution and tracing (v0.2.0+)
RunID string // Unique identifier for this run
Metadata map[string]string // Additional metadata for debugging/tracing
// Event bus for runtime/TUI events
EventBus events.Bus
// State management
StateStoreConfig *StateStoreConfig // Optional state store configuration
ConversationID string // Conversation identifier for state persistence
// Per-run eval orchestrator override (for workflow scenarios that need
// isolated workflow metadata). If nil, the executor's shared orchestrator is used.
EvalOrchestrator *EvalOrchestrator
// VoiceSTT is the STT provider for the VAD voice path (Task 7). Nil in ASM mode.
// Set from --voice-stt by the chat command; consumed by runInteractiveVADVoice.
VoiceSTT *config.Provider
// VoiceOutputVoice is the TTS voice ID for the VAD voice path (Task 7).
// Set from --voice-output-voice by the chat command; consumed by runInteractiveVADVoice.
VoiceOutputVoice string
// VoiceBargeIn enables barge-in (interrupting the agent mid-reply) on the
// composed-VAD voice path. Opt-in (--barge-in) because it needs headphones /
// AEC and audio-sink flush support; off by default the console does clean
// turn-taking.
VoiceBargeIn bool
// RecordingConfig enables RecordingStage in the pipeline for message.created events.
// If nil, no recording stages are added.
RecordingConfig *stage.RecordingStageConfig
// EventStore is the destination for RecordingStage writes. Required when
// RecordingConfig is non-nil; without it, recording stages will not be added
// to the pipeline.
EventStore events.EventStore
// PostTurnHook is called after each turn completes. Used by the workflow
// engine to commit deferred transitions after the pipeline finishes.
PostTurnHook func() error
// ContextEnricher is called before each turn to enrich the pipeline context.
// Used to inject per-run state (e.g., skill filters) into context for tool execution.
ContextEnricher func(ctx context.Context) context.Context
// ActiveCompositionResolver, when non-nil, is called per-turn to determine
// the active composition for the current workflow state (RFC 0010). It returns
// nil for states that are not composition-orchestrated.
ActiveCompositionResolver func() *composition.Composition
// CompositionRecorder is the per-run recorder for RFC 0010 testability. When
// non-nil it is stamped onto every TurnRequest so buildStagePipeline can pass
// it to NewCompositionStageWithRecorder. Reset() is called per turn so stale
// data from a prior turn does not leak into the next turn's assertions.
CompositionRecorder *stage.CompositionRecorder
// AudioRouter, when non-nil, is the per-run AudioRouter for audio
// monitoring. MonitorTap stages added to the pipeline publish to this
// router. Lifetime is the run; the engine owns Close().
AudioRouter *arenaaudio.AudioRouter
// CurrentWorkflowState returns the live workflow-state metadata for the turn
// about to run (captured at turn start, before the pipeline builds its
// prompt). Nil when there is no workflow. Used to stamp current_workflow_state
// onto each assistant result so composition-orchestrated terminal states —
// which leave no transition tool-result message — are still visible per turn.
CurrentWorkflowState func() map[string]interface{}
// StampWorkflowState attaches meta (captured at turn start) to the assistant
// message produced by the just-completed turn. No-op when meta is nil. This is
// side-effect-only and must never alter turn output, error, or flow.
StampWorkflowState func(meta map[string]interface{})
// contains filtered or unexported fields
}
ConversationRequest contains all data needed for conversation execution. Using a request object makes the API extensible without breaking changes.
type ConversationResult ¶
type ConversationResult struct {
Messages []types.Message // Flat list of all messages in the conversation
Cost types.CostInfo // Total cost across all messages
ToolStats *types.ToolStats // Tool usage statistics
Violations []types.ValidationError // Validation errors
MediaOutputs []MediaOutput // Media outputs generated by LLMs
// Conversation-level assertions (test-only gates: passed/failed)
ConversationAssertionResults []assertions.ConversationValidationResult `json:"conv_assertions_results,omitempty"`
// EvalResults are the runtime's pack-level eval observations —
// non-gating measurements that fire during a session (the same
// thing that would surface in production). Distinct from
// ConversationAssertionResults because evals don't pass/fail;
// they emit structured Value/Details for reporting and aggregation.
EvalResults []evals.EvalResult `json:"eval_results,omitempty"`
// Self-play metadata
SelfPlay bool `json:"self_play,omitempty"`
PersonaID string `json:"persona_id,omitempty"`
// Error handling
Error string `json:"error,omitempty"` // Error message if execution failed
Failed bool `json:"failed,omitempty"` // Whether execution failed (but partial results may be available)
Skipped bool `json:"skipped,omitempty"` // Whether execution was skipped due to transient provider error
SkipReason string `json:"skip_reason,omitempty"` // Reason for skipping
}
ConversationResult contains the outcome of conversation execution
type ConversationStreamChunk ¶
type ConversationStreamChunk struct {
// Current turn number (0-indexed)
TurnIndex int
// Delta content from this specific chunk
Delta string
// Token count (accumulated for current turn)
TokenCount int
// Finish reason for current turn (only in last chunk of turn)
FinishReason *string
// Complete conversation result (accumulated, updated with each chunk)
Result *ConversationResult
// Error if streaming failed
Error error
// Metadata
Metadata map[string]interface{}
}
ConversationStreamChunk represents a streaming chunk during conversation execution
type DefaultConversationExecutor ¶
type DefaultConversationExecutor struct {
// contains filtered or unexported fields
}
DefaultConversationExecutor implements ConversationExecutor interface
func NewDefaultConversationExecutor ¶
func NewDefaultConversationExecutor( scriptedExecutor turnexecutors.TurnExecutor, selfPlayExecutor turnexecutors.TurnExecutor, selfPlayRegistry *selfplay.Registry, promptRegistry *prompt.Registry, evalOrchestrator *EvalOrchestrator, ) *DefaultConversationExecutor
NewDefaultConversationExecutor creates a new conversation executor
func (*DefaultConversationExecutor) ExecuteConversation ¶
func (ce *DefaultConversationExecutor) ExecuteConversation(ctx context.Context, req ConversationRequest) *ConversationResult
ExecuteConversation runs a complete conversation based on scenario using the new Turn model
func (*DefaultConversationExecutor) ExecuteConversationStream ¶
func (ce *DefaultConversationExecutor) ExecuteConversationStream(ctx context.Context, req ConversationRequest) (<-chan ConversationStreamChunk, error)
ExecuteConversationStream runs a complete conversation with streaming
type DuplexConversationExecutor ¶ added in v1.1.6
type DuplexConversationExecutor struct {
// contains filtered or unexported fields
}
DuplexConversationExecutor handles duplex (bidirectional streaming) conversations. Unlike the standard executor which processes turns sequentially, this executor establishes a persistent streaming session and handles real-time audio I/O.
func NewDuplexConversationExecutor ¶ added in v1.1.6
func NewDuplexConversationExecutor( selfPlayRegistry *selfplay.Registry, promptRegistry *prompt.Registry, toolRegistry *tools.Registry, mediaStorage storage.MediaStorageService, evalOrchestrator *EvalOrchestrator, ) *DuplexConversationExecutor
NewDuplexConversationExecutor creates a new duplex conversation executor.
func (*DuplexConversationExecutor) ExecuteConversation ¶ added in v1.1.6
func (de *DuplexConversationExecutor) ExecuteConversation( ctx context.Context, req ConversationRequest, ) *ConversationResult
ExecuteConversation runs a duplex conversation based on scenario. For duplex mode, this establishes a streaming session and processes audio turns in real-time.
func (*DuplexConversationExecutor) ExecuteConversationStream ¶ added in v1.1.6
func (de *DuplexConversationExecutor) ExecuteConversationStream( ctx context.Context, req ConversationRequest, ) (<-chan ConversationStreamChunk, error)
ExecuteConversationStream runs a duplex conversation with streaming output. For duplex mode, this returns chunks as they arrive from the provider.
func (*DuplexConversationExecutor) RunInteractiveVoice ¶ added in v1.5.3
func (de *DuplexConversationExecutor) RunInteractiveVoice( ctx context.Context, req *ConversationRequest, mic <-chan []byte, play func([]byte), ) error
RunInteractiveVoice drives a live, mic-fed voice conversation through the duplex pipeline until mic is closed or ctx is canceled.
Mode selection (in priority order):
- req.VoiceSTT != nil → composed VAD pipeline (AudioTurn → STT → LLM → TTS). The caller explicitly configured an STT provider via --voice-stt, signaling the composed path even when the underlying provider also implements StreamInputSupport. The OpenAI provider implements StreamInputSupport unconditionally for every model (including plain text models like gpt-4o), so checking VoiceSTT first prevents a plain-text agent from being wrongly routed to the ASM/realtime session path (which would attempt to create a gpt-4o-realtime-preview session and fail with model_not_found).
- provider implements StreamInputSupport → ASM pipeline (buildDuplexPipeline → DuplexProviderStage → ArenaStateStoreSaveStage).
- fallback → composed VAD pipeline (runInteractiveVADVoice will return an error if VoiceSTT is nil, explaining the missing --voice-stt flag).
Mic frames (raw PCM16 mono @ 16 kHz or provider-preferred rate) are forwarded to the pipeline input channel one element per frame. Response audio from the pipeline output channel is delivered to play concurrently; the drain goroutine is joined before RunInteractiveVoice returns so every response frame is delivered to play by the time the function exits.
When mic is closed, an EndOfStream element is sent to the pipeline to signal end-of-user-speech to the provider (matching how processSingleDuplexTurn does it), then inputChan is closed and the output drain is awaited.
History (transcripts + tool calls) is persisted by ArenaStateStoreSaveStage inside the pipeline, exactly as for a duplex scenario run.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine manages the execution of prompt testing scenarios across multiple providers, regions, and configurations. It coordinates conversation execution, tool calling, validation, and result collection.
The engine supports both scripted conversations and self-play mode where an LLM simulates user behavior. It handles provider initialization, concurrent execution, and comprehensive result tracking including costs and tool usage.
func NewEngine ¶
func NewEngine( cfg *config.Config, providerRegistry *providers.Registry, promptRegistry *prompt.Registry, mcpRegistry *mcp.RegistryImpl, convExecutor ConversationExecutor, adapterRegistry *adapters.Registry, toolRegistry *tools.Registry, ) (*Engine, error)
NewEngine creates a new simulation engine from pre-built components. This is the primary constructor for the Engine and is preferred for testing where components can be created and configured independently.
This constructor uses dependency injection, accepting all required registries and executors as parameters. This makes testing easier and follows better architectural practices.
Parameters:
- cfg: Fully loaded and validated Config object
- providerRegistry: Registry for looking up providers by ID
- promptRegistry: Registry for system prompts and task types
- convExecutor: Executor for full conversations
- adapterRegistry: Registry for recording adapters (used for eval enumeration)
Returns an initialized Engine ready for test execution.
func NewEngineFromConfig ¶ added in v1.1.11
NewEngineFromConfig creates a new Engine from a pre-loaded configuration. This allows CLI or programmatic callers to modify the config before engine creation. providerFilter limits which providers have credentials resolved; nil or empty means all providers are initialized.
func NewEngineFromConfigFile ¶
NewEngineFromConfigFile creates a new simulation engine from a configuration file. It loads the configuration, validates it, initializes all registries, and sets up the execution pipeline for conversation testing.
The configuration file is loaded along with all referenced resources (scenarios, providers, tools, personas), making the Config object fully self-contained.
This constructor performs all necessary initialization steps in the correct order: 1. Load and validate configuration from file (including all referenced resources) 2. Build registries from loaded resources 3. Initialize executors (turn, conversation, self-play if enabled) 4. Create Engine with all components
Note: Logger verbosity should be configured at application startup, not here. This function does not modify global logger settings.
Parameters:
- configPath: Path to the arena.yaml configuration file
Returns an initialized Engine ready for test execution, or an error if:
- Configuration file cannot be read or parsed
- Configuration validation fails
- Any resource file cannot be loaded
- Provider type is unsupported
func (*Engine) Agents ¶ added in v1.5.2
Agents lists the prompt configs (agents) declared in the loaded config, sorted by task_type for stable ordering in pickers.
func (*Engine) AudioMonitorEnabled ¶ added in v1.4.7
AudioMonitorEnabled reports whether EnableAudioMonitor was called. Callers use this to decide whether to construct an arenaaudio.Monitor.
func (*Engine) AudioMonitorOptions ¶ added in v1.4.7
func (e *Engine) AudioMonitorOptions() arenaaudio.Options
AudioMonitorOptions returns the configured monitor options. Zero value when monitoring is disabled — check AudioMonitorEnabled first.
func (*Engine) Close ¶
Close shuts down the engine and cleans up resources. This includes closing all MCP server connections, provider HTTP clients, and the event store if session recording is enabled.
func (*Engine) ConfigureSessionRecordingFromConfig ¶ added in v1.1.6
ConfigureSessionRecordingFromConfig enables session recording if configured. It reads the recording configuration from the engine's config and enables session recording with the appropriate directory path. Returns nil if recording is not enabled in the config.
func (*Engine) EnableAudioMonitor ¶ added in v1.4.7
func (e *Engine) EnableAudioMonitor(opts arenaaudio.Options) error
EnableAudioMonitor enables real-time audio monitoring for duplex scenarios.
The monitor surfaces (LocalSink, SSE relay, TUI level meter) are added to per-run pipelines only when at least one consumer is wired and the scenario uses duplex streaming. CI / non-interactive runs (auto mode + no TTY) do not construct any audio infrastructure.
Options.Rate must be one of audio.Rate16k, Rate24k, Rate48k.
This method only stores configuration; the per-run wiring happens in the pipeline construction path (see duplex_executor_pipeline_integration.go).
func (*Engine) EnableMessageEvents ¶ added in v1.3.22
func (e *Engine) EnableMessageEvents()
EnableMessageEvents enables RecordingStage in pipelines so message.created events are published to the event bus. This does NOT write session recordings to disk — use EnableSessionRecording for that. Requires an event bus to be configured via SetEventBus.
func (*Engine) EnableMockProviderMode ¶ added in v1.1.0
EnableMockProviderMode replaces all providers in the registry with mock providers. This enables testing of scenario behavior without making real API calls. Mock providers can use either file-based configuration for scenario-specific responses or default in-memory responses.
Parameters:
- mockConfigPath: Optional path to YAML configuration file for mock responses
Returns an error if the mock configuration file cannot be loaded or parsed.
func (*Engine) EnableSessionRecording ¶ added in v1.1.6
EnableSessionRecording enables session recording for all runs. Recordings are stored in the specified directory as JSONL files, one file per session (using RunID as session ID). Returns an error if the directory cannot be created.
func (*Engine) EnableSessionRecordingWithStore ¶ added in v1.4.7
func (e *Engine) EnableSessionRecordingWithStore(store events.EventStore) error
EnableSessionRecordingWithStore enables session recording using a caller-provided EventStore. Programmatic Arena consumers can inject any implementation: in-memory, S3-backed, sampled, ring-buffered, etc. The dir-based EnableSessionRecording is a thin wrapper that constructs a FileEventStore.
func (*Engine) ExecuteRuns ¶
GetStateStore returns the engine's state store for accessing run results Runs are executed concurrently up to the specified concurrency limit, with run IDs collected in order matching the input plan.
Each run executes independently: - Loads scenario and provider - Executes conversation turns (with self-play if configured) - Runs validators on the results - Tracks costs, timing, and tool calls - Saves results to StateStore
The context can be used to cancel all in-flight executions. Run IDs are returned for all combinations, with errors captured in individual RunResult (accessible via StateStore).
Parameters:
- ctx: Context for cancellation
- plan: RunPlan containing combinations to execute
- concurrency: Maximum number of simultaneous executions
Returns a slice of RunIDs in the same order as plan.Combinations, or an error if execution setup fails. Individual run errors are stored in StateStore, not returned here.
func (*Engine) GenerateRunPlan ¶
func (e *Engine) GenerateRunPlan(regionFilter, providerFilter, scenarioFilter, evalFilter []string) (*RunPlan, error)
GenerateRunPlan creates a comprehensive test execution plan from filter criteria. The plan contains all combinations of regions × providers × scenarios OR evals that match the provided filters. Scenarios and evals are mutually exclusive.
For scenarios: - regionFilter: Empty = all regions from prompt configs (or default) - providerFilter: Empty = all registered providers (or scenario-specified providers) - scenarioFilter: Empty = all loaded scenarios
For evals: - evalFilter: Empty = all loaded evals - Regions and providers are not used (they come from recordings)
Provider selection logic (scenarios only): 1. If scenario specifies providers: use those (intersected with CLI filter if provided) 2. If scenario doesn't specify providers: use all arena providers (intersected with CLI filter)
Returns a RunPlan containing all matching combinations, ready for execution. Each combination represents one independent test run that will be executed and validated separately.
func (*Engine) GetDuplexExecutor ¶ added in v1.5.3
func (e *Engine) GetDuplexExecutor() *DuplexConversationExecutor
GetDuplexExecutor returns the DuplexConversationExecutor from the engine's composite executor, or nil if the engine was not built with a duplex executor. Used by the chat command's voice path to drive RunInteractiveVoice.
func (*Engine) GetRecordingDir ¶ added in v1.1.6
GetRecordingDir returns the directory where session recordings are stored. Returns empty string if recording is not enabled.
func (*Engine) GetRecordingPath ¶ added in v1.1.6
GetRecordingPath returns the path to the recording file for a given run ID. Returns empty string if recording is not enabled.
func (*Engine) GetStateStore ¶
func (e *Engine) GetStateStore() statestore.Store
GetStateStore returns the engine's state store for accessing run results
func (*Engine) HasConfigEvals ¶ added in v1.5.2
HasConfigEvals reports whether the config declares any evals the console can run for scores.
func (*Engine) ListProviders ¶ added in v1.4.7
func (e *Engine) ListProviders() []ProviderInfo
ListProviders returns IDs of all loaded providers, sorted to put mock-style providers first (so a picker UI can default to a no-cost option) and the rest alphabetically afterwards.
func (*Engine) ListScenarios ¶ added in v1.4.7
func (e *Engine) ListScenarios() []ScenarioInfo
ListScenarios returns IDs and descriptions of all loaded scenarios, sorted by ID.
func (*Engine) MissingRequiredVars ¶ added in v1.5.2
MissingRequiredVars returns the names of template variables the prompt for taskType declares as required but that are absent (or blank) in provided. The console uses this to prompt the user before chatting.
func (*Engine) NewInteractiveSession ¶ added in v1.5.2
func (e *Engine) NewInteractiveSession(opts InteractiveSessionOptions) (*InteractiveSession, error)
NewInteractiveSession builds a session. Errors if the provider or scripted executor cannot be resolved. Variables are merged over the config's prompt-config vars (user wins).
func (*Engine) ProviderIDs ¶ added in v1.5.2
ProviderIDs lists the inference provider IDs declared in the loaded config, sorted for stable ordering.
func (*Engine) RegisterAudioMonitorHook ¶ added in v1.4.7
func (e *Engine) RegisterAudioMonitorHook(hook AudioMonitorHook)
RegisterAudioMonitorHook registers a callback fired whenever the engine constructs an AudioRouter for a run. Multiple hooks can be registered; each fires in registration order. Nil hooks are ignored.
Hooks should treat the router as borrowed — Subscribe / SubscribeRMS as needed for the run's duration, but never call Close. Hook panics propagate to the goroutine running the run; treat hook bodies as application code and recover internally if needed.
func (*Engine) RegisterRunCompletedHook ¶ added in v1.4.7
func (e *Engine) RegisterRunCompletedHook(hook RunCompletedHook)
RegisterRunCompletedHook registers a callback fired after each run in ExecuteRuns completes. Multiple hooks are supported and fire in registration order. Nil hooks are ignored. Hook panics propagate to the worker goroutine; treat hook bodies as application code.
func (*Engine) SetEventBus ¶ added in v1.1.4
func (e *Engine) SetEventBus(bus events.Bus, opts ...EventBusOption)
SetEventBus configures the shared event bus used for runtime and TUI observability. If session recording is enabled, the event store is subscribed to the bus.
func (*Engine) SetMetrics ¶ added in v1.3.20
SetMetrics configures Prometheus metrics collection for the engine. When set, a MetricContext is created and subscribed to the event bus, recording provider call durations, token counts, costs, and tool call metrics. An event bus must be configured via SetEventBus before calling this method.
func (*Engine) SetTracerProvider ¶ added in v1.3.20
func (e *Engine) SetTracerProvider(tp trace.TracerProvider)
SetTracerProvider configures OpenTelemetry distributed tracing for the engine. When set, an OTelEventListener is created and subscribed to the event bus, converting provider call, tool call, and pipeline events into OTel spans. An event bus must be configured via SetEventBus before calling this method.
func (*Engine) WithArtifactStore ¶ added in v1.5.2
WithArtifactStore sets the backend that persists per-run report artifacts. Defaults to the local backend (bytes left in place) when unset.
func (*Engine) WithOutputDir ¶ added in v1.5.2
WithOutputDir sets the resolved report output directory so the engine can expose each run's artifacts base (<outputDir>/artifacts/<runID>) to hooks via SessionEvent metadata. A nil/empty dir disables artifacts-base exposure.
func (*Engine) WithSessionHooks ¶ added in v1.5.2
WithSessionHooks configures session lifecycle hooks on the Engine. When set, the engine fires OnSessionStart before the first turn, OnSessionUpdate after each turn, and OnSessionEnd after the conversation completes. A nil registry is a no-op — no hooks are fired.
type EvalConversationExecutor ¶ added in v1.1.9
type EvalConversationExecutor struct {
// contains filtered or unexported fields
}
EvalConversationExecutor handles evaluation mode: replaying saved conversations with assertions. Unlike scenario execution, eval mode: - Loads turns from recordings (no prompt building) - Applies assertions to pre-recorded assistant messages - Skips tool execution (tool calls are metadata only) - Returns results in the same schema as scenario execution for output parity
func NewEvalConversationExecutor ¶ added in v1.1.9
func NewEvalConversationExecutor( adapterRegistry *adapters.Registry, promptRegistry *prompt.Registry, providerRegistry *providers.Registry, evalOrchestrator *EvalOrchestrator, ) *EvalConversationExecutor
NewEvalConversationExecutor creates a new eval conversation executor.
func (*EvalConversationExecutor) ExecuteConversation ¶ added in v1.1.9
func (e *EvalConversationExecutor) ExecuteConversation( ctx context.Context, req ConversationRequest, ) *ConversationResult
ExecuteConversation runs an evaluation on a saved conversation.
func (*EvalConversationExecutor) ExecuteConversationStream ¶ added in v1.1.9
func (e *EvalConversationExecutor) ExecuteConversationStream( ctx context.Context, req ConversationRequest, ) (<-chan ConversationStreamChunk, error)
ExecuteConversationStream runs evaluation with streaming output. For eval mode, we don't have true streaming since we're replaying, but we implement this to satisfy the interface.
type EvalOrchestrator ¶ added in v1.3.20
type EvalOrchestrator struct {
// contains filtered or unexported fields
}
EvalOrchestrator orchestrates eval and assertion execution during Arena runs.
func NewEvalOrchestrator ¶ added in v1.3.20
func NewEvalOrchestrator( registry *evals.EvalTypeRegistry, defs []evals.EvalDef, skipEvals bool, evalTypeFilter []string, taskType string, ) *EvalOrchestrator
NewEvalOrchestrator creates a hook for executing pack evals during Arena runs. If skipEvals is true, the runner is nil and all methods are no-ops. The evalTypeFilter, when non-empty, restricts execution to matching eval types.
func (*EvalOrchestrator) Clone ¶ added in v1.3.20
func (h *EvalOrchestrator) Clone() *EvalOrchestrator
Clone creates a shallow copy suitable for per-run use. The runner and defs are shared (immutable after construction), but metadata and workflow provider are independent. This avoids data races when concurrent runs set different workflow metadata providers.
func (*EvalOrchestrator) HasEvals ¶ added in v1.3.20
func (h *EvalOrchestrator) HasEvals() bool
HasEvals returns true if there are eval defs to execute.
func (*EvalOrchestrator) RunAssertionsAsConversationResults ¶ added in v1.3.20
func (h *EvalOrchestrator) RunAssertionsAsConversationResults( ctx context.Context, assertionConfigs []assertions.AssertionConfig, messages []types.Message, turnIndex int, sessionID string, trigger evals.EvalTrigger, ) []assertions.ConversationValidationResult
RunAssertionsAsConversationResults converts assertion configs to EvalDefs, runs them through the runner, and wraps results in ConversationValidationResult. The results use the original assertion type names (not pack_eval: prefixed).
func (*EvalOrchestrator) RunAssertionsAsEvals ¶ added in v1.3.20
func (h *EvalOrchestrator) RunAssertionsAsEvals( ctx context.Context, assertionConfigs []assertions.AssertionConfig, messages []types.Message, turnIndex int, sessionID string, trigger evals.EvalTrigger, ) []evals.EvalResult
RunAssertionsAsEvals converts assertion configs to EvalDefs and runs them through the runner. Returns raw EvalResults (not converted to assertion format). The trigger parameter overrides the default trigger on each converted def.
Each assertion is converted to an EvalDef with type "assertion", which the runner dispatches to AssertionEvalHandler. The wrapper resolves the inner eval handler from the registry, executes it, and applies min_score/max_score thresholds to determine pass/fail.
func (*EvalOrchestrator) RunConversationEvals ¶ added in v1.3.20
func (h *EvalOrchestrator) RunConversationEvals( ctx context.Context, messages []types.Message, sessionID string, ) []assertions.ConversationValidationResult
RunConversationEvals runs conversation-complete evals after all turns finish. Returns converted ConversationValidationResult entries.
func (*EvalOrchestrator) RunSessionEvals ¶ added in v1.3.20
func (h *EvalOrchestrator) RunSessionEvals( ctx context.Context, messages []types.Message, sessionID string, ) []evals.EvalResult
RunSessionEvals runs session-complete evals after conversation finishes and returns the runtime's raw EvalResult values. Pack-level evals are observations (no pass/fail), so arena passes them through to the report unchanged — same shape that would surface in production. Use RunSessionAssertions for arena-only test assertions that do go through the gating-shaped converter.
func (*EvalOrchestrator) RunTurnEvals ¶ added in v1.3.20
func (h *EvalOrchestrator) RunTurnEvals( ctx context.Context, messages []types.Message, turnIndex int, sessionID string, ) []assertions.ConversationValidationResult
RunTurnEvals runs turn-triggered evals after a turn completes. Returns converted ConversationValidationResult entries.
func (*EvalOrchestrator) SetClassifyRegistry ¶ added in v1.4.10
func (h *EvalOrchestrator) SetClassifyRegistry(r *classify.Registry)
SetClassifyRegistry sets the classify.Registry that will be attached to the context passed to every eval/assertion handler. Handlers that need a classifier (audio_emotion, text_toxicity, ...) read it via classify.FromContext. Safe to call with nil — the context attach is a no-op and FromContext returns nil for handlers to surface their own error.
func (*EvalOrchestrator) SetCompositionMetadataProvider ¶ added in v1.5.0
func (h *EvalOrchestrator) SetCompositionMetadataProvider(provider CompositionMetadataProvider)
SetCompositionMetadataProvider sets the composition observability provider for eval context injection. Called per-run for composition scenarios so composition_* assertions can access per-step outputs/branch/parallel status.
func (*EvalOrchestrator) SetEventBus ¶ added in v1.3.20
func (h *EvalOrchestrator) SetEventBus(bus events.Bus)
SetEventBus configures the event bus for provider call telemetry in eval handlers. When set, an emitter is injected into each EvalContext's metadata so that LLM judge provider calls emit ProviderCallStarted/Completed/Failed events.
func (*EvalOrchestrator) SetMetadata ¶ added in v1.3.20
func (h *EvalOrchestrator) SetMetadata(metadata map[string]any)
SetMetadata sets metadata that will be injected into every EvalContext. Used to pass judge_targets, prompt_registry, and other config to eval handlers.
func (*EvalOrchestrator) SetWorkflowMetadataProvider ¶ added in v1.3.20
func (h *EvalOrchestrator) SetWorkflowMetadataProvider(provider WorkflowMetadataProvider)
SetWorkflowMetadataProvider sets the workflow state provider for eval context injection. Called per-run for workflow scenarios so assertions can access the current workflow state.
type EventBusOption ¶ added in v1.3.22
type EventBusOption func(*eventBusConfig)
EventBusOption configures optional behavior when setting the event bus.
func WithMessageEvents ¶ added in v1.3.22
func WithMessageEvents() EventBusOption
WithMessageEvents enables RecordingStage in pipelines so message.created events are published to the event bus. This does NOT write session recordings to disk — use EnableSessionRecording for that.
type InteractiveSession ¶ added in v1.5.2
type InteractiveSession struct {
// contains filtered or unexported fields
}
InteractiveSession drives one user turn at a time against a chosen agent and provider, persisting history under a fresh conversation ID. Guardrails fire automatically via the provider pipeline; assertions are never populated.
func (*InteractiveSession) ConversationID ¶ added in v1.5.2
func (s *InteractiveSession) ConversationID() string
ConversationID returns this session's persistence key.
func (*InteractiveSession) Messages ¶ added in v1.5.2
Messages loads the full transcript from the state store.
func (*InteractiveSession) Provider ¶ added in v1.5.3
func (s *InteractiveSession) Provider() providers.Provider
Provider returns the resolved provider for this session. Used by the voice chat path to obtain the provider handle without a separate registry lookup.
func (*InteractiveSession) RunEvals ¶ added in v1.5.2
func (s *InteractiveSession) RunEvals(ctx context.Context) ([]evals.EvalResult, error)
RunEvals runs the config's evals against the current transcript and returns their raw scores. Returns nil when eval scoring is disabled for the session or the config declares no evals. Evals are pure primitives (no pass/fail).
func (*InteractiveSession) RunEvalsEnabled ¶ added in v1.5.2
func (s *InteractiveSession) RunEvalsEnabled() bool
RunEvalsEnabled reports whether eval scoring is on for this session.
func (*InteractiveSession) SendUserMessage ¶ added in v1.5.2
func (s *InteractiveSession) SendUserMessage( ctx context.Context, text string, ) (<-chan turnexecutors.MessageStreamChunk, error)
SendUserMessage drives one live user turn and returns the assistant stream. Assertions are intentionally left nil so the assertion stage never runs; guardrails still fire inside the provider pipeline.
type InteractiveSessionOptions ¶ added in v1.5.2
type InteractiveSessionOptions struct {
ProviderID string // provider to chat against
TaskType string // agent (prompt config) task_type
Variables map[string]string // user-supplied template variables
RunEvals bool // when true, run config evals per turn for scores
}
InteractiveSessionOptions configures a live chat session.
type MediaOutput ¶ added in v1.1.0
type MediaOutput struct {
Type string `json:"type"` // "image", "audio", "video"
MIMEType string `json:"mime_type"` // e.g., "image/jpeg", "audio/mp3"
SizeBytes int64 `json:"size_bytes"` // Size of the media file
Duration *int `json:"duration,omitempty"` // Duration in seconds (for audio/video)
Width *int `json:"width,omitempty"` // Width in pixels (for image/video)
Height *int `json:"height,omitempty"` // Height in pixels (for image/video)
FilePath string `json:"file_path,omitempty"` // Path where media was saved
Thumbnail string `json:"thumbnail,omitempty"` // Base64-encoded thumbnail for HTML reports
MessageIdx int `json:"message_index"` // Index of message containing this media
PartIdx int `json:"part_index"` // Index of content part within the message
}
MediaOutput represents media content generated by an LLM during test execution
func CollectMediaOutputs ¶ added in v1.1.0
func CollectMediaOutputs(messages []types.Message) []MediaOutput
CollectMediaOutputs extracts media outputs from conversation messages Returns a slice of MediaOutput for tracking in RunResult
type MediaOutputStats ¶ added in v1.1.0
type MediaOutputStats struct {
Total int `json:"total"`
ImageCount int `json:"image_count"`
AudioCount int `json:"audio_count"`
VideoCount int `json:"video_count"`
TotalSizeBytes int64 `json:"total_size_bytes"`
ByType map[string]int `json:"by_type"`
}
MediaOutputStats contains summary statistics for media outputs
func GetMediaOutputStatistics ¶ added in v1.1.0
func GetMediaOutputStatistics(outputs []MediaOutput) MediaOutputStats
GetMediaOutputStatistics calculates summary statistics for media outputs
type PerturbationVariant ¶ added in v1.3.10
type PerturbationVariant struct {
// Substitutions maps placeholder names to their values for this variant.
Substitutions map[string]string
}
PerturbationVariant represents a single set of variable substitutions.
func ExpandPerturbations ¶ added in v1.3.10
func ExpandPerturbations(scenario *config.Scenario) []PerturbationVariant
ExpandPerturbations computes all perturbation variants for a scenario. It collects all perturbation maps across turns and computes the Cartesian product. Returns nil if no perturbations are defined.
type ProviderInfo ¶ added in v1.4.7
type ProviderInfo struct {
ID string `json:"id"`
Type string `json:"type"`
Model string `json:"model,omitempty"`
}
ProviderInfo is a lightweight view of a loaded provider for picker UIs.
type RunCombination ¶
type RunCombination struct {
Region string
ScenarioID string // For scenario-based runs
EvalID string // For eval-based runs (mutually exclusive with ScenarioID)
ProviderID string // Not used for eval runs (provider comes from recording)
RecordingRef string // For batch evals: specific recording reference ID (resolved by adapter)
TrialIndex int // Trial number (0-based) when scenario has Trials > 1
TotalTrials int // Total number of trials for this scenario (0 or 1 = single run)
// PerturbationIndex identifies which perturbation variant this run uses (-1 = no perturbation).
PerturbationIndex int
}
RunCombination represents a single test execution
type RunCompletedHook ¶ added in v1.4.7
RunCompletedHook fires after each individual run finishes (success or failure). Used by the web server to persist completed runs to disk as soon as they're done, so killing the server mid-batch doesn't strand completed runs in the in-memory state store with no on-disk record.
runID is the identifier of the run that just completed. err is the run's error, or nil on success. Hooks should not retain runID across calls — the next run uses a fresh ID.
type RunPlan ¶
type RunPlan struct {
Combinations []RunCombination
}
RunPlan defines the test execution plan
type RunResult ¶
type RunResult struct {
RunID string `json:"RunID"`
PromptPack string `json:"PromptPack"`
Region string `json:"Region"`
ScenarioID string `json:"ScenarioID"`
ProviderID string `json:"ProviderID"`
// Labels are stratification tags copied from the scenario manifest's
// metadata.labels. Carried through to JSON/HTML reports for slicing.
Labels map[string]string `json:"Labels,omitempty"`
Params map[string]interface{} `json:"Params"`
Messages []types.Message `json:"Messages"`
Commit map[string]interface{} `json:"Commit"`
Cost types.CostInfo `json:"Cost"`
ToolStats *types.ToolStats `json:"ToolStats"`
Violations []types.ValidationError `json:"Violations"`
StartTime time.Time `json:"StartTime"`
EndTime time.Time `json:"EndTime"`
Duration time.Duration `json:"Duration"`
Error string `json:"Error"`
Skipped bool `json:"Skipped,omitempty"`
SkipReason string `json:"SkipReason,omitempty"`
SelfPlay bool `json:"SelfPlay"`
PersonaID string `json:"PersonaID"`
UserFeedback *statestore.Feedback `json:"UserFeedback"`
SessionTags []string `json:"SessionTags"`
AssistantRole *SelfPlayRoleInfo `json:"AssistantRole"`
UserRole *SelfPlayRoleInfo `json:"UserRole"`
// Media outputs generated by LLMs during test execution
MediaOutputs []MediaOutput `json:"MediaOutputs,omitempty"`
// Session recording path (if recording was enabled)
RecordingPath string `json:"RecordingPath,omitempty"`
// Conversation-level assertions evaluated after the conversation completes (summary format)
ConversationAssertions AssertionsSummary `json:"conversation_assertions,omitempty"`
// EvalResults are the runtime's pack-level eval observations
// captured during the session — same machinery that fires in
// production. These don't carry pass/fail; they carry structured
// Value/Details for jq aggregation. Distinct channel from
// ConversationAssertions so the report can render observations
// without the gating ceremony.
EvalResults []evals.EvalResult `json:"eval_results,omitempty"`
// A2A agent metadata (populated from config for report rendering)
A2AAgents []A2AAgentInfo `json:"A2AAgents,omitempty"`
// TrialResults holds aggregated statistics when a scenario is run with Trials > 1.
TrialResults *TrialResults `json:"trial_results,omitempty"`
}
RunResult contains the complete results of a single test execution
type ScenarioInfo ¶ added in v1.4.7
type ScenarioInfo struct {
ID string `json:"id"`
Description string `json:"description,omitempty"`
}
ScenarioInfo is a lightweight view of a loaded scenario for picker UIs.
type SelfPlayRoleInfo ¶
SelfPlayRoleInfo contains provider information for self-play roles
type SkillFilterer ¶ added in v1.4.5
SkillFilterer controls which skills are available based on workflow state.
type StateStoreConfig ¶
type StateStoreConfig struct {
Store interface{} // State store implementation (statestore.Store)
UserID string // User identifier (optional)
Metadata map[string]interface{} // Additional metadata to store (optional)
}
StateStoreConfig wraps the pipeline StateStore configuration for Arena
type TrialGroupKey ¶ added in v1.3.10
TrialGroupKey identifies a unique scenario+provider+region combination for trial grouping.
type TrialResults ¶ added in v1.3.10
type TrialResults struct {
// TrialCount is the number of trials executed.
TrialCount int `json:"trial_count"`
// PassRate is the fraction of trials where all assertions passed (0.0-1.0).
PassRate float64 `json:"pass_rate"`
// FlakinessScore ranges from 0 (deterministic) to 1 (maximally flaky, 50/50).
FlakinessScore float64 `json:"flakiness_score"`
// PerAssertionStats maps assertion ID to its pass rate across trials.
PerAssertionStats map[string]AssertionTrialStats `json:"per_assertion_stats,omitempty"`
}
TrialResults holds aggregated results from multiple trial executions of a scenario.
type WorkflowMetadataProvider ¶ added in v1.3.20
WorkflowMetadataProvider is implemented by types that provide workflow state metadata for injection into the eval context during assertion evaluation.
Source Files
¶
- builder_integration.go
- classify_registry.go
- composite_conversation_executor.go
- conversation_executor.go
- cost_aggregation.go
- duplex_conversation_executor.go
- duplex_executor_assertions_integration.go
- duplex_executor_events_integration.go
- duplex_executor_pipeline_integration.go
- duplex_executor_results_integration.go
- duplex_executor_tools_integration.go
- duplex_executor_turns_integration.go
- duplex_executor_types.go
- duplex_interactive.go
- duplex_tts_streaming.go
- engine.go
- engine_audio.go
- eval_conversation_executor.go
- eval_orchestrator.go
- execution.go
- execution_mcpsource.go
- execution_memory_integration.go
- execution_workflow_integration.go
- interactive.go
- interfaces.go
- mcpsource_scope.go
- media_collector.go
- perturbations.go
- picker_integration.go
- runtime_hooks.go
- session_hook_context.go
- trials.go
- types.go
- workflow_events.go
- workflow_tool_executor.go