agentic_llm/traits.rs
1//! Core traits and types for LLM adapters.
2
3use async_trait::async_trait;
4use futures::Stream;
5use std::pin::Pin;
6
7use crate::error::LLMError;
8
9/// Role in a conversation.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum Role {
12 /// System message (instructions)
13 System,
14 /// User message
15 User,
16 /// Assistant response
17 Assistant,
18}
19
20/// A message in a conversation.
21#[derive(Debug, Clone)]
22pub struct LLMMessage {
23 /// Role of the message sender
24 pub role: Role,
25 /// Content of the message
26 pub content: String,
27}
28
29impl LLMMessage {
30 /// Create a system message.
31 #[must_use]
32 pub fn system(content: impl Into<String>) -> Self {
33 Self {
34 role: Role::System,
35 content: content.into(),
36 }
37 }
38
39 /// Create a user message.
40 #[must_use]
41 pub fn user(content: impl Into<String>) -> Self {
42 Self {
43 role: Role::User,
44 content: content.into(),
45 }
46 }
47
48 /// Create an assistant message.
49 #[must_use]
50 pub fn assistant(content: impl Into<String>) -> Self {
51 Self {
52 role: Role::Assistant,
53 content: content.into(),
54 }
55 }
56}
57
58/// Token usage statistics.
59#[derive(Debug, Clone, Default)]
60pub struct TokenUsage {
61 /// Tokens in the prompt
62 pub prompt: u32,
63 /// Tokens in the completion
64 pub completion: u32,
65 /// Total tokens used
66 pub total: u32,
67}
68
69/// Reason for completion finishing.
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum FinishReason {
72 /// Normal stop (end of response)
73 Stop,
74 /// Hit max tokens limit
75 Length,
76 /// Error occurred
77 Error,
78}
79
80/// Response from an LLM.
81#[derive(Debug, Clone)]
82pub struct LLMResponse {
83 /// Generated content
84 pub content: String,
85 /// Token usage statistics
86 pub tokens_used: TokenUsage,
87 /// Reason for finishing
88 pub finish_reason: FinishReason,
89 /// Model that generated the response
90 pub model: String,
91}
92
93/// A chunk from streaming response.
94#[derive(Debug, Clone)]
95pub struct StreamChunk {
96 /// Partial content
97 pub content: String,
98 /// Whether this is the final chunk
99 pub done: bool,
100 /// Token usage (only available on final chunk)
101 pub tokens_used: Option<TokenUsage>,
102 /// Finish reason (only available on final chunk)
103 pub finish_reason: Option<FinishReason>,
104}
105
106/// Trait for LLM adapters.
107///
108/// Implement this trait to add support for a new LLM provider.
109#[async_trait]
110pub trait LLMAdapter: Send + Sync {
111 /// Get the provider name (e.g., "openai", "anthropic").
112 fn provider(&self) -> &str;
113
114 /// Get the model name being used.
115 fn model(&self) -> &str;
116
117 /// Generate a completion from messages.
118 ///
119 /// # Errors
120 ///
121 /// Returns an error if the API call fails.
122 async fn generate(&self, messages: &[LLMMessage]) -> Result<LLMResponse, LLMError>;
123
124 /// Generate a streaming completion.
125 ///
126 /// Returns a stream of chunks that can be processed as they arrive.
127 fn generate_stream(
128 &self,
129 messages: &[LLMMessage],
130 ) -> Pin<Box<dyn Stream<Item = Result<StreamChunk, LLMError>> + Send + '_>>;
131
132 /// Check if the LLM provider is accessible.
133 ///
134 /// # Errors
135 ///
136 /// Returns an error if the health check fails.
137 async fn health_check(&self) -> Result<bool, LLMError>;
138}