Skip to main content

infraqueue_lib/
model.rs

1use chrono::Utc; // use UTC for consistent, timezone-agnostic timestamps
2use serde::{Deserialize, Serialize};
3use uuid::Uuid; // for unique message IDs
4
5/// Message envelope for INFRAQUEUE.
6///
7/// LLM usage: payload is typically a JSON string carrying inputs for generation,
8/// embedding, or RAG retrieval. Suggested fields: `v` (schema version), `trace_id`,
9/// `prompt` or `query`, `params` (e.g., temperature, top_k), and domain-specific context.
10/// Keep payloads reasonably small; store large artifacts in object storage and reference
11/// them by URL or key.
12#[derive(Clone, Debug, Serialize, Deserialize)]
13pub struct InfraQueueMessage {
14    pub id: String,           // globally unique identifier for the message
15    pub timestamp: u64,       // timestamp in milliseconds since UNIX epoch (UTC)
16    pub sender: String,       // identifier for the message producer
17    pub topic: String,        // logical topic/queue name
18    pub payload: String,      // opaque payload (typically JSON)
19    pub retry_count: u8,      // number of delivery retries attempted
20    pub priority: Option<u8>, // optional priority (lower is higher priority)
21}
22
23impl InfraQueueMessage {
24    /// Create a new message with safe defaults and robust timestamp handling.
25    ///
26    /// LLM guidance:
27    /// - Use this to wrap a job payload for topics like `rag-search`, `semantic-search`,
28    ///   `content-tagging`, `embeddings`, `pii-scrubbing`, etc.
29    /// - Payload is a JSON string; include a schema version (e.g., `v: 1`) so you can evolve it.
30    /// - Keep payloads small; reference large documents via URLs/keys.
31    /// - Idempotency: include a trace or business key so your worker can de-duplicate.
32    ///
33    /// Example payloads (stringified JSON):
34    /// - RAG search:
35    ///   {"v":1,"trace_id":"abc123","query":"how do I rotate haproxy certs?","top_k":5,"filters":{"env":"prod"}}
36    /// - Generation:
37    ///   {"v":1,"trace_id":"abc123","prompt":"Write a changelog entry for...","params":{"temperature":0.2}}
38    /// - Embedding:
39    ///   {"v":1,"trace_id":"abc123","text":"The quick brown fox...","model":"text-embedding-3-small"}
40    pub fn new(
41        sender: impl Into<String>,
42        topic: impl Into<String>,
43        payload: impl Into<String>,
44    ) -> Self {
45        // Get milliseconds since epoch; can be negative on systems with incorrect clock,
46        // so clamp to zero before casting to u64 to avoid underflow.
47        let ts_ms_i64 = Utc::now().timestamp_millis();
48        let ts_ms_u64 = if ts_ms_i64 >= 0 { ts_ms_i64 as u64 } else { 0 };
49
50        // Generate a random UUIDv4 as the message ID.
51        let id = Uuid::new_v4().to_string();
52
53        Self {
54            id,
55            timestamp: ts_ms_u64,
56            sender: sender.into(),
57            topic: topic.into(),
58            payload: payload.into(),
59            retry_count: 0,
60            priority: None,
61        }
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn test_message_new_basics() {
71        let m = InfraQueueMessage::new("s", "t", "p");
72        assert!(!m.id.is_empty());
73        assert!(m.timestamp > 0);
74        assert_eq!(m.sender, "s");
75        assert_eq!(m.topic, "t");
76        assert_eq!(m.payload, "p");
77        assert_eq!(m.retry_count, 0);
78        assert!(m.priority.is_none());
79    }
80}