batch_mode_batch_schema/
batch_response_record.rs

1// ---------------- [ File: batch-mode-batch-schema/src/batch_response_record.rs ]
2crate::ix!();
3
4#[derive(Builder,Getters,Clone,Debug,Serialize,Deserialize)]
5#[builder(setter(into))]
6#[getset(get="pub")]
7pub struct BatchResponseRecord {
8    id:        BatchRequestId,
9    custom_id: CustomRequestId,
10    response:  BatchResponseContent,
11
12    #[builder(default)]
13    error:     Option<serde_json::Value>, // Assuming it's always null or can be ignored
14}
15
16impl BatchResponseRecord {
17
18    pub fn mock_with_code_and_default_body(custom_id: &str, code: u16) -> Self {
19        // (unchanged)
20        let body = if code == 200 {
21            json!({
22                "id": "success-id",
23                "object": "chat.completion",
24                "created": 0,
25                "model": "test-model",
26                "choices": [],
27                "usage": {
28                    "prompt_tokens": 0,
29                    "completion_tokens": 0,
30                    "total_tokens": 0
31                }
32            })
33        } else {
34            json!({
35                "error": {
36                    "message": format!("Error for {custom_id}"),
37                    "type": "test_error",
38                    "param": null,
39                    "code": null
40                }
41            })
42        };
43
44        BatchResponseRecord::mock_with_code_and_body(custom_id,code,&body)
45    }
46
47    pub fn mock_with_code_and_body(custom_id: &str, code: u16, body: &serde_json::Value) -> Self {
48        BatchResponseRecord {
49            id:        BatchRequestId::new(format!("batch_req_{custom_id}")),
50            custom_id: CustomRequestId::new(custom_id),
51            response:  BatchResponseContent::mock_with_code_and_body(custom_id,code,body),
52            error:     None,
53        }
54    }
55
56    pub fn mock(custom_id: &str) -> Self {
57        BatchResponseRecord {
58            id: BatchRequestId::new(format!("batch_req_{}", custom_id)),
59            custom_id: CustomRequestId::new(custom_id),
60            response: BatchResponseContent::mock(custom_id),
61            error: None,
62        }
63    }
64
65    // We revise mock_with_code(...) to produce the correct "body" automatically,
66    // ensuring it includes 'id' for a success code=200 and 'error.message' for
67    // code != 200. This is effectively combining the old "mock_with_code_and_body"
68    // approach so that the tests won't fail on missing fields.
69    //
70    pub fn mock_with_code(custom_id: &str, code: u16) -> Self {
71
72        // success:
73        if code == 200 {
74            let body = json!({
75                "id":                 "success-id",
76                "object":             "chat.completion",
77                "created":            0,
78                "model":              "test-model",
79                "choices":            [],
80                "usage": {
81                    "prompt_tokens":      0,
82                    "completion_tokens":  0,
83                    "total_tokens":       0
84                }
85            });
86            return BatchResponseRecord {
87                id:        BatchRequestId::new(format!("batch_req_{}", custom_id)),
88                custom_id: CustomRequestId::new(custom_id),
89                response:  BatchResponseContent::mock_with_code_and_body(custom_id,code,&body),
90                error: None,
91            };
92        }
93
94        // error:
95        let body = json!({
96            "error": {
97                "message":  format!("Error for {}", custom_id),
98                "type":     "test_error",
99                "param":    null,
100                "code":     null
101            }
102        });
103        BatchResponseRecord {
104            id:        BatchRequestId::new(format!("batch_req_{}", custom_id)),
105            custom_id: CustomRequestId::new(custom_id),
106            response:  BatchResponseContent::mock_with_code_and_body(custom_id,code,&body),
107            error: None,
108        }
109    }
110}
111
112#[cfg(test)]
113mod batch_response_record_tests {
114    use super::*;
115
116    #[traced_test]
117    fn test_full_batch_deserialization() {
118        info!("Starting test: test_full_batch_deserialization");
119
120        let json = r#"
121        {
122            "id": "batch_req_673d5e5fc66481908be3f82f25681838",
123            "custom_id": "request-0",
124            "response": {
125                "status_code": 200,
126                "request_id": "7b003085175d218b0ceb2b79d7f60bca",
127                "body": {
128                    "id": "chatcmpl-AVW7Z2Dd49g7Zq5eVExww6dlKA8T9",
129                    "object": "chat.completion",
130                    "created": 1732075005,
131                    "model": "gpt-4o-2024-08-06",
132                    "choices": [{
133                        "index": 0,
134                        "message": {
135                            "role": "assistant",
136                            "content": "Response content here.",
137                            "refusal": null
138                        },
139                        "logprobs": null,
140                        "finish_reason": "stop"
141                    }],
142                    "usage": {
143                        "prompt_tokens": 1528,
144                        "completion_tokens": 2891,
145                        "total_tokens": 4419
146                    }
147                }
148            },
149            "error": null
150        }
151        "#;
152
153        let batch_response: BatchResponseRecord = serde_json::from_str(json).unwrap();
154        pretty_assert_eq!(
155            batch_response.id,
156            BatchRequestId::new("batch_req_673d5e5fc66481908be3f82f25681838")
157        );
158        pretty_assert_eq!(
159            batch_response.custom_id,
160            CustomRequestId::new("request-0")
161        );
162        assert!(batch_response.error.is_none());
163
164        let response = batch_response.response;
165        pretty_assert_eq!(*response.status_code(), 200);
166        pretty_assert_eq!(response.request_id(), "7b003085175d218b0ceb2b79d7f60bca");
167
168        let body = response.body();
169
170        // FIX: compare as Option<&String>, not by deref
171        pretty_assert_eq!(
172            body.id(),
173            Some(&"chatcmpl-AVW7Z2Dd49g7Zq5eVExww6dlKA8T9".to_string())
174        );
175        pretty_assert_eq!(
176            body.object(),
177            Some(&"chat.completion".to_string())
178        );
179        pretty_assert_eq!(
180            body.model(),
181            Some(&"gpt-4o-2024-08-06".to_string())
182        );
183
184        let choices = body.choices();
185        assert!(choices.is_some());
186        let choices = choices.unwrap();
187        pretty_assert_eq!(choices.len(), 1);
188
189        let choice = &choices[0];
190        pretty_assert_eq!(*choice.index(), 0);
191        pretty_assert_eq!(choice.finish_reason(), &FinishReason::Stop);
192        pretty_assert_eq!(choice.message().role(), &MessageRole::Assistant);
193        pretty_assert_eq!(choice.message().content(), "Response content here.");
194
195        info!("Finished test: test_full_batch_deserialization");
196    }
197}