1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/*!
  | basic example:
  | {
  |     "custom_id": "request-1", 
  |     "method": "POST", 
  |     "url": "/v1/chat/completions", 
  |     "body": {
  |         "model": "gpt-4", 
  |         "messages": [
  |             {"role": "system", "content": "You are a helpful assistant."},
  |             {"role": "user", "content": "Hello world!"}
  |         ],
  |         "max_tokens": 1000
  |     }
  | }
  */
crate::ix!();

/// Represents the complete request structure.
#[derive(Debug, Serialize, Deserialize)]
pub struct GptBatchAPIRequest {

    /// Identifier for the custom request.
    custom_id: String,

    /// HTTP method used for the request.
    #[serde(with = "http_method")]
    method: HttpMethod,

    /// URL of the API endpoint.
    #[serde(with = "api_url")]
    url:  GptApiUrl,

    /// Body of the request.
    body: GptRequestBody,
}

impl GptBatchAPIRequest {

    pub fn new_basic(idx: usize, system_message: &str, user_message: &str) -> Self {
        Self {
            custom_id: Self::custom_id_for_idx(idx),
            method:    HttpMethod::Post,
            url:       GptApiUrl::ChatCompletions,
            body:      GptRequestBody::new_basic(system_message,user_message),
        }
    }

    pub fn new_with_image(idx: usize, system_message: &str, user_message: &str, image_b64: &str) -> Self {
        Self {
            custom_id: Self::custom_id_for_idx(idx),
            method:    HttpMethod::Post,
            url:       GptApiUrl::ChatCompletions,
            body:      GptRequestBody::new_with_image(system_message,user_message,image_b64),
        }
    }
}

impl Display for GptBatchAPIRequest {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match serde_json::to_string(self) {
            Ok(json) => write!(f, "{}", json),
            Err(e) => {
                // Handle JSON serialization errors, though they shouldn't occur with proper struct definitions
                write!(f, "Error serializing to JSON: {}", e)
            }
        }
    }
}

impl GptBatchAPIRequest {

    pub(crate) fn custom_id_for_idx(idx: usize) -> String {
        format!("request-{}",idx)
    }
}

/// Custom serialization modules for enum string representations.
mod http_method {

    use super::*;

    pub fn serialize<S>(value: &HttpMethod, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&value.to_string())
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<HttpMethod, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s: String = Deserialize::deserialize(deserializer)?;
        match s.as_ref() {
            "POST" => Ok(HttpMethod::Post),
            _ => Err(serde::de::Error::custom("unknown method")),
        }
    }
}

mod api_url {

    use super::*;

    pub fn serialize<S>(value: &GptApiUrl, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&value.to_string())
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<GptApiUrl, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s: String = Deserialize::deserialize(deserializer)?;
        match s.as_ref() {
            "/v1/chat/completions" => Ok(GptApiUrl::ChatCompletions),
            _ => Err(serde::de::Error::custom("unknown URL")),
        }
    }
}