1use async_trait::async_trait;
2use autoagents_llm::chat::{ChatMessage, ChatProvider, ChatResponse, StructuredOutputFormat, Tool};
3use autoagents_llm::completion::{CompletionProvider, CompletionRequest, CompletionResponse};
4use autoagents_llm::embedding::EmbeddingProvider;
5use autoagents_llm::error::LLMError;
6use autoagents_llm::models::ModelsProvider;
7use autoagents_llm::{HasConfig, LLMProvider, NoConfig};
8use std::fmt;
9
10#[derive(Debug, Default)]
11pub struct EchoProvider;
12
13#[async_trait]
14impl ChatProvider for EchoProvider {
15 async fn chat_with_tools(
16 &self,
17 messages: &[ChatMessage],
18 _tools: Option<&[Tool]>,
19 _json_schema: Option<StructuredOutputFormat>,
20 ) -> Result<Box<dyn ChatResponse>, LLMError> {
21 Ok(Box::new(EchoResponse {
22 text: messages
23 .last()
24 .map(|message| message.content.clone())
25 .unwrap_or_default(),
26 }))
27 }
28}
29
30#[async_trait]
31impl CompletionProvider for EchoProvider {
32 async fn complete(
33 &self,
34 request: &CompletionRequest,
35 _json_schema: Option<StructuredOutputFormat>,
36 ) -> Result<CompletionResponse, LLMError> {
37 Ok(CompletionResponse {
38 text: request.prompt.clone(),
39 })
40 }
41}
42
43#[async_trait]
44impl EmbeddingProvider for EchoProvider {
45 async fn embed(&self, input: Vec<String>) -> Result<Vec<Vec<f32>>, LLMError> {
46 Ok(input
47 .into_iter()
48 .map(|text| vec![text.len() as f32])
49 .collect())
50 }
51}
52
53impl ModelsProvider for EchoProvider {}
54
55impl LLMProvider for EchoProvider {}
56
57impl HasConfig for EchoProvider {
58 type Config = NoConfig;
59}
60
61#[derive(Debug)]
62struct EchoResponse {
63 text: String,
64}
65
66impl ChatResponse for EchoResponse {
67 fn text(&self) -> Option<String> {
68 Some(self.text.clone())
69 }
70
71 fn tool_calls(&self) -> Option<Vec<autoagents_llm::ToolCall>> {
72 None
73 }
74}
75
76impl fmt::Display for EchoResponse {
77 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
78 formatter.write_str(&self.text)
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[tokio::test]
87 async fn echo_completion_and_embedding_are_deterministic() {
88 let provider = EchoProvider;
89 let completion = provider
90 .complete(&CompletionRequest::new("hello"), None)
91 .await
92 .expect("completion");
93 let embeddings = provider
94 .embed(vec!["abc".to_string(), "abcd".to_string()])
95 .await
96 .expect("embeddings");
97
98 assert_eq!(completion.text, "hello");
99 assert_eq!(embeddings, vec![vec![3.0], vec![4.0]]);
100 }
101}