batch_mode_process_response/
process_batch_output_and_errors.rs

1// ---------------- [ File: batch-mode-process-response/src/process_batch_output_and_errors.rs ]
2crate::ix!();
3
4/// We add `'static + Send + Sync` to `T`, so that storing it in an
5/// `Arc<dyn ... + Send + Sync + 'static>` is valid and the resulting
6/// `Future` can be `Send`.
7pub async fn process_batch_output_and_errors<T>(
8    workspace:              &dyn BatchWorkspaceInterface, 
9    batch_execution_result: &BatchExecutionResult,
10    expected_content_type:  &ExpectedContentType,
11) -> Result<(), BatchProcessingError> 
12where
13    // The key is here: `'static + Send + Sync`.
14    // This ensures that any Arc<T> or Arc<dyn ...> is also Send + Sync,
15    // letting the async function be `Pin<Box<dyn Future<...> + Send>>`.
16    T: 'static + Send + Sync + DeserializeOwned + Named + GetTargetPathForAIExpansion,
17{
18    trace!("process_batch_output_and_errors => start.");
19
20    // Process the outputs
21    if let Some(output_data) = &batch_execution_result.outputs() {
22        info!("processing batch output data of len {}", output_data.len());
23        // Also requires `'static + Send + Sync` in process_output_data signature
24        process_output_data::<T>(output_data, workspace, expected_content_type).await?;
25    }
26
27    // Process the errors
28    if let Some(error_data) = &batch_execution_result.errors() {
29        info!("processing batch error data of len {}", error_data.len());
30        process_error_data(error_data).await?;
31    }
32
33    Ok(())
34}
35
36#[cfg(test)]
37mod process_batch_output_and_errors_tests {
38    use super::*;
39    use std::fs;
40    use tokio::runtime::Runtime;
41
42    #[derive(Debug, Clone, Deserialize, Serialize, NamedItem)]
43    pub struct BatchOutputErrorMockItem {
44        pub name: String,
45    }
46
47    #[traced_test]
48    async fn test_process_batch_output_and_errors() {
49
50        let workspace: Arc<dyn BatchWorkspaceInterface> = BatchWorkspace::new_temp().await.unwrap();
51
52        // 3) Build a successful record
53        let success_msg = BatchMessageBuilder::default()
54            .role(MessageRole::Assistant)
55            .content(
56                BatchMessageContentBuilder::default()
57                    .content("{\"name\":\"batch_out_item\"}".to_string())
58                    .build()
59                    .unwrap(),
60            )
61            .build()
62            .unwrap();
63
64        let success_choice = BatchChoiceBuilder::default()
65            .index(0_u32)
66            .finish_reason(FinishReason::Stop)
67            .logprobs(None)
68            .message(success_msg)
69            .build()
70            .unwrap();
71
72        let success_body = BatchSuccessResponseBodyBuilder::default()
73            .id("just-an-id".to_string())
74            .object("chat.completion".to_string())
75            .created(0_u64)
76            .model("test-model".to_string())
77            .choices(vec![success_choice])
78            .usage(BatchUsage::mock())
79            .build()
80            .unwrap();
81
82        let success_content = BatchResponseContentBuilder::default()
83            .status_code(200_u16)
84            .request_id(ResponseRequestId::new("resp_req_ok_1"))
85            .body(BatchResponseBody::Success(success_body))
86            .build()
87            .unwrap();
88
89        let success_record = BatchResponseRecordBuilder::default()
90            .id(BatchRequestId::new("batch_req_ok_1"))
91            .custom_id(CustomRequestId::new("ok_1"))
92            .response(success_content)
93            .build()
94            .unwrap();
95
96        // 4) Build an error record
97        let error_details = BatchErrorDetailsBuilder::default()
98            .error_type(ErrorType::Unknown("999".to_string()))
99            .message("some test error".to_string())
100            .build()
101            .unwrap();
102
103        let error_body = BatchErrorResponseBodyBuilder::default()
104            .error(error_details)
105            .build()
106            .unwrap();
107
108        let error_content = BatchResponseContentBuilder::default()
109            .status_code(400_u16)
110            .request_id(ResponseRequestId::new("resp_req_err_1"))
111            .body(BatchResponseBody::Error(error_body))
112            .build()
113            .unwrap();
114
115        let error_record = BatchResponseRecordBuilder::default()
116            .id(BatchRequestId::new("batch_req_err_1"))
117            .custom_id(CustomRequestId::new("err_1"))
118            .response(error_content)
119            .build()
120            .unwrap();
121
122        // 5) Construct a batch result
123        let out_data = BatchOutputData::new(vec![success_record]);
124        let err_data = BatchErrorData::new(vec![error_record]);
125        let batch_result = BatchExecutionResultBuilder::default()
126            .outputs(Some(out_data))
127            .errors(Some(err_data))
128            .build()
129            .unwrap();
130
131        // 6) Attempt to process
132        let result = process_batch_output_and_errors::<BatchOutputErrorMockItem>(
133            workspace.as_ref(),
134            &batch_result,
135            &ExpectedContentType::Json,
136        ).await;
137
138        // 7) We expect it to succeed
139        assert!(
140            result.is_ok(),
141            "Should handle success & error records gracefully, returning Ok."
142        );
143    }
144}