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}