Skip to main content

stakpak_server/
message_bridge.rs

1use stakpak_shared::models::{
2    integrations::openai::ChatMessage,
3    llm::LLMMessage,
4    stakai_adapter::{from_stakai_message, to_stakai_message},
5};
6
7/// Storage adapter: local/session storage currently persists ChatMessage payloads.
8///
9/// Runtime/API boundaries should stay StakAI-native. These adapters exist only at
10/// the persistence edge until storage is migrated to `Vec<stakai::Message>`.
11pub fn chat_to_stakai(messages: Vec<ChatMessage>) -> Vec<stakai::Message> {
12    messages
13        .into_iter()
14        .map(LLMMessage::from)
15        .map(|message| to_stakai_message(&message))
16        .collect()
17}
18
19pub fn stakai_to_chat(messages: &[stakai::Message]) -> Vec<ChatMessage> {
20    messages
21        .iter()
22        .map(from_stakai_message)
23        .map(ChatMessage::from)
24        .collect()
25}
26
27#[cfg(test)]
28mod tests {
29    use super::*;
30    use serde_json::json;
31    use stakai::{ContentPart, Message, MessageContent, Role};
32
33    #[test]
34    fn converts_stakai_to_chat_and_back() {
35        let messages = vec![
36            Message::new(Role::System, "system"),
37            Message::new(Role::User, "hello"),
38            Message::new(Role::Assistant, "hi"),
39        ];
40
41        let chat = stakai_to_chat(&messages);
42        let back = chat_to_stakai(chat);
43
44        assert_eq!(back.len(), 3);
45        assert_eq!(back[0].role, Role::System);
46        assert_eq!(back[1].role, Role::User);
47        assert_eq!(back[2].role, Role::Assistant);
48        assert_eq!(back[1].text(), Some("hello".to_string()));
49    }
50
51    #[test]
52    fn preserves_tool_result_tool_call_id_through_storage_adapter() {
53        let messages = vec![Message::new(
54            Role::Tool,
55            MessageContent::Parts(vec![ContentPart::tool_result(
56                "toolu_01Abc123".to_string(),
57                json!("result payload"),
58            )]),
59        )];
60
61        let chat = stakai_to_chat(&messages);
62        assert_eq!(chat.len(), 1);
63        assert_eq!(chat[0].tool_call_id.as_deref(), Some("toolu_01Abc123"));
64
65        let back = chat_to_stakai(chat);
66        assert_eq!(back.len(), 1);
67        assert_eq!(back[0].role, Role::Tool);
68
69        match &back[0].content {
70            MessageContent::Parts(parts) => {
71                assert_eq!(parts.len(), 1);
72                match &parts[0] {
73                    ContentPart::ToolResult { tool_call_id, .. } => {
74                        assert_eq!(tool_call_id, "toolu_01Abc123");
75                    }
76                    other => panic!("expected ToolResult part, got {other:?}"),
77                }
78            }
79            other => panic!("expected parts content, got {other:?}"),
80        }
81    }
82}