use async_trait::async_trait;
pub struct InferResult {
pub text: String,
pub tokens_in: u32,
pub tokens_out: u32,
}
#[async_trait]
pub trait InferBackend: Send + Sync {
async fn infer(
&self,
prompt: &str,
max_tokens: u32,
on_token: &mut (dyn for<'t> FnMut(&'t str) -> bool + Send),
) -> anyhow::Result<InferResult>;
fn model_name(&self) -> String;
}
pub struct StubBackend {
pub reply: String,
}
impl Default for StubBackend {
fn default() -> Self {
Self {
reply: "a stub summary".into(),
}
}
}
#[async_trait]
impl InferBackend for StubBackend {
async fn infer(
&self,
prompt: &str,
max_tokens: u32,
on_token: &mut (dyn for<'t> FnMut(&'t str) -> bool + Send),
) -> anyhow::Result<InferResult> {
let mut out = String::new();
let mut tokens_out = 0u32;
for word in self.reply.split_whitespace().take(max_tokens as usize) {
let piece = if out.is_empty() {
word.to_string()
} else {
format!(" {word}")
};
tokens_out += 1;
let keep_going = on_token(&piece);
out.push_str(&piece);
if !keep_going {
break;
}
tokio::task::yield_now().await;
}
Ok(InferResult {
text: out,
tokens_in: prompt.split_whitespace().count() as u32,
tokens_out,
})
}
fn model_name(&self) -> String {
"stub".into()
}
}