Skip to main content

liminal_sdk/
conversation.rs

1use alloc::string::String;
2use core::future::Future;
3
4use futures_core::Stream;
5use serde::Serialize;
6use serde::de::DeserializeOwned;
7
8use crate::SdkError;
9
10/// Application-visible identifier for a conversation.
11///
12/// SDK callers use this value for correlation and lifecycle observation. It is
13/// not a beamr process identifier and does not require callers to manage any
14/// supervised runtime process directly.
15#[derive(Clone, Debug, PartialEq, Eq, Hash)]
16pub struct ConversationId(String);
17
18impl ConversationId {
19    /// Creates a conversation identifier from an application-visible string.
20    #[must_use]
21    pub fn new(value: impl Into<String>) -> Self {
22        Self(value.into())
23    }
24
25    /// Returns the identifier as a string slice.
26    #[must_use]
27    pub fn as_str(&self) -> &str {
28        self.0.as_str()
29    }
30}
31
32impl From<String> for ConversationId {
33    fn from(value: String) -> Self {
34        Self::new(value)
35    }
36}
37
38impl From<&'static str> for ConversationId {
39    fn from(value: &'static str) -> Self {
40        Self::new(value)
41    }
42}
43
44/// Lifecycle events emitted by a conversation.
45///
46/// Every event carries the application-visible conversation identifier so
47/// lifecycle streams can be correlated without exposing process identifiers,
48/// protocol frames, or transport details.
49#[derive(Debug)]
50pub enum ConversationEvent {
51    /// The conversation was opened.
52    Opened {
53        /// Identifier for the conversation that emitted this event.
54        conversation_id: ConversationId,
55    },
56    /// A message was observed within the conversation.
57    Message {
58        /// Identifier for the conversation that emitted this event.
59        conversation_id: ConversationId,
60    },
61    /// The conversation has begun closing.
62    Closing {
63        /// Identifier for the conversation that emitted this event.
64        conversation_id: ConversationId,
65    },
66    /// The conversation closed.
67    Closed {
68        /// Identifier for the conversation that emitted this event.
69        conversation_id: ConversationId,
70    },
71    /// The conversation encountered an error.
72    Error {
73        /// Identifier for the conversation that emitted this event.
74        conversation_id: ConversationId,
75        /// Error reported for the conversation lifecycle.
76        error: SdkError,
77    },
78}
79
80/// Application-facing typed conversation API.
81///
82/// A conversation is the fundamental messaging unit in liminal. The handle lets
83/// callers send typed messages, receive typed messages, and observe lifecycle
84/// events without handling transport details or supervised runtime process IDs.
85pub trait ConversationHandle: core::fmt::Debug + Send + Sync {
86    /// Future returned by [`receive`](Self::receive) for message type `M`.
87    type ReceiveFuture<'a, M>: Future<Output = Result<M, SdkError>> + 'a
88    where
89        Self: 'a,
90        M: DeserializeOwned + 'a;
91
92    /// Stream returned by [`lifecycle`](Self::lifecycle).
93    type LifecycleStream: Stream<Item = ConversationEvent>;
94
95    /// Sends a typed message within this conversation.
96    ///
97    /// # Errors
98    ///
99    /// Returns [`SdkError`] when the concrete conversation implementation cannot
100    /// serialize or transmit the message in the conversation context.
101    fn send<M>(&self, message: M) -> Result<(), SdkError>
102    where
103        M: Serialize;
104
105    /// Receives the next typed message from this conversation.
106    ///
107    /// The message type is owned after deserialization so callers never borrow
108    /// buffers managed by an SDK implementation.
109    ///
110    /// # Errors
111    ///
112    /// The returned future resolves to [`SdkError`] when the concrete
113    /// implementation cannot receive or deserialize the next message.
114    fn receive<M>(&self) -> Self::ReceiveFuture<'_, M>
115    where
116        M: DeserializeOwned;
117
118    /// Observes lifecycle events for this conversation.
119    fn lifecycle(&self) -> Self::LifecycleStream;
120}