generate internal id (debugging only)
NOT set to provider... Anthropic will reject a message with id
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1546,6 +1546,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"llama_cpp",
|
||||
"nanoid",
|
||||
"rand",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
||||
@@ -29,3 +29,4 @@ tokio-util = "0.7"
|
||||
dirs = "5.0"
|
||||
llama_cpp = { version = "0.3.2", features = ["metal"] }
|
||||
shellexpand = "3.1"
|
||||
rand = "0.8"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use anyhow::Result;
|
||||
use std::collections::HashMap;
|
||||
use rand::Rng;
|
||||
|
||||
/// Trait for LLM providers
|
||||
#[async_trait::async_trait]
|
||||
@@ -75,6 +76,7 @@ impl CacheControl {
|
||||
pub struct Message {
|
||||
pub role: MessageRole,
|
||||
pub content: String,
|
||||
pub id: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub cache_control: Option<CacheControl>,
|
||||
}
|
||||
@@ -137,11 +139,30 @@ pub use embedded::EmbeddedProvider;
|
||||
pub use openai::OpenAIProvider;
|
||||
|
||||
impl Message {
|
||||
/// Generate a unique message ID in format HHMMSS-XXX
|
||||
/// where XXX are 3 random alphanumeric characters (upper and lowercase)
|
||||
fn generate_id() -> String {
|
||||
let now = chrono::Local::now();
|
||||
let timestamp = now.format("%H%M%S").to_string();
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let random_chars: String = (0..3)
|
||||
.map(|_| {
|
||||
let chars = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
let idx = rng.gen_range(0..chars.len());
|
||||
chars[idx] as char
|
||||
})
|
||||
.collect();
|
||||
|
||||
format!("{}-{}", timestamp, random_chars)
|
||||
}
|
||||
|
||||
/// Create a new message with optional cache control
|
||||
pub fn new(role: MessageRole, content: String) -> Self {
|
||||
Self {
|
||||
role,
|
||||
content,
|
||||
id: Self::generate_id(),
|
||||
cache_control: None,
|
||||
}
|
||||
}
|
||||
@@ -151,6 +172,7 @@ impl Message {
|
||||
Self {
|
||||
role,
|
||||
content,
|
||||
id: Self::generate_id(),
|
||||
cache_control: Some(cache_control),
|
||||
}
|
||||
}
|
||||
@@ -288,4 +310,56 @@ mod tests {
|
||||
assert!(json.contains("ephemeral"), "JSON should contain 'ephemeral' type");
|
||||
assert!(json.contains("\"ttl\":\"1h\""), "JSON should contain ttl field with 1h value");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_message_id_generation() {
|
||||
let msg = Message::new(MessageRole::User, "Hello".to_string());
|
||||
|
||||
// Check that id is not empty
|
||||
assert!(!msg.id.is_empty(), "Message ID should not be empty");
|
||||
|
||||
// Check format: HHMMSS-XXX
|
||||
let parts: Vec<&str> = msg.id.split('-').collect();
|
||||
assert_eq!(parts.len(), 2, "Message ID should have format HHMMSS-XXX");
|
||||
|
||||
// Check timestamp part is 6 digits
|
||||
assert_eq!(parts[0].len(), 6, "Timestamp should be 6 digits (HHMMSS)");
|
||||
assert!(parts[0].chars().all(|c| c.is_ascii_digit()), "Timestamp should be all digits");
|
||||
|
||||
// Check random part is 3 alpha characters
|
||||
assert_eq!(parts[1].len(), 3, "Random part should be 3 characters");
|
||||
assert!(parts[1].chars().all(|c| c.is_ascii_alphabetic()),
|
||||
"Random part should be all alphabetic characters");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_message_id_uniqueness() {
|
||||
let msg1 = Message::new(MessageRole::User, "Hello".to_string());
|
||||
let msg2 = Message::new(MessageRole::User, "Hello".to_string());
|
||||
|
||||
// IDs should be different (due to random component)
|
||||
// Note: There's a tiny chance they could be the same, but very unlikely
|
||||
println!("msg1.id: {}, msg2.id: {}", msg1.id, msg2.id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_message_id_not_serialized() {
|
||||
let msg = Message::new(MessageRole::User, "Hello".to_string());
|
||||
let json = serde_json::to_string(&msg).unwrap();
|
||||
|
||||
println!("Message JSON: {}", json);
|
||||
assert!(!json.contains("\"id\""), "JSON should not contain 'id' field");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_message_with_cache_control_has_id() {
|
||||
let msg = Message::with_cache_control(
|
||||
MessageRole::User,
|
||||
"Hello".to_string(),
|
||||
CacheControl::ephemeral(),
|
||||
);
|
||||
|
||||
assert!(!msg.id.is_empty(), "Message with cache control should have an ID");
|
||||
assert!(msg.id.contains('-'), "Message ID should contain hyphen separator");
|
||||
}
|
||||
}
|
||||
|
||||
32
examples/verify_message_id.rs
Normal file
32
examples/verify_message_id.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
// Verification script to demonstrate Message ID implementation
|
||||
// Run with: cargo run --example verify_message_id
|
||||
|
||||
use g3_providers::{Message, MessageRole};
|
||||
|
||||
fn main() {
|
||||
println!("=== Message ID Implementation Verification ===");
|
||||
println!();
|
||||
|
||||
// Create several messages to show ID generation
|
||||
println!("Creating 5 messages to demonstrate ID generation:");
|
||||
for i in 1..=5 {
|
||||
let msg = Message::new(MessageRole::User, format!("Test message {}", i));
|
||||
println!(" Message {}: id = '{}'", i, msg.id);
|
||||
}
|
||||
|
||||
println!();
|
||||
println!("ID Format: HHMMSS-XXX");
|
||||
println!(" - HHMMSS: Current time (hours, minutes, seconds)");
|
||||
println!(" - XXX: 3 random alphabetic characters (a-z, A-Z)");
|
||||
|
||||
println!();
|
||||
println!("Verifying ID is NOT serialized to JSON:");
|
||||
let msg = Message::new(MessageRole::User, "Hello World".to_string());
|
||||
let json = serde_json::to_string(&msg).unwrap();
|
||||
println!(" Message ID: {}", msg.id);
|
||||
println!(" JSON output: {}", json);
|
||||
println!(" Contains 'id' field: {}", json.contains("\"id\""));
|
||||
|
||||
println!();
|
||||
println!("✅ Implementation complete!");
|
||||
}
|
||||
Reference in New Issue
Block a user