agentic_core/storage/models/
response.rs1use super::super::pool::{DbPool, DbResult, DbTransaction};
4use crate::utils::common::{deserialize_from_string_opt, deserialize_from_string_opt_or_default, utcnow_str};
5
6#[derive(Debug, Clone, sqlx::FromRow)]
11pub struct Response {
12 pub id: String,
14
15 pub conversation_id: Option<String>,
17
18 pub previous_response_id: Option<String>,
20
21 pub history_item_ids: Option<String>,
23
24 pub metadata: Option<String>,
26
27 pub created_at: i64,
29}
30
31pub async fn create_in_tx(
36 tx: &mut DbTransaction<'_>,
37 id: &str,
38 conversation_id: Option<&str>,
39 previous_response_id: Option<&str>,
40 history_item_ids: Option<&str>,
41 metadata: Option<&str>,
42) -> DbResult<Response> {
43 let now = utcnow_str();
44 sqlx::query_as::<_, Response>(
45 "INSERT INTO responses \
46 (id, conversation_id, previous_response_id, history_item_ids, metadata, created_at) \
47 VALUES ($1, $2, $3, $4, $5, $6) RETURNING *",
48 )
49 .bind(id)
50 .bind(conversation_id)
51 .bind(previous_response_id)
52 .bind(history_item_ids)
53 .bind(metadata)
54 .bind(now)
55 .fetch_one(&mut **tx)
56 .await
57}
58
59pub async fn get(pool: &DbPool, id: &str) -> DbResult<Option<Response>> {
64 sqlx::query_as::<_, Response>("SELECT * FROM responses WHERE id = $1")
65 .bind(id)
66 .fetch_optional(pool)
67 .await
68}
69
70impl Response {
71 #[must_use]
73 pub fn history_item_ids_vec(&self) -> Vec<String> {
74 deserialize_from_string_opt_or_default(&self.history_item_ids)
75 }
76
77 #[must_use]
79 pub fn metadata_as<T: serde::de::DeserializeOwned>(&self) -> Option<T> {
80 deserialize_from_string_opt(&self.metadata)
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn test_response_history_ids_empty() {
90 let response = Response {
91 id: "test".to_string(),
92 conversation_id: None,
93 previous_response_id: None,
94 history_item_ids: None,
95 metadata: None,
96 created_at: 1_704_067_200,
97 };
98
99 let ids: Vec<String> = response.history_item_ids_vec();
100 assert!(ids.is_empty());
101 }
102
103 #[test]
104 fn test_response_history_ids_valid() {
105 let response = Response {
106 id: "test".to_string(),
107 conversation_id: None,
108 previous_response_id: None,
109 history_item_ids: Some(r#"["item_1", "item_2"]"#.to_string()),
110 metadata: None,
111 created_at: 1_704_067_200,
112 };
113
114 let ids = response.history_item_ids_vec();
115 assert_eq!(ids.len(), 2);
116 assert_eq!(ids[0], "item_1");
117 }
118
119 #[test]
120 fn test_response_metadata_deserialize() {
121 #[derive(serde::Deserialize, PartialEq, Debug)]
122 struct TestMeta {
123 model: String,
124 }
125
126 let response = Response {
127 id: "resp_1".to_string(),
128 conversation_id: None,
129 previous_response_id: None,
130 history_item_ids: None,
131 metadata: Some(r#"{"model":"gpt-4"}"#.to_string()),
132 created_at: 1_704_067_200,
133 };
134
135 let meta: Option<TestMeta> = response.metadata_as();
136 assert!(meta.is_some());
137 }
138}