praxis_graph/
streaming.rs

1/// Adapter Pattern for Event Conversion
2/// 
3/// Converts between provider-specific event formats and the graph's internal event format.
4/// This abstraction allows the graph to work with different LLM providers without
5/// coupling to their specific event structures.
6
7/// Stream adapter trait for converting between event formats
8/// 
9/// # Type Parameters
10/// * `ProviderEvent` - The event type from the LLM provider
11/// * `GraphEvent` - The internal graph event type
12pub trait StreamAdapter {
13    type ProviderEvent;
14    type GraphEvent;
15    
16    /// Convert a provider event to a graph event
17    /// 
18    /// Returns None if the event should be filtered/ignored
19    fn adapt(&self, event: Self::ProviderEvent) -> Option<Self::GraphEvent>;
20}
21
22/// OpenAI stream adapter
23/// 
24/// Converts OpenAI `StreamEvent` to graph `StreamEvent`.
25/// Currently uses the From trait for direct conversion, but this adapter
26/// provides a clear extension point for custom logic.
27pub struct OpenAIStreamAdapter;
28
29impl StreamAdapter for OpenAIStreamAdapter {
30    type ProviderEvent = praxis_llm::StreamEvent;
31    type GraphEvent = crate::types::StreamEvent;
32    
33    fn adapt(&self, event: Self::ProviderEvent) -> Option<Self::GraphEvent> {
34        // Use the From trait implementation for conversion
35        // In the future, we could add filtering, transformation, or enrichment logic here
36        Some(event.into())
37    }
38}
39
40/// Future: Azure OpenAI adapter
41#[allow(dead_code)]
42pub struct AzureStreamAdapter;
43
44// impl StreamAdapter for AzureStreamAdapter {
45//     type ProviderEvent = AzureStreamEvent;
46//     type GraphEvent = crate::types::StreamEvent;
47//     
48//     fn adapt(&self, event: Self::ProviderEvent) -> Option<Self::GraphEvent> {
49//         // Convert Azure-specific events to graph events
50//         todo!("Azure adapter not yet implemented")
51//     }
52// }
53
54/// Future: Anthropic adapter
55#[allow(dead_code)]
56pub struct AnthropicStreamAdapter;
57
58// impl StreamAdapter for AnthropicStreamAdapter {
59//     type ProviderEvent = AnthropicStreamEvent;
60//     type GraphEvent = crate::types::StreamEvent;
61//     
62//     fn adapt(&self, event: Self::ProviderEvent) -> Option<Self::GraphEvent> {
63//         // Convert Anthropic-specific events to graph events
64//         todo!("Anthropic adapter not yet implemented")
65//     }
66// }
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71    use praxis_llm::StreamEvent as LLMEvent;
72    use crate::types::StreamEvent as GraphEvent;
73    
74    #[test]
75    fn test_openai_adapter_message() {
76        let adapter = OpenAIStreamAdapter;
77        let llm_event = LLMEvent::Message {
78            content: "Hello".to_string(),
79        };
80        
81        let graph_event = adapter.adapt(llm_event);
82        assert!(graph_event.is_some());
83        
84        match graph_event.unwrap() {
85            GraphEvent::Message { content } => {
86                assert_eq!(content, "Hello");
87            }
88            _ => panic!("Expected Message event"),
89        }
90    }
91    
92    #[test]
93    fn test_openai_adapter_reasoning() {
94        let adapter = OpenAIStreamAdapter;
95        let llm_event = LLMEvent::Reasoning {
96            content: "Thinking...".to_string(),
97        };
98        
99        let graph_event = adapter.adapt(llm_event);
100        assert!(graph_event.is_some());
101        
102        match graph_event.unwrap() {
103            GraphEvent::Reasoning { content } => {
104                assert_eq!(content, "Thinking...");
105            }
106            _ => panic!("Expected Reasoning event"),
107        }
108    }
109}
110