agent-feedback crate provides a cloneable Tower Layer and Service for Axum. Finite JSON object and HTML responses are instrumented in-place; responses without an exact bounded Content-Length — including streams — are left completely untouched. Telemetry is dispatched through a bounded Tokio mpsc channel and never blocks the product response.
Requirements
- Rust 1.88 or later
- Axum 0.8
- Tower 0.5
- A Tokio async runtime
Installation
1
Add the crate
2
Create a product key
Open the Agent Feedback dashboard, create a product, and copy the API key. Store it as
AGENT_FEEDBACK_KEY in your environment.Creating the layer
CallAgentFeedbackLayer::new with an Options value built using the builder API. Clone the layer freely — all clones share the same telemetry queue.
Options builder methods
Construct anOptions value using Options::new(api_key) and chain the builder methods below. Only the API key is required.
impl Into<String>
required
Constructs a new
Options value with the given API key. The key must match the pattern af_live_<keyId>_<secret>. AgentFeedbackLayer::new returns an error if the key format is invalid.impl Into<String>
Override the Agent Feedback API endpoint. Trailing slashes are trimmed. Defaults to
https://agent-feedback-api-production.up.railway.app.impl IntoIterator<Item = impl Into<String>>
Glob-style path patterns that opt routes in. When empty, all routes are instrumented (subject to default and custom excludes). Supports
* (single segment) and ** (any path). Example: .include(["/search", "/docs/**"]).impl IntoIterator<Item = impl Into<String>>
Glob-style patterns to skip. The default exclusion list (
/health, /healthz, /metrics, /favicon.ico, /robots.txt, /_agent-feedback/*, /api/v2/outcomes) is always applied. Patterns you provide here extend that list.impl Fn(&Request<Body>) -> Option<String> + Send + Sync + 'static
Closure that extracts an opaque customer identifier from the request. Used to group interactions by account in the dashboard.
impl Fn(&Request<Body>) -> Option<String> + Send + Sync + 'static
Closure that extracts a session identifier from the request. Links a sequence of interactions for session-level analytics.
impl Fn(&Request<Body>) -> Option<String> + Send + Sync + 'static
Closure that returns a hint about the agent runtime. Used for runtime-level breakdown in the dashboard.
FeedbackMode
Set feedback_mode directly on the Options struct (it is a public field). The enum has three variants:
Stream detection
The layer reads the response body size hint from thehttp_body::Body trait before buffering. Responses without an exact bounded size — including streams — bypass instrumentation entirely and are forwarded untouched to the client. The body size cap is 1 MiB; responses larger than this limit are also forwarded without modification.
Graceful shutdown
Callfeedback.shutdown().await at application shutdown. The method sends a shutdown signal to the Tokio telemetry worker and waits up to 2 seconds for in-flight events to flush.
Agent helper functions
The crate exportsfeedback_from_response and submit_product_outcome for building feedback-aware agent runtimes.
feedback_from_response
Extracts the Envelope from a response’s HeaderMap and body bytes. Checks the JSON body for _agentFeedback, then the HTML body for <script id="agent-feedback">, then the agent-feedback response header.
submit_product_outcome
Submits a compact outcome review to the URL embedded in the envelope. Validates the contract, enforces the allowed-origin allow-list, and returns a serde_json::Value with the API response.
submit_product_outcome returns a SubmitError::UntrustedOrigin error if the submit URL’s origin is not in the allowed list. Pass a non-empty slice only when submitting to a self-hosted endpoint.