claude_agent/client/messages/
types.rs

1//! Helper types for message requests.
2
3use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6
7use crate::types::{ToolDefinition, ToolSearchTool, WebFetchTool, WebSearchTool};
8
9#[derive(Debug, Clone, Default, Serialize, Deserialize)]
10pub struct RequestMetadata {
11    #[serde(skip_serializing_if = "Option::is_none")]
12    pub user_id: Option<String>,
13    #[serde(flatten)]
14    pub extra: HashMap<String, serde_json::Value>,
15}
16
17impl RequestMetadata {
18    pub fn generate() -> Self {
19        let session_id = uuid::Uuid::new_v4();
20        let user_hash = format!("{:x}", simple_hash(session_id.as_bytes()));
21        let account_uuid = uuid::Uuid::new_v4();
22        Self {
23            user_id: Some(format!(
24                "user_{}_account_{}_session_{}",
25                user_hash, account_uuid, session_id
26            )),
27            extra: HashMap::new(),
28        }
29    }
30}
31
32fn simple_hash(data: &[u8]) -> u128 {
33    let mut hash: u128 = 0;
34    for (i, &byte) in data.iter().enumerate() {
35        hash = hash.wrapping_add((byte as u128).wrapping_mul((i as u128).wrapping_add(1)));
36        hash = hash.wrapping_mul(31);
37    }
38    hash
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
42#[serde(untagged)]
43pub enum ApiTool {
44    Custom(ToolDefinition),
45    WebSearch(WebSearchTool),
46    WebFetch(WebFetchTool),
47    ToolSearch(ToolSearchTool),
48}
49
50impl From<ToolDefinition> for ApiTool {
51    fn from(tool: ToolDefinition) -> Self {
52        Self::Custom(tool)
53    }
54}
55
56impl From<WebSearchTool> for ApiTool {
57    fn from(tool: WebSearchTool) -> Self {
58        Self::WebSearch(tool)
59    }
60}
61
62impl From<ToolSearchTool> for ApiTool {
63    fn from(tool: ToolSearchTool) -> Self {
64        Self::ToolSearch(tool)
65    }
66}
67
68impl From<WebFetchTool> for ApiTool {
69    fn from(tool: WebFetchTool) -> Self {
70        Self::WebFetch(tool)
71    }
72}
73
74#[derive(Debug, Clone, Deserialize)]
75pub struct ErrorResponse {
76    #[serde(rename = "type")]
77    pub error_type: String,
78    pub error: ErrorDetail,
79}
80
81impl ErrorResponse {
82    pub fn into_error(self, status: u16) -> crate::Error {
83        crate::Error::Api {
84            message: self.error.message,
85            status: Some(status),
86            error_type: Some(self.error.error_type),
87        }
88    }
89}
90
91#[derive(Debug, Clone, Deserialize)]
92pub struct ErrorDetail {
93    #[serde(rename = "type")]
94    pub error_type: String,
95    pub message: String,
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn test_request_metadata_generate() {
104        let metadata = RequestMetadata::generate();
105        assert!(metadata.user_id.is_some());
106        let user_id = metadata.user_id.unwrap();
107        assert!(user_id.starts_with("user_"));
108        assert!(user_id.contains("_account_"));
109        assert!(user_id.contains("_session_"));
110    }
111}