honcho_ai/types/
message.rs1use std::collections::HashMap;
4
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7
8pub use super::common::ReasoningConfiguration;
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
15#[non_exhaustive]
16pub struct MessageResponse {
17 pub id: String,
19 pub content: String,
21 pub peer_id: String,
23 pub session_id: String,
25 #[serde(default)]
27 pub metadata: HashMap<String, serde_json::Value>,
28 pub created_at: DateTime<Utc>,
30 pub workspace_id: String,
32 #[serde(default)]
34 pub token_count: u64,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, bon::Builder)]
39#[builder(on(String, into))]
40#[builder(finish_fn = build)]
41#[non_exhaustive]
42pub struct MessageCreate {
43 pub content: String,
45 pub peer_id: String,
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub metadata: Option<HashMap<String, serde_json::Value>>,
50 #[serde(skip_serializing_if = "Option::is_none")]
52 pub configuration: Option<MessageConfiguration>,
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub created_at: Option<DateTime<Utc>>,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, bon::Builder)]
60#[builder(finish_fn = build)]
61#[non_exhaustive]
62pub struct MessageBatchCreate {
63 pub messages: Vec<MessageCreate>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default, bon::Builder)]
69#[builder(on(String, into))]
70#[builder(finish_fn = build)]
71#[non_exhaustive]
72pub struct MessageUpdate {
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub metadata: Option<HashMap<String, serde_json::Value>>,
76}
77
78#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
86pub(crate) struct MessageMetadataSet {
87 pub metadata: HashMap<String, serde_json::Value>,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
95#[non_exhaustive]
96pub struct MessageConfiguration {
97 #[serde(default, skip_serializing_if = "Option::is_none")]
99 pub reasoning: Option<ReasoningConfiguration>,
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, bon::Builder)]
104#[builder(on(String, into))]
105#[builder(finish_fn = build)]
106#[non_exhaustive]
107pub struct MessageSearchOptions {
108 pub query: String,
110 #[serde(skip_serializing_if = "Option::is_none")]
112 pub filters: Option<HashMap<String, serde_json::Value>>,
113 #[serde(default = "default_limit")]
115 #[builder(default = default_limit())]
116 pub limit: u32,
117}
118
119const fn default_limit() -> u32 {
120 MessageSearchOptions::DEFAULT_LIMIT
121}
122
123impl MessageSearchOptions {
124 const DEFAULT_LIMIT: u32 = 10;
125}
126
127pub type MessagePage = super::pagination::Page<MessageResponse>;
129
130#[cfg(test)]
131#[allow(
132 clippy::unwrap_used,
133 clippy::expect_used,
134 clippy::panic,
135 clippy::unnecessary_wraps,
136 clippy::needless_pass_by_value,
137 clippy::unused_async
138)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn message_search_options_default_limit() {
144 let opts: MessageSearchOptions = serde_json::from_str(r#"{"query":"hello"}"#).unwrap();
145 assert_eq!(opts.limit, 10);
146 }
147
148 #[test]
149 fn message_batch_create_builder_constructs_from_messages() {
150 let msg = MessageCreate::builder()
153 .content("hello")
154 .peer_id("peer_01")
155 .build();
156 let batch = MessageBatchCreate::builder().messages(vec![msg]).build();
157 assert_eq!(batch.messages.len(), 1);
158 }
159
160 #[test]
161 fn message_response_token_count_defaults_to_zero_when_missing() {
162 let json = r#"{
164 "id": "msg_01",
165 "content": "hello",
166 "peer_id": "peer_01",
167 "session_id": "sess_01",
168 "workspace_id": "ws_01",
169 "created_at": "2024-01-01T00:00:00Z"
170 }"#;
171 let msg: MessageResponse = serde_json::from_str(json).unwrap();
172 assert_eq!(msg.token_count, 0);
173 }
174}