Harness Observability: Structured Output, Events, Testkit
July 4, 2026 ยท View on GitHub
Continues from README.md and runtime.md:
structured output, events and streaming, errors, testkit, and
implementation milestones.
Structured Output
Structured output should support:
- provider-native schema mode
- tool-call fallback mode
- JSON parsing mode for simple local models
pub enum ResponseFormat {
Text,
JsonSchema(JsonSchema),
ProviderNative(JsonSchema),
ToolStrategy { tool_name: String, schema: JsonSchema },
}
The final run result should keep messages and structured output separate:
pub struct AgentRun<State, Output = ()> {
pub state: State,
pub messages: Vec<Message>,
pub structured_response: Option<Output>,
pub events: Vec<AgentEvent>,
}
Events And Streaming
The harness event stream should be typed, not a string callback.
pub enum AgentEvent {
RunStarted { run_id: RunId, thread_id: Option<ThreadId> },
ModelStarted { call_id: CallId, model: ModelName },
ModelDelta { call_id: CallId, delta: MessageDelta },
ModelCompleted { call_id: CallId, started_at_ms: Option<u64>, usage: Option<Usage> },
ToolStarted { call_id: CallId, tool_name: ToolName },
ToolDelta { call_id: CallId, delta: ToolDelta },
ToolCompleted { call_id: CallId, tool_name: ToolName, started_at_ms: Option<u64> },
MiddlewareStarted { name: String },
MiddlewareCompleted { name: String },
RetryScheduled { call_id: CallId, attempt: usize },
Custom { name: String, payload: serde_json::Value },
RunCompleted { run_id: RunId },
RunFailed { run_id: RunId, error: String },
}
Streaming modes:
messages: model deltas and final messagestools: tool start, progress, resultupdates: state or memory updatesevents: all low-level eventsfinal: final output only
Errors
Harness errors should distinguish:
- invalid request
- missing model
- missing tool
- invalid tool schema
- invalid tool arguments
- provider authentication failure
- provider rate limit
- provider server error
- timeout
- retry exhausted
- structured output validation failure
- middleware failure
- memory failure
Retry policy should only retry explicitly retryable classes by default:
- network interruption
- timeout
- rate limit
- provider 5xx
Do not retry authentication, schema, malformed request, or missing tool errors unless a user explicitly overrides policy.
Testkit
harness::testkit should be part of the early API.
Utilities:
FakeChatModelScriptedChatModelFakeStreamingModelFakeToolInMemoryShortTermMemoryInMemoryStoreEventRecorder- deterministic ids
- deterministic clock
- trajectory assertions
Example trajectory assertion:
assert_trajectory(run.events())
.model_called("default")
.tool_called("lookup_user")
.model_called("default")
.completed();
Implementation Milestones
H1: Current Minimal Traits
- Keep
ChatMessage. - Keep
ChatModel. - Keep
Tool. - Add better tool call ids.
H2: Registries And Context
- Add
ModelRegistry. - Add
ToolRegistry. - Add
RunConfig. - Add
RunContext.
H3: Agent Loop
- Implement model-tool loop.
- Enforce limits.
- Add fake model and fake tool tests.
H4: Middleware And Events
- Add middleware stack.
- Add typed events.
- Add event recorder.
H5: Memory And Structured Output
- Add short-term memory trait.
- Add store trait.
- Add structured response format.
H6: Providers
- Add feature-gated provider crates or modules.
- Start with mock and one hosted provider.