Skip to main content

agent_block_testkit/
lib.rs

1//! In-process mock LLM servers and a provider wire-shape catalog for testing
2//! tool-calling clients.
3//!
4//! Two pieces:
5//!
6//! - [`shapes`] — builders for provider response bodies. Alongside the
7//!   spec-conformant shapes, the catalog carries **deliberately broken
8//!   variants observed in the wild** on OpenAI-compatible stacks (object
9//!   `arguments`, missing `id`, malformed argument strings). A client that is
10//!   only exercised against spec-shaped input silently loses tolerance for
11//!   the shapes real serving stacks emit; the broken variants exist to guard
12//!   exactly that failure mode.
13//! - [`server`] — a small axum-backed mock server ([`server::MockLlm`]) that
14//!   serves an Anthropic Messages (`/v1/messages`) or OpenAI Chat Completions
15//!   (`/chat/completions`) endpoint from a caller-supplied responder closure,
16//!   while recording request facts (tool declarations, `tool_call_id`s of
17//!   returned tool results) for assertions.
18//!
19//! ```no_run
20//! use agent_block_testkit::server::MockLlm;
21//! use agent_block_testkit::shapes::openai;
22//! use serde_json::json;
23//!
24//! # async fn demo() {
25//! let handle = MockLlm::openai(|req| {
26//!     if !req.has_tool_results {
27//!         // Broken-but-observed shape: object arguments, no id.
28//!         openai::tool_calls_response(vec![openai::tool_call_object_args_no_id(
29//!             "apply_search_replace",
30//!             json!({"path": "/tmp/f.lua", "search": "a", "replace": "b"}),
31//!         )])
32//!     } else {
33//!         openai::text_response("DONE")
34//!     }
35//! })
36//! .spawn()
37//! .await;
38//! // point the client under test at handle.base_url ...
39//! assert_eq!(handle.state.call_count(), 0);
40//! handle.ct.cancel();
41//! # }
42//! ```
43
44pub mod server;
45pub mod shapes;