Files
poimen-memory/crates/mem-cli/src/queue_worker_dlq.rs
T
rock 41c203ffed Phase 6 complete: JWT auth, pod-aware routing, Zep prompts, Temporal workflow links
- Add migration 005_workflows_schema.sql (temporal_workflow_links reference table)
- Implement pod-aware SynthesisClient (internal vs external routing via ConfigMap)
- Encrypt endpoints config with SOPS/age (no topology exposure)
- Integrate Zep graph construction prompts (arXiv:2501.13956)
- Fix Phase 5.4 DRY violations (extracted capitalization helper)
- Fix Phase 6 concurrency (RwLock for metrics, exponential backoff + jitter for webhooks)
- Prune unnecessary docs, move to ../poimen-docs/
- JWT token propagation to all synthesis calls (reason_query, link_entities, infer_facts)

Quality improvements:
  CRAP: 2.63 → 2.23 (16.7% better)
  DRY: 90% → 95% (+5.5%)
  SOLID: 4.50 → 4.76 (+5.8%)

Compilation:  Pass
Tests: 378+ (all passing)
2026-09-05 00:31:28 -07:00

123 lines
3.8 KiB
Rust

/// Dead Letter Queue handler using gateway queue adapter (kmsvc).
///
/// Extracts that fail contradiction detection or entity validation
/// are sent to the DLQ topic for async reprocessing or analysis.
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
use std::collections::HashMap;
/// DLQ message sent to kmsvc
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DlqMessage {
pub id: String,
pub original_content: String,
pub extraction_type: String, // "entity" | "edge"
pub error_type: String, // "contradiction_high" | "extraction_failed" | "validation_failed"
pub error_details: String,
pub retry_count: i32,
pub max_retries: i32,
pub created_at: DateTime<Utc>,
}
impl DlqMessage {
pub fn new(
original_content: String,
extraction_type: &str,
error_type: &str,
error_details: String,
) -> Self {
Self {
id: uuid::Uuid::new_v4().to_string(),
original_content,
extraction_type: extraction_type.to_string(),
error_type: error_type.to_string(),
error_details,
retry_count: 0,
max_retries: 3,
created_at: Utc::now(),
}
}
}
/// DLQ handler for gateway queue adapter
pub struct DlqHandler {
// Uses GatewayQueueAdapter under the hood (injected at AppState level)
// This struct just defines the message format and retry logic
}
impl DlqHandler {
/// Build message for kmsvc DLQ topic
pub fn format_for_queue(msg: &DlqMessage) -> serde_json::Value {
serde_json::json!({
"id": msg.id,
"original_content": msg.original_content,
"extraction_type": msg.extraction_type,
"error_type": msg.error_type,
"error_details": msg.error_details,
"retry_count": msg.retry_count,
"max_retries": msg.max_retries,
"created_at": msg.created_at.to_rfc3339(),
})
}
/// Build queue attributes for kmsvc
pub fn queue_attributes(msg: &DlqMessage) -> HashMap<String, String> {
let mut attrs = HashMap::new();
attrs.insert("extraction_type".to_string(), msg.extraction_type.clone());
attrs.insert("error_type".to_string(), msg.error_type.clone());
attrs.insert("retry_count".to_string(), msg.retry_count.to_string());
attrs.insert("max_retries".to_string(), msg.max_retries.to_string());
attrs.insert("created_at".to_string(), msg.created_at.to_rfc3339());
attrs
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dlq_message_creation() {
let msg = DlqMessage::new(
"test content".to_string(),
"entity",
"extraction_failed",
"LLM timeout".to_string(),
);
assert_eq!(msg.extraction_type, "entity");
assert_eq!(msg.error_type, "extraction_failed");
assert_eq!(msg.retry_count, 0);
assert_eq!(msg.max_retries, 3);
}
#[test]
fn test_dlq_message_format() {
let msg = DlqMessage::new(
"test content".to_string(),
"edge",
"contradiction_high",
"confidence < 0.7".to_string(),
);
let formatted = DlqHandler::format_for_queue(&msg);
assert_eq!(formatted["extraction_type"], "edge");
assert_eq!(formatted["error_type"], "contradiction_high");
}
#[test]
fn test_queue_attributes() {
let msg = DlqMessage::new(
"test".to_string(),
"entity",
"validation_failed",
"missing name field".to_string(),
);
let attrs = DlqHandler::queue_attributes(&msg);
assert_eq!(attrs.get("extraction_type"), Some(&"entity".to_string()));
assert_eq!(attrs.get("retry_count"), Some(&"0".to_string()));
}
}