Skip to main content

PromptSource

Trait PromptSource 

Source
pub trait PromptSource:
    Send
    + Sync
    + Debug {
    // Required methods
    fn load<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 self,
        name: &'life1 PromptName,
        selector: &'life2 PromptSelector,
    ) -> Pin<Box<dyn Future<Output = Result<LoadedPrompt, PromptError>> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             Self: 'async_trait;
    fn describe(&self) -> &'static str;
}
Expand description

Where prompt text comes from.

§The obligation

An implementation owes exactly one thing beyond returning text:

The text it returns must be the text whose hash the reference carries.

Everything downstream rests on that. The replay record stores the reference and not the text, so an auditor answering “which prompt produced this turn” is trusting the digest; a source that returns one text and a reference to another turns the whole record into decoration. Returning LoadedPrompt::new discharges the obligation by construction, and LoadedPrompt::from_parts checks it when a reference arrives from somewhere else.

Two smaller expectations follow from it:

§Object safety

The trait is dyn-compatible through async_trait, because the runtime holds one as Arc<dyn PromptSource> and because a cache decorates an arbitrary one. Implementations are Send + Sync for the same reason.

use std::sync::Arc;
use turnframe_core::prompt::{
    LoadedPrompt, PromptError, PromptName, PromptSelector, PromptSource,
};

#[derive(Debug)]
struct OnePrompt(&'static str);

#[async_trait::async_trait]
impl PromptSource for OnePrompt {
    async fn load(
        &self,
        name: &PromptName,
        _selector: &PromptSelector,
    ) -> Result<LoadedPrompt, PromptError> {
        Ok(LoadedPrompt::new(name.clone(), "v1", self.0))
    }

    fn describe(&self) -> &'static str {
        "one-prompt"
    }
}

// The coercion is the assertion: a source is usable behind a shared
// pointer, which is how the runtime holds one.
let shared: Arc<dyn PromptSource> = Arc::new(OnePrompt("Say hello."));
assert_eq!(shared.describe(), "one-prompt");

let name = PromptName::from("greeting");
let loading = shared.load(&name, &PromptSelector::Latest);
// Awaiting it needs an executor; that the future exists at all is what
// dyn-compatibility buys.
drop(loading);

Required Methods§

Source

fn load<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, name: &'life1 PromptName, selector: &'life2 PromptSelector, ) -> Pin<Box<dyn Future<Output = Result<LoadedPrompt, PromptError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, Self: 'async_trait,

Loads one prompt.

§Errors

See PromptError. A source that reaches a network reports a failure to reach it as PromptError::Transport, so a caller can tell “this prompt does not exist” from “this registry is down”.

Source

fn describe(&self) -> &'static str

A short stable label naming the kind of source, for logs and metrics.

"file", "cache", "langfuse". Never a URL and never a credential.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§