pub struct FakeProvider { /* private fields */ }Expand description
A stand-in for a real provider, for use in tests.
Register it into a Registry under a normal provider name
(e.g. "openai") and it behaves like the real thing as far as your
application code can tell – except instead of making a network call, it
hands back canned responses you scripted ahead of time with
respond_with, and records every request it received
so you can assert on what your code actually sent.
§Example
use llmprism::testing::{FakeProvider, FakeTextResponse};
use llmprism::Registry;
let fake = FakeProvider::new("openai").respond_with(FakeTextResponse::new("Hello!"));
let mut registry = Registry::new();
registry.register("openai", fake);
// Code under test just calls `registry.text("openai", ...)` as normal, with
// no idea it's talking to a fake.
let response = registry.text("openai", "gpt-4o-mini").unwrap()
.with_prompt("hi")
.generate()
.await
.unwrap();
assert_eq!(response.text.as_deref(), Some("Hello!"));The same scripted responses work for stream
too – FakeProvider synthesizes StreamEvents from the canned Step
instead of needing a second, stream-shaped fixture format. By default it
yields one word per StreamEvent::TextDelta; change that with
with_stream_chunk_words.
Every capability keeps its own independent queue (text, structured output,
moderation, and so on) – a test exercising more than one capability
against the same fake scripts each separately, e.g.
respond_with_structured alongside
respond_with.
Implementations§
Source§impl FakeProvider
impl FakeProvider
Sourcepub fn new(name: impl Into<String>) -> Self
pub fn new(name: impl Into<String>) -> Self
Creates a fake provider that will report name from Provider::name,
with no canned responses queued yet.
Sourcepub fn respond_with(self, step: impl Into<Step>) -> Self
pub fn respond_with(self, step: impl Into<Step>) -> Self
Queues one canned response, to be returned the next time this provider is called. Call this once per request/response round trip you expect your test to trigger – for example, twice if you’re testing a single tool call followed by a final reply.
Sourcepub fn respond_with_structured(
self,
response: impl Into<StructuredResponse>,
) -> Self
pub fn respond_with_structured( self, response: impl Into<StructuredResponse>, ) -> Self
Queues one canned structured-output response, to be returned the next
time this provider receives a structured request. Kept as a separate
queue from respond_with since a structured
response is a different shape (StructuredResponse, not Step) –
a test that exercises both capabilities scripts each independently.
Sourcepub fn respond_with_moderation(
self,
response: impl Into<ModerationResponse>,
) -> Self
pub fn respond_with_moderation( self, response: impl Into<ModerationResponse>, ) -> Self
Queues one canned moderation response, to be returned the next time this provider receives a moderation request.
Sourcepub fn respond_with_embeddings(
self,
response: impl Into<EmbeddingsResponse>,
) -> Self
pub fn respond_with_embeddings( self, response: impl Into<EmbeddingsResponse>, ) -> Self
Queues one canned embeddings response, to be returned the next time this provider receives an embeddings request.
Sourcepub fn respond_with_rerank(self, response: impl Into<RerankResponse>) -> Self
pub fn respond_with_rerank(self, response: impl Into<RerankResponse>) -> Self
Queues one canned rerank response, to be returned the next time this provider receives a rerank request.
Sourcepub fn respond_with_images(self, response: impl Into<ImagesResponse>) -> Self
pub fn respond_with_images(self, response: impl Into<ImagesResponse>) -> Self
Queues one canned image-generation response, to be returned the next time this provider receives an images request.
Sourcepub fn respond_with_audio(self, response: impl Into<AudioResponse>) -> Self
pub fn respond_with_audio(self, response: impl Into<AudioResponse>) -> Self
Queues one canned text-to-speech response, to be returned the next time this provider receives a text-to-speech request.
Sourcepub fn respond_with_transcription(
self,
response: impl Into<TranscriptionResponse>,
) -> Self
pub fn respond_with_transcription( self, response: impl Into<TranscriptionResponse>, ) -> Self
Queues one canned speech-to-text response, to be returned the next time this provider receives a speech-to-text request.
Sourcepub fn with_stream_chunk_words(self, words: usize) -> Self
pub fn with_stream_chunk_words(self, words: usize) -> Self
Controls how many words stream_text_once
groups into each StreamEvent::TextDelta it synthesizes from a canned
response’s text. Defaults to one word per delta; raise it if a test wants
fewer, larger chunks.
Sourcepub fn recorded_requests(&self) -> Vec<TextRequest>
pub fn recorded_requests(&self) -> Vec<TextRequest>
Returns every request this provider actually received, in order – useful for asserting on exactly what your code sent (which model, which messages, which tools, and so on).
Sourcepub fn recorded_structured_requests(&self) -> Vec<StructuredRequest>
pub fn recorded_structured_requests(&self) -> Vec<StructuredRequest>
Returns every structured request this provider actually received, in
order – the structured-output counterpart to
recorded_requests.
Sourcepub fn recorded_moderation_requests(&self) -> Vec<ModerationRequest>
pub fn recorded_moderation_requests(&self) -> Vec<ModerationRequest>
Returns every moderation request this provider actually received, in order.
Sourcepub fn recorded_embeddings_requests(&self) -> Vec<EmbeddingsRequest>
pub fn recorded_embeddings_requests(&self) -> Vec<EmbeddingsRequest>
Returns every embeddings request this provider actually received, in order.
Sourcepub fn recorded_rerank_requests(&self) -> Vec<RerankRequest>
pub fn recorded_rerank_requests(&self) -> Vec<RerankRequest>
Returns every rerank request this provider actually received, in order.
Sourcepub fn recorded_images_requests(&self) -> Vec<ImagesRequest>
pub fn recorded_images_requests(&self) -> Vec<ImagesRequest>
Returns every images request this provider actually received, in order.
Sourcepub fn recorded_text_to_speech_requests(&self) -> Vec<TextToSpeechRequest>
pub fn recorded_text_to_speech_requests(&self) -> Vec<TextToSpeechRequest>
Returns every text-to-speech request this provider actually received, in order.
Sourcepub fn recorded_speech_to_text_requests(&self) -> Vec<SpeechToTextRequest>
pub fn recorded_speech_to_text_requests(&self) -> Vec<SpeechToTextRequest>
Returns every speech-to-text request this provider actually received, in order.
Trait Implementations§
Source§impl Provider for FakeProvider
impl Provider for FakeProvider
Source§fn name(&self) -> &str
fn name(&self) -> &str
Registry::text).Source§fn text_step<'life0, 'life1, 'async_trait>(
&'life0 self,
request: &'life1 TextRequest,
) -> Pin<Box<dyn Future<Output = Result<Step, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn text_step<'life0, 'life1, 'async_trait>(
&'life0 self,
request: &'life1 TextRequest,
) -> Pin<Box<dyn Future<Output = Result<Step, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn stream_text_once<'life0, 'life1, 'async_trait>(
&'life0 self,
request: &'life1 TextRequest,
) -> Pin<Box<dyn Future<Output = Result<BoxStream<'static, Result<StreamEvent, Error>>, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn stream_text_once<'life0, 'life1, 'async_trait>(
&'life0 self,
request: &'life1 TextRequest,
) -> Pin<Box<dyn Future<Output = Result<BoxStream<'static, Result<StreamEvent, Error>>, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
text_step: performs
exactly one request/response round trip, but instead of waiting for the
whole reply, returns a stream that yields StreamEvents as they arrive. Read moreSource§fn structured<'life0, 'async_trait>(
&'life0 self,
request: StructuredRequest,
) -> Pin<Box<dyn Future<Output = Result<StructuredResponse, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn structured<'life0, 'async_trait>(
&'life0 self,
request: StructuredRequest,
) -> Pin<Box<dyn Future<Output = Result<StructuredResponse, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn stream_structured_once<'life0, 'life1, 'async_trait>(
&'life0 self,
request: &'life1 StructuredRequest,
) -> Pin<Box<dyn Future<Output = Result<BoxStream<'static, Result<StructuredStreamEvent, Error>>, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn stream_structured_once<'life0, 'life1, 'async_trait>(
&'life0 self,
request: &'life1 StructuredRequest,
) -> Pin<Box<dyn Future<Output = Result<BoxStream<'static, Result<StructuredStreamEvent, Error>>, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
structured: instead
of waiting for the whole reply, returns a stream that yields
StructuredStreamEvent::PartialObjects as the model’s JSON output
arrives, ending with exactly one
StructuredStreamEvent::End. Like structured, this is always
exactly one round trip – there’s no separate loop layer above it. Read moreSource§fn moderation<'life0, 'async_trait>(
&'life0 self,
request: ModerationRequest,
) -> Pin<Box<dyn Future<Output = Result<ModerationResponse, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn moderation<'life0, 'async_trait>(
&'life0 self,
request: ModerationRequest,
) -> Pin<Box<dyn Future<Output = Result<ModerationResponse, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Error::Unsupported like every
other optional capability here.Source§fn embeddings<'life0, 'async_trait>(
&'life0 self,
request: EmbeddingsRequest,
) -> Pin<Box<dyn Future<Output = Result<EmbeddingsResponse, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn embeddings<'life0, 'async_trait>(
&'life0 self,
request: EmbeddingsRequest,
) -> Pin<Box<dyn Future<Output = Result<EmbeddingsResponse, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn rerank<'life0, 'async_trait>(
&'life0 self,
request: RerankRequest,
) -> Pin<Box<dyn Future<Output = Result<RerankResponse, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn rerank<'life0, 'async_trait>(
&'life0 self,
request: RerankRequest,
) -> Pin<Box<dyn Future<Output = Result<RerankResponse, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
crate::rerank for how this differs from embeddings.