use std::sync::mpsc::Receiver;
use anyhow::Result;
use crate::{CompletionRequest, CompletionResponse, ModelAdapter, Role, StreamChunk};
#[derive(Debug, Default, Clone, Copy)]
pub struct EchoAdapter;
impl ModelAdapter for EchoAdapter {
fn name(&self) -> &str {
"echo"
}
fn complete(&self, request: &CompletionRequest) -> Result<CompletionResponse> {
let content = request
.messages
.iter()
.rev()
.find(|m| m.role == Role::User)
.map(|m| m.content.clone())
.unwrap_or_default();
Ok(CompletionResponse { content, model: self.name().to_string() })
}
fn stream(&self, request: &CompletionRequest) -> Receiver<StreamChunk> {
let (tx, rx) = std::sync::mpsc::channel();
let content = request
.messages
.iter()
.rev()
.find(|m| m.role == Role::User)
.map(|m| m.content.clone())
.unwrap_or_default();
std::thread::spawn(move || {
for piece in content.split_inclusive(char::is_whitespace) {
if tx.send(StreamChunk::Token(piece.to_string())).is_err() {
return;
}
}
let _ = tx.send(StreamChunk::Done);
});
rx
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Message;
#[test]
fn echoes_the_last_user_message() {
let request = CompletionRequest::new([
Message::system("you are a helpful assistant"),
Message::user("first"),
Message::assistant("ack"),
Message::user("second"),
]);
let response = EchoAdapter.complete(&request).unwrap();
assert_eq!(response.content, "second");
assert_eq!(response.model, "echo");
}
#[test]
fn empty_when_there_is_no_user_message() {
let request = CompletionRequest::new([Message::system("system only")]);
let response = EchoAdapter.complete(&request).unwrap();
assert_eq!(response.content, "");
}
#[test]
fn stream_yields_tokens_incrementally_then_done() {
let request = CompletionRequest::new([Message::user("one two three")]);
let chunks: Vec<StreamChunk> = EchoAdapter.stream(&request).iter().collect();
let tokens: Vec<&str> = chunks
.iter()
.filter_map(|c| match c {
StreamChunk::Token(t) => Some(t.as_str()),
_ => None,
})
.collect();
assert!(tokens.len() > 1, "expected multiple Token chunks, got {tokens:?}");
assert_eq!(tokens.concat(), "one two three");
assert_eq!(chunks.last(), Some(&StreamChunk::Done));
assert_eq!(chunks.iter().filter(|c| **c == StreamChunk::Done).count(), 1);
assert!(!chunks.iter().any(|c| matches!(c, StreamChunk::Error(_))));
}
#[test]
fn stream_of_a_single_word_is_one_token_then_done() {
let request = CompletionRequest::new([Message::user("solo")]);
let chunks: Vec<StreamChunk> = EchoAdapter.stream(&request).iter().collect();
assert_eq!(chunks, vec![StreamChunk::Token("solo".to_string()), StreamChunk::Done]);
}
}