gproxy-protocol 1.0.20

Wire-format types and cross-protocol transforms for Claude, OpenAI, and Gemini LLM APIs.
Documentation
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
use crate::claude::count_tokens::types::{
    BetaContextManagementEdit, BetaMessageRole, BetaOutputEffort, BetaThinkingConfigParam,
    BetaToolChoice, BetaToolInputSchema, BetaToolInputSchemaType, BetaToolUnion,
};
use crate::claude::create_message::request::ClaudeCreateMessageRequest;
use crate::claude::create_message::types::{BetaServiceTierParam, BetaSpeed};
use crate::openai::count_tokens::types::{
    HttpMethod, ResponseApplyPatchTool, ResponseApplyPatchToolType, ResponseApproximateLocation,
    ResponseApproximateLocationType, ResponseCodeInterpreterContainer, ResponseCodeInterpreterTool,
    ResponseCodeInterpreterToolAuto, ResponseCodeInterpreterToolAutoType,
    ResponseCodeInterpreterToolType, ResponseComputerEnvironment, ResponseComputerTool,
    ResponseComputerToolType, ResponseFormatTextJsonSchemaConfig,
    ResponseFormatTextJsonSchemaConfigType, ResponseFunctionShellTool,
    ResponseFunctionShellToolType, ResponseFunctionTool, ResponseFunctionToolType, ResponseInput,
    ResponseInputItem, ResponseInputMessage, ResponseInputMessageContent, ResponseInputMessageRole,
    ResponseInputMessageType, ResponseMcpAllowedTools, ResponseMcpTool, ResponseMcpToolType,
    ResponseReasoning, ResponseReasoningEffort, ResponseTextConfig, ResponseTextFormatConfig,
    ResponseTextVerbosity, ResponseTool, ResponseToolChoice, ResponseToolChoiceFunction,
    ResponseToolChoiceFunctionType, ResponseToolChoiceOptions, ResponseTruncation,
    ResponseWebSearchFilters, ResponseWebSearchTool, ResponseWebSearchToolType,
};
use crate::openai::create_response::request::{
    OpenAiCreateResponseRequest, PathParameters, QueryParameters, RequestBody, RequestHeaders,
};
use crate::openai::create_response::types::{
    Metadata, ResponseContextManagementEntry, ResponseContextManagementType, ResponseServiceTier,
};
use crate::transform::claude::generate_content::utils::{
    beta_system_prompt_to_text, claude_model_to_string,
};
use crate::transform::utils::TransformError;
use serde_json::{Map, Value};
use std::collections::BTreeMap;

fn tool_input_schema_to_json_object(
    input_schema: BetaToolInputSchema,
) -> std::collections::BTreeMap<String, Value> {
    let mut parameters = std::collections::BTreeMap::new();
    let schema_type = match input_schema.type_ {
        BetaToolInputSchemaType::Object => "object",
    };
    parameters.insert("type".to_string(), Value::String(schema_type.to_string()));
    if let Some(properties) = input_schema.properties {
        let properties_object = properties.into_iter().collect::<Map<String, Value>>();
        parameters.insert("properties".to_string(), Value::Object(properties_object));
    }
    if let Some(required) = input_schema.required {
        parameters.insert(
            "required".to_string(),
            Value::Array(required.into_iter().map(Value::String).collect()),
        );
    }
    parameters
}

use crate::claude::count_tokens::types as ct;
use crate::openai::count_tokens::types as ot;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ClaudeToolKind {
    Function,
    Custom,
    Mcp,
    CodeInterpreter,
    Computer,
    WebSearch,
    WebFetch,
    Shell,
    ApplyPatch,
    FileSearch,
}

#[derive(Debug, Clone, Copy)]
struct RecordedToolCall {
    item_index: usize,
    kind: ClaudeToolKind,
}

fn json_string<T: serde::Serialize>(value: &T) -> String {
    serde_json::to_string(value).unwrap_or_else(|_| "{}".to_string())
}

fn input_text_content(text: String) -> ot::ResponseInputContent {
    ot::ResponseInputContent::Text(ot::ResponseInputText {
        text,
        type_: ot::ResponseInputTextType::InputText,
    })
}

fn flush_input_parts(
    input_items: &mut Vec<ResponseInputItem>,
    role: ResponseInputMessageRole,
    parts: &mut Vec<ot::ResponseInputContent>,
) {
    if parts.is_empty() {
        return;
    }

    let content = if parts.len() == 1 {
        match parts.pop() {
            Some(ot::ResponseInputContent::Text(text_part)) => {
                ResponseInputMessageContent::Text(text_part.text)
            }
            Some(part) => ResponseInputMessageContent::List(vec![part]),
            None => ResponseInputMessageContent::Text(String::new()),
        }
    } else {
        ResponseInputMessageContent::List(std::mem::take(parts))
    };

    input_items.push(ResponseInputItem::Message(ResponseInputMessage {
        content,
        role,
        phase: None,
        status: None,
        type_: Some(ResponseInputMessageType::Message),
    }));
}

fn output_message_item(id: String, text: String) -> ResponseInputItem {
    ResponseInputItem::OutputMessage(ot::ResponseOutputMessage {
        id,
        content: vec![ot::ResponseOutputContent::Text(ot::ResponseOutputText {
            annotations: Vec::new(),
            logprobs: None,
            text,
            type_: ot::ResponseOutputTextType::OutputText,
        })],
        role: ot::ResponseOutputMessageRole::Assistant,
        phase: None,
        status: Some(ot::ResponseItemStatus::Completed),
        type_: Some(ot::ResponseOutputMessageType::Message),
    })
}

fn image_media_type(media_type: ct::BetaImageMediaType) -> &'static str {
    match media_type {
        ct::BetaImageMediaType::ImageJpeg => "image/jpeg",
        ct::BetaImageMediaType::ImagePng => "image/png",
        ct::BetaImageMediaType::ImageGif => "image/gif",
        ct::BetaImageMediaType::ImageWebp => "image/webp",
    }
}

fn image_block_to_input_content(
    block: ct::BetaImageBlockParam,
) -> Option<ot::ResponseInputContent> {
    match block.source {
        ct::BetaImageSource::Base64(source) => {
            Some(ot::ResponseInputContent::Image(ot::ResponseInputImage {
                detail: None,
                type_: ot::ResponseInputImageType::InputImage,
                file_id: None,
                image_url: Some(format!(
                    "data:{};base64,{}",
                    image_media_type(source.media_type),
                    source.data
                )),
            }))
        }
        ct::BetaImageSource::Url(source) => {
            Some(ot::ResponseInputContent::Image(ot::ResponseInputImage {
                detail: None,
                type_: ot::ResponseInputImageType::InputImage,
                file_id: None,
                image_url: Some(source.url),
            }))
        }
        ct::BetaImageSource::File(source) => {
            Some(ot::ResponseInputContent::Image(ot::ResponseInputImage {
                detail: None,
                type_: ot::ResponseInputImageType::InputImage,
                file_id: Some(source.file_id),
                image_url: None,
            }))
        }
    }
}

fn document_block_to_input_content(
    block: ct::BetaRequestDocumentBlock,
) -> Option<ot::ResponseInputContent> {
    let filename = block.title;
    match block.source {
        ct::BetaDocumentSource::Base64Pdf(source) => {
            Some(ot::ResponseInputContent::File(ot::ResponseInputFile {
                type_: ot::ResponseInputFileType::InputFile,
                detail: None,
                file_data: Some(source.data),
                file_id: None,
                file_url: None,
                filename,
            }))
        }
        ct::BetaDocumentSource::PlainText(source) => {
            Some(ot::ResponseInputContent::File(ot::ResponseInputFile {
                type_: ot::ResponseInputFileType::InputFile,
                detail: None,
                file_data: Some(source.data),
                file_id: None,
                file_url: None,
                filename,
            }))
        }
        ct::BetaDocumentSource::UrlPdf(source) => {
            Some(ot::ResponseInputContent::File(ot::ResponseInputFile {
                type_: ot::ResponseInputFileType::InputFile,
                detail: None,
                file_data: None,
                file_id: None,
                file_url: Some(source.url),
                filename,
            }))
        }
        ct::BetaDocumentSource::File(source) => {
            Some(ot::ResponseInputContent::File(ot::ResponseInputFile {
                type_: ot::ResponseInputFileType::InputFile,
                detail: None,
                file_data: None,
                file_id: Some(source.file_id),
                file_url: None,
                filename,
            }))
        }
        ct::BetaDocumentSource::Content(source) => {
            let text = match source.content {
                ct::BetaContentBlockSourceContentPayload::Text(text) => text,
                ct::BetaContentBlockSourceContentPayload::Blocks(parts) => parts
                    .into_iter()
                    .filter_map(|part| match part {
                        ct::BetaContentBlockSourceContent::Text(text) => Some(text.text),
                        ct::BetaContentBlockSourceContent::Image(_) => None,
                    })
                    .collect::<Vec<_>>()
                    .join("\n"),
            };
            if text.is_empty() {
                None
            } else {
                Some(ot::ResponseInputContent::File(ot::ResponseInputFile {
                    type_: ot::ResponseInputFileType::InputFile,
                    detail: None,
                    file_data: Some(text),
                    file_id: None,
                    file_url: None,
                    filename,
                }))
            }
        }
    }
}

fn user_message_part_from_block(
    block: ct::BetaContentBlockParam,
) -> Option<ot::ResponseInputContent> {
    match block {
        ct::BetaContentBlockParam::Text(block) => Some(input_text_content(block.text)),
        ct::BetaContentBlockParam::Image(block) => image_block_to_input_content(block),
        ct::BetaContentBlockParam::RequestDocument(block) => document_block_to_input_content(block),
        ct::BetaContentBlockParam::SearchResult(block) => {
            let text = block
                .content
                .into_iter()
                .map(|entry| entry.text)
                .filter(|text| !text.is_empty())
                .collect::<Vec<_>>()
                .join("\n");
            let summary = if text.is_empty() {
                format!("{}\n{}", block.title, block.source)
            } else {
                format!("{}\n{}\n{}", block.title, block.source, text)
            };
            Some(input_text_content(summary))
        }
        ct::BetaContentBlockParam::ContainerUpload(block) => {
            Some(ot::ResponseInputContent::File(ot::ResponseInputFile {
                type_: ot::ResponseInputFileType::InputFile,
                detail: None,
                file_data: None,
                file_id: Some(block.file_id),
                file_url: None,
                filename: None,
            }))
        }
        _ => None,
    }
}

fn tool_result_block_to_text(block: ct::BetaToolResultContentBlockParam) -> String {
    match block {
        ct::BetaToolResultContentBlockParam::Text(part) => part.text,
        ct::BetaToolResultContentBlockParam::Image(part) => match part.source {
            ct::BetaImageSource::Base64(source) => {
                format!(
                    "data:{};base64,{}",
                    image_media_type(source.media_type),
                    source.data
                )
            }
            ct::BetaImageSource::Url(source) => source.url,
            ct::BetaImageSource::File(source) => format!("file_id:{}", source.file_id),
        },
        ct::BetaToolResultContentBlockParam::SearchResult(part) => {
            let content = part
                .content
                .into_iter()
                .map(|entry| entry.text)
                .collect::<Vec<_>>()
                .join("\n");
            if content.is_empty() {
                format!("{}\n{}", part.title, part.source)
            } else {
                format!("{}\n{}\n{}", part.title, part.source, content)
            }
        }
        ct::BetaToolResultContentBlockParam::Document(part) => {
            document_block_to_input_content(part)
                .and_then(|content| match content {
                    ot::ResponseInputContent::File(file) => file
                        .file_url
                        .or(file.file_id)
                        .or(file.filename)
                        .or(file.file_data),
                    _ => None,
                })
                .unwrap_or_default()
        }
        ct::BetaToolResultContentBlockParam::ToolReference(part) => part.tool_name,
    }
}

fn tool_result_blocks_to_text(parts: Vec<ct::BetaToolResultContentBlockParam>) -> String {
    parts
        .into_iter()
        .map(tool_result_block_to_text)
        .filter(|text| !text.is_empty())
        .collect::<Vec<_>>()
        .join("\n")
}

fn tool_result_blocks_to_input_contents(
    parts: Vec<ct::BetaToolResultContentBlockParam>,
) -> Option<Vec<ot::ResponseInputContent>> {
    let mut converted = Vec::new();
    for part in parts {
        match part {
            ct::BetaToolResultContentBlockParam::Text(part) => {
                converted.push(input_text_content(part.text));
            }
            ct::BetaToolResultContentBlockParam::Image(part) => {
                converted.push(image_block_to_input_content(part)?);
            }
            ct::BetaToolResultContentBlockParam::Document(part) => {
                converted.push(document_block_to_input_content(part)?);
            }
            ct::BetaToolResultContentBlockParam::SearchResult(_)
            | ct::BetaToolResultContentBlockParam::ToolReference(_) => return None,
        }
    }
    Some(converted)
}

fn tool_result_content_to_text(content: Option<ct::BetaToolResultBlockParamContent>) -> String {
    match content {
        Some(ct::BetaToolResultBlockParamContent::Text(text)) => text,
        Some(ct::BetaToolResultBlockParamContent::Blocks(parts)) => {
            tool_result_blocks_to_text(parts)
        }
        None => String::new(),
    }
}

fn tool_result_content_to_function_output(
    content: Option<ct::BetaToolResultBlockParamContent>,
) -> ot::ResponseFunctionCallOutputContent {
    match content {
        Some(ct::BetaToolResultBlockParamContent::Text(text)) => {
            ot::ResponseFunctionCallOutputContent::Text(text)
        }
        Some(ct::BetaToolResultBlockParamContent::Blocks(parts)) => {
            if let Some(contents) = tool_result_blocks_to_input_contents(parts.clone()) {
                ot::ResponseFunctionCallOutputContent::Content(contents)
            } else {
                ot::ResponseFunctionCallOutputContent::Text(tool_result_blocks_to_text(parts))
            }
        }
        None => ot::ResponseFunctionCallOutputContent::Text(String::new()),
    }
}

fn tool_result_content_to_custom_output(
    content: Option<ct::BetaToolResultBlockParamContent>,
) -> ot::ResponseCustomToolCallOutputContent {
    match content {
        Some(ct::BetaToolResultBlockParamContent::Text(text)) => {
            ot::ResponseCustomToolCallOutputContent::Text(text)
        }
        Some(ct::BetaToolResultBlockParamContent::Blocks(parts)) => {
            if let Some(contents) = tool_result_blocks_to_input_contents(parts.clone()) {
                ot::ResponseCustomToolCallOutputContent::Content(contents)
            } else {
                ot::ResponseCustomToolCallOutputContent::Text(tool_result_blocks_to_text(parts))
            }
        }
        None => ot::ResponseCustomToolCallOutputContent::Text(String::new()),
    }
}

fn mcp_result_text(content: Option<ct::BetaMcpToolResultBlockParamContent>) -> String {
    match content {
        Some(ct::BetaMcpToolResultBlockParamContent::Text(text)) => text,
        Some(ct::BetaMcpToolResultBlockParamContent::Blocks(parts)) => parts
            .into_iter()
            .map(|part| part.text)
            .filter(|text| !text.is_empty())
            .collect::<Vec<_>>()
            .join("\n"),
        None => String::new(),
    }
}

fn shell_output_text(stdout: String, stderr: String) -> String {
    if stderr.is_empty() {
        stdout
    } else if stdout.is_empty() {
        stderr
    } else {
        format!("stdout: {stdout}\nstderr: {stderr}")
    }
}

fn string_list(value: Option<&serde_json::Value>) -> Vec<String> {
    match value {
        Some(serde_json::Value::Array(values)) => values
            .iter()
            .filter_map(|value| value.as_str().map(ToString::to_string))
            .collect(),
        Some(serde_json::Value::String(value)) => vec![value.clone()],
        _ => Vec::new(),
    }
}

fn f64_field(input: &ct::JsonObject, key: &str) -> Option<f64> {
    input.get(key).and_then(|value| value.as_f64())
}

fn str_field<'a>(input: &'a ct::JsonObject, key: &str) -> Option<&'a str> {
    input.get(key).and_then(|value| value.as_str())
}

fn computer_mouse_button(input: &ct::JsonObject) -> ot::ResponseComputerMouseButton {
    match str_field(input, "button").unwrap_or("left") {
        "right" => ot::ResponseComputerMouseButton::Right,
        "wheel" => ot::ResponseComputerMouseButton::Wheel,
        "back" => ot::ResponseComputerMouseButton::Back,
        "forward" => ot::ResponseComputerMouseButton::Forward,
        _ => ot::ResponseComputerMouseButton::Left,
    }
}

fn computer_action_from_input(input: &ct::JsonObject) -> Option<ot::ResponseComputerAction> {
    let action = str_field(input, "action")
        .or_else(|| str_field(input, "type"))?
        .to_string();

    match action.as_str() {
        "click" => Some(ot::ResponseComputerAction::Click {
            button: computer_mouse_button(input),
            x: f64_field(input, "x")?,
            y: f64_field(input, "y")?,
        }),
        "double_click" => Some(ot::ResponseComputerAction::DoubleClick {
            x: f64_field(input, "x")?,
            y: f64_field(input, "y")?,
        }),
        "drag" => {
            let path = input
                .get("path")
                .and_then(|value| value.as_array())
                .map(|values| {
                    values
                        .iter()
                        .filter_map(|value| {
                            let x = value.get("x")?.as_f64()?;
                            let y = value.get("y")?.as_f64()?;
                            Some(ot::ResponseComputerPoint { x, y })
                        })
                        .collect::<Vec<_>>()
                })
                .unwrap_or_default();
            if path.is_empty() {
                None
            } else {
                Some(ot::ResponseComputerAction::Drag { path })
            }
        }
        "keypress" => {
            let mut keys = string_list(input.get("keys"));
            if keys.is_empty()
                && let Some(key) = str_field(input, "key")
            {
                keys.push(key.to_string());
            }
            if keys.is_empty() {
                None
            } else {
                Some(ot::ResponseComputerAction::Keypress { keys })
            }
        }
        "move" => Some(ot::ResponseComputerAction::Move {
            x: f64_field(input, "x")?,
            y: f64_field(input, "y")?,
        }),
        "screenshot" => Some(ot::ResponseComputerAction::Screenshot),
        "scroll" => Some(ot::ResponseComputerAction::Scroll {
            scroll_x: f64_field(input, "scroll_x")
                .or_else(|| f64_field(input, "delta_x"))
                .unwrap_or_default(),
            scroll_y: f64_field(input, "scroll_y")
                .or_else(|| f64_field(input, "delta_y"))
                .unwrap_or_default(),
            x: f64_field(input, "x").unwrap_or_default(),
            y: f64_field(input, "y").unwrap_or_default(),
        }),
        "type" => Some(ot::ResponseComputerAction::Type {
            text: str_field(input, "text")?.to_string(),
        }),
        "wait" => Some(ot::ResponseComputerAction::Wait),
        _ => None,
    }
}

fn computer_screenshot_from_tool_result_content(
    content: Option<ct::BetaToolResultBlockParamContent>,
) -> Option<ot::ResponseComputerToolCallOutputScreenshot> {
    let ct::BetaToolResultBlockParamContent::Blocks(parts) = content? else {
        return None;
    };

    for part in parts {
        let ct::BetaToolResultContentBlockParam::Image(image) = part else {
            continue;
        };
        return match image.source {
            ct::BetaImageSource::Base64(source) => {
                Some(ot::ResponseComputerToolCallOutputScreenshot {
                    type_: ot::ResponseComputerToolCallOutputScreenshotType::ComputerScreenshot,
                    file_id: None,
                    image_url: Some(format!(
                        "data:{};base64,{}",
                        image_media_type(source.media_type),
                        source.data
                    )),
                })
            }
            ct::BetaImageSource::Url(source) => {
                Some(ot::ResponseComputerToolCallOutputScreenshot {
                    type_: ot::ResponseComputerToolCallOutputScreenshotType::ComputerScreenshot,
                    file_id: None,
                    image_url: Some(source.url),
                })
            }
            ct::BetaImageSource::File(source) => {
                Some(ot::ResponseComputerToolCallOutputScreenshot {
                    type_: ot::ResponseComputerToolCallOutputScreenshotType::ComputerScreenshot,
                    file_id: Some(source.file_id),
                    image_url: None,
                })
            }
        };
    }

    None
}

fn file_search_queries(input: &ct::JsonObject) -> Vec<String> {
    let mut queries = string_list(input.get("queries"));
    if queries.is_empty() {
        for key in ["query", "pattern", "term"] {
            if let Some(value) = str_field(input, key) {
                queries.push(value.to_string());
                break;
            }
        }
    }
    queries
}

fn shell_commands(input: &ct::JsonObject) -> Vec<String> {
    let mut commands = string_list(input.get("commands"));
    if commands.is_empty() {
        for key in ["command", "cmd"] {
            if let Some(value) = str_field(input, key) {
                commands.push(value.to_string());
                break;
            }
        }
    }
    commands
}

fn apply_claude_tool_choice(
    tool_choice: Option<BetaToolChoice>,
    tool_registry: &BTreeMap<String, ClaudeToolKind>,
) -> Option<ResponseToolChoice> {
    match tool_choice {
        Some(BetaToolChoice::Auto(_)) => {
            Some(ResponseToolChoice::Options(ResponseToolChoiceOptions::Auto))
        }
        Some(BetaToolChoice::Any(_)) => Some(ResponseToolChoice::Options(
            ResponseToolChoiceOptions::Required,
        )),
        Some(BetaToolChoice::None(_)) => {
            Some(ResponseToolChoice::Options(ResponseToolChoiceOptions::None))
        }
        Some(BetaToolChoice::Tool(choice)) => match tool_registry.get(&choice.name) {
            Some(ClaudeToolKind::Custom) => {
                Some(ResponseToolChoice::Custom(ot::ResponseToolChoiceCustom {
                    name: choice.name,
                    type_: ot::ResponseToolChoiceCustomType::Custom,
                }))
            }
            Some(ClaudeToolKind::Mcp) => Some(ResponseToolChoice::Mcp(ot::ResponseToolChoiceMcp {
                server_label: choice.name,
                type_: ot::ResponseToolChoiceMcpType::Mcp,
                name: None,
            })),
            Some(ClaudeToolKind::ApplyPatch) => Some(ResponseToolChoice::ApplyPatch(
                ot::ResponseToolChoiceApplyPatch {
                    type_: ot::ResponseToolChoiceApplyPatchType::ApplyPatch,
                },
            )),
            Some(ClaudeToolKind::Shell) => {
                Some(ResponseToolChoice::Shell(ot::ResponseToolChoiceShell {
                    type_: ot::ResponseToolChoiceShellType::Shell,
                }))
            }
            Some(ClaudeToolKind::FileSearch) => {
                Some(ResponseToolChoice::Types(ot::ResponseToolChoiceTypes {
                    type_: ot::ResponseToolChoiceBuiltinType::FileSearch,
                }))
            }
            Some(ClaudeToolKind::Computer) => {
                Some(ResponseToolChoice::Types(ot::ResponseToolChoiceTypes {
                    type_: ot::ResponseToolChoiceBuiltinType::ComputerUsePreview,
                }))
            }
            Some(ClaudeToolKind::CodeInterpreter) => {
                Some(ResponseToolChoice::Types(ot::ResponseToolChoiceTypes {
                    type_: ot::ResponseToolChoiceBuiltinType::CodeInterpreter,
                }))
            }
            Some(ClaudeToolKind::WebSearch | ClaudeToolKind::WebFetch) => {
                Some(ResponseToolChoice::Types(ot::ResponseToolChoiceTypes {
                    type_: ot::ResponseToolChoiceBuiltinType::WebSearchPreview,
                }))
            }
            _ => Some(ResponseToolChoice::Function(ResponseToolChoiceFunction {
                name: choice.name,
                type_: ResponseToolChoiceFunctionType::Function,
            })),
        },
        None => None,
    }
}

impl TryFrom<ClaudeCreateMessageRequest> for OpenAiCreateResponseRequest {
    type Error = TransformError;

    fn try_from(value: ClaudeCreateMessageRequest) -> Result<Self, TransformError> {
        let body = value.body;
        let model = claude_model_to_string(&body.model);

        let instructions = beta_system_prompt_to_text(body.system.clone());
        let parallel_tool_calls = match body.tool_choice.as_ref() {
            Some(BetaToolChoice::Auto(choice)) => choice.disable_parallel_tool_use.map(|v| !v),
            Some(BetaToolChoice::Any(choice)) => choice.disable_parallel_tool_use.map(|v| !v),
            Some(BetaToolChoice::Tool(choice)) => choice.disable_parallel_tool_use.map(|v| !v),
            Some(BetaToolChoice::None(_)) | None => None,
        };

        let mut tool_registry = BTreeMap::new();
        if let Some(tools) = body.tools.as_ref() {
            for tool in tools {
                match tool {
                    BetaToolUnion::Custom(tool) => {
                        tool_registry.insert(
                            tool.name.clone(),
                            if matches!(tool.type_, Some(ct::BetaCustomToolType::Custom)) {
                                ClaudeToolKind::Custom
                            } else {
                                ClaudeToolKind::Function
                            },
                        );
                    }
                    BetaToolUnion::CodeExecution20250522(_)
                    | BetaToolUnion::CodeExecution20250825(_) => {
                        tool_registry.insert(
                            "code_execution".to_string(),
                            ClaudeToolKind::CodeInterpreter,
                        );
                    }
                    BetaToolUnion::ComputerUse20241022(_)
                    | BetaToolUnion::ComputerUse20250124(_)
                    | BetaToolUnion::ComputerUse20251124(_) => {
                        tool_registry.insert("computer".to_string(), ClaudeToolKind::Computer);
                    }
                    BetaToolUnion::WebSearch20250305(_) => {
                        tool_registry.insert("web_search".to_string(), ClaudeToolKind::WebSearch);
                    }
                    BetaToolUnion::WebFetch20250910(_) => {
                        tool_registry.insert("web_fetch".to_string(), ClaudeToolKind::WebFetch);
                    }
                    BetaToolUnion::Bash20241022(_) | BetaToolUnion::Bash20250124(_) => {
                        tool_registry.insert("bash".to_string(), ClaudeToolKind::Shell);
                    }
                    BetaToolUnion::ToolSearchBm25_20251119(_) => {
                        tool_registry.insert(
                            "tool_search_tool_bm25".to_string(),
                            ClaudeToolKind::FileSearch,
                        );
                    }
                    BetaToolUnion::ToolSearchRegex20251119(_) => {
                        tool_registry.insert(
                            "tool_search_tool_regex".to_string(),
                            ClaudeToolKind::FileSearch,
                        );
                    }
                    BetaToolUnion::TextEditor20241022(_) => {
                        tool_registry
                            .insert("str_replace_editor".to_string(), ClaudeToolKind::ApplyPatch);
                    }
                    BetaToolUnion::TextEditor20250124(_)
                    | BetaToolUnion::TextEditor20250429(_)
                    | BetaToolUnion::TextEditor20250728(_) => {
                        tool_registry.insert(
                            "str_replace_based_edit_tool".to_string(),
                            ClaudeToolKind::ApplyPatch,
                        );
                    }
                    BetaToolUnion::McpToolset(tool) => {
                        tool_registry.insert(tool.mcp_server_name.clone(), ClaudeToolKind::Mcp);
                    }
                    BetaToolUnion::Memory20250818(_) => {}
                }
            }
        }
        if let Some(servers) = body.mcp_servers.as_ref() {
            for server in servers {
                tool_registry.insert(server.name.clone(), ClaudeToolKind::Mcp);
            }
        }
        let tool_choice = apply_claude_tool_choice(body.tool_choice.clone(), &tool_registry);

        let reasoning_effort_from_thinking = match body.thinking.clone() {
            Some(BetaThinkingConfigParam::Enabled(config)) => Some(if config.budget_tokens == 0 {
                ResponseReasoningEffort::None
            } else if config.budget_tokens <= 4096 {
                ResponseReasoningEffort::Minimal
            } else if config.budget_tokens <= 8192 {
                ResponseReasoningEffort::Low
            } else if config.budget_tokens <= 16384 {
                ResponseReasoningEffort::Medium
            } else if config.budget_tokens <= 32768 {
                ResponseReasoningEffort::High
            } else {
                ResponseReasoningEffort::XHigh
            }),
            Some(BetaThinkingConfigParam::Disabled(_)) => Some(ResponseReasoningEffort::None),
            Some(BetaThinkingConfigParam::Adaptive(_)) => Some(ResponseReasoningEffort::Medium),
            None => None,
        };
        let reasoning = reasoning_effort_from_thinking.map(|effort| ResponseReasoning {
            effort: Some(effort),
            generate_summary: None,
            summary: None,
        });
        let output_schema = body
            .output_config
            .as_ref()
            .and_then(|config| config.format.as_ref());
        let text_format = output_schema.map(|schema| {
            ResponseTextFormatConfig::JsonSchema(ResponseFormatTextJsonSchemaConfig {
                name: "output".to_string(),
                schema: schema.schema.clone(),
                type_: ResponseFormatTextJsonSchemaConfigType::JsonSchema,
                description: None,
                strict: None,
            })
        });
        let text_verbosity = body
            .output_config
            .as_ref()
            .and_then(|config| config.effort.as_ref())
            .map(|effort| match effort {
                BetaOutputEffort::Low => ResponseTextVerbosity::Low,
                BetaOutputEffort::Medium => ResponseTextVerbosity::Medium,
                BetaOutputEffort::High | BetaOutputEffort::XHigh | BetaOutputEffort::Max => {
                    ResponseTextVerbosity::High
                }
            });
        let text = if text_format.is_some() || text_verbosity.is_some() {
            Some(ResponseTextConfig {
                format: text_format,
                verbosity: text_verbosity,
            })
        } else {
            None
        };
        let context_management = body.context_management.as_ref().and_then(|config| {
            let mut entries = Vec::new();
            if let Some(edits) = config.edits.as_ref() {
                for edit in edits {
                    if let BetaContextManagementEdit::Compact(compact) = edit {
                        entries.push(ResponseContextManagementEntry {
                            type_: ResponseContextManagementType::Compaction,
                            compact_threshold: compact
                                .trigger
                                .as_ref()
                                .map(|trigger| trigger.value),
                        });
                    }
                }
            }

            if entries.is_empty() {
                None
            } else {
                Some(entries)
            }
        });
        let truncation = body
            .context_management
            .as_ref()
            .map(|_| ResponseTruncation::Auto);

        let mut input_items = Vec::new();
        let mut recorded_calls = BTreeMap::<String, RecordedToolCall>::new();
        let mut assistant_message_index = 0u64;
        let mut reasoning_index = 0u64;

        for message in body.messages {
            match (message.role, message.content) {
                (BetaMessageRole::User, ct::BetaMessageContent::Text(text)) => {
                    if !text.is_empty() {
                        input_items.push(ResponseInputItem::Message(ResponseInputMessage {
                            content: ResponseInputMessageContent::Text(text),
                            role: ResponseInputMessageRole::User,
                            phase: None,
                            status: None,
                            type_: Some(ResponseInputMessageType::Message),
                        }));
                    }
                }
                (BetaMessageRole::User, ct::BetaMessageContent::Blocks(blocks)) => {
                    let mut message_parts = Vec::new();
                    for block in blocks {
                        if let Some(part) = user_message_part_from_block(block.clone()) {
                            message_parts.push(part);
                            continue;
                        }

                        match block {
                            ct::BetaContentBlockParam::ToolResult(block) => {
                                flush_input_parts(
                                    &mut input_items,
                                    ResponseInputMessageRole::User,
                                    &mut message_parts,
                                );
                                let kind = recorded_calls
                                    .get(&block.tool_use_id)
                                    .map(|record| record.kind)
                                    .unwrap_or(ClaudeToolKind::Function);
                                let is_error = block.is_error.unwrap_or(false);
                                match kind {
                                    ClaudeToolKind::Function => {
                                        input_items.push(ResponseInputItem::FunctionCallOutput(
                                            ot::ResponseFunctionCallOutput {
                                                call_id: block.tool_use_id,
                                                output: tool_result_content_to_function_output(
                                                    block.content,
                                                ),
                                                type_: ot::ResponseFunctionCallOutputType::FunctionCallOutput,
                                                id: None,
                                                status: Some(if is_error {
                                                    ot::ResponseItemStatus::Incomplete
                                                } else {
                                                    ot::ResponseItemStatus::Completed
                                                }),
                                            },
                                        ));
                                    }
                                    ClaudeToolKind::Custom | ClaudeToolKind::ApplyPatch => {
                                        input_items.push(ResponseInputItem::CustomToolCallOutput(
                                            ot::ResponseCustomToolCallOutput {
                                                call_id: block.tool_use_id,
                                                output: tool_result_content_to_custom_output(
                                                    block.content,
                                                ),
                                                type_: ot::ResponseCustomToolCallOutputType::CustomToolCallOutput,
                                                id: None,
                                            },
                                        ));
                                    }
                                    ClaudeToolKind::Mcp => {
                                        if let Some(record) = recorded_calls.get(&block.tool_use_id)
                                            && let Some(ResponseInputItem::McpCall(call)) =
                                                input_items.get_mut(record.item_index)
                                        {
                                            let text = tool_result_content_to_text(block.content);
                                            call.output = (!is_error && !text.is_empty())
                                                .then_some(text.clone());
                                            call.error = if is_error {
                                                Some(if text.is_empty() {
                                                    "mcp_tool_result_error".to_string()
                                                } else {
                                                    text
                                                })
                                            } else {
                                                None
                                            };
                                            call.status = Some(if is_error {
                                                ot::ResponseToolCallStatus::Failed
                                            } else {
                                                ot::ResponseToolCallStatus::Completed
                                            });
                                        }
                                    }
                                    ClaudeToolKind::CodeInterpreter => {
                                        let output_text =
                                            tool_result_content_to_text(block.content);
                                        if let Some(record) = recorded_calls.get(&block.tool_use_id)
                                            && let Some(ResponseInputItem::CodeInterpreterToolCall(
                                                call,
                                            )) = input_items.get_mut(record.item_index)
                                        {
                                            call.outputs =
                                                (!output_text.is_empty()).then_some(vec![
                                                    ot::ResponseCodeInterpreterOutputItem::Logs {
                                                        logs: output_text,
                                                    },
                                                ]);
                                            call.status = if is_error {
                                                ot::ResponseCodeInterpreterToolCallStatus::Failed
                                            } else {
                                                ot::ResponseCodeInterpreterToolCallStatus::Completed
                                            };
                                        }
                                    }
                                    ClaudeToolKind::Shell => {
                                        let output_text =
                                            tool_result_content_to_text(block.content);
                                        input_items.push(ResponseInputItem::ShellCallOutput(
                                            ot::ResponseShellCallOutput {
                                                call_id: block.tool_use_id,
                                                output: if output_text.is_empty() {
                                                    Vec::new()
                                                } else {
                                                    vec![ot::ResponseFunctionShellCallOutputContent {
                                                        outcome: ot::ResponseShellCallOutcome::Exit {
                                                            exit_code: if is_error { 1 } else { 0 },
                                                        },
                                                        stderr: if is_error {
                                                            output_text.clone()
                                                        } else {
                                                            String::new()
                                                        },
                                                        stdout: if is_error {
                                                            String::new()
                                                        } else {
                                                            output_text
                                                        },
                                                    }]
                                                },
                                                type_: ot::ResponseShellCallOutputType::ShellCallOutput,
                                                id: None,
                                                max_output_length: None,
                                                status: Some(if is_error {
                                                    ot::ResponseItemStatus::Incomplete
                                                } else {
                                                    ot::ResponseItemStatus::Completed
                                                }),
                                            },
                                        ));
                                    }
                                    ClaudeToolKind::FileSearch => {
                                        let output_text =
                                            tool_result_content_to_text(block.content);
                                        if let Some(record) = recorded_calls.get(&block.tool_use_id)
                                            && let Some(ResponseInputItem::FileSearchToolCall(call)) =
                                                input_items.get_mut(record.item_index)
                                        {
                                            call.results =
                                                (!output_text.is_empty()).then_some(vec![
                                                    ot::ResponseFileSearchResult {
                                                        text: Some(output_text),
                                                        ..Default::default()
                                                    },
                                                ]);
                                            call.status = if is_error {
                                                ot::ResponseFileSearchToolCallStatus::Failed
                                            } else {
                                                ot::ResponseFileSearchToolCallStatus::Completed
                                            };
                                        }
                                    }
                                    ClaudeToolKind::Computer => {
                                        if let Some(screenshot) =
                                            computer_screenshot_from_tool_result_content(
                                                block.content,
                                            )
                                        {
                                            input_items.push(ResponseInputItem::ComputerCallOutput(
                                                ot::ResponseComputerCallOutput {
                                                    call_id: block.tool_use_id,
                                                    output: screenshot,
                                                    type_: ot::ResponseComputerCallOutputType::ComputerCallOutput,
                                                    id: None,
                                                    acknowledged_safety_checks: None,
                                                    status: Some(if is_error {
                                                        ot::ResponseItemStatus::Incomplete
                                                    } else {
                                                        ot::ResponseItemStatus::Completed
                                                    }),
                                                },
                                            ));
                                        }
                                    }
                                    ClaudeToolKind::WebSearch | ClaudeToolKind::WebFetch => {}
                                }
                            }
                            ct::BetaContentBlockParam::McpToolResult(block) => {
                                flush_input_parts(
                                    &mut input_items,
                                    ResponseInputMessageRole::User,
                                    &mut message_parts,
                                );
                                let output_text = mcp_result_text(block.content);
                                if let Some(record) = recorded_calls.get(&block.tool_use_id)
                                    && let Some(ResponseInputItem::McpCall(call)) =
                                        input_items.get_mut(record.item_index)
                                {
                                    call.output = (!block.is_error.unwrap_or(false)
                                        && !output_text.is_empty())
                                    .then_some(output_text.clone());
                                    call.error = if block.is_error.unwrap_or(false) {
                                        Some(if output_text.is_empty() {
                                            "mcp_tool_result_error".to_string()
                                        } else {
                                            output_text
                                        })
                                    } else {
                                        None
                                    };
                                    call.status = Some(if block.is_error.unwrap_or(false) {
                                        ot::ResponseToolCallStatus::Failed
                                    } else {
                                        ot::ResponseToolCallStatus::Completed
                                    });
                                }
                            }
                            ct::BetaContentBlockParam::WebSearchToolResult(block) => {
                                flush_input_parts(
                                    &mut input_items,
                                    ResponseInputMessageRole::User,
                                    &mut message_parts,
                                );
                                let status = match block.content {
                                    ct::BetaWebSearchToolResultBlockParamContent::Results(
                                        results,
                                    ) => {
                                        let sources = results
                                            .into_iter()
                                            .map(|result| ot::ResponseFunctionWebSearchSource {
                                                type_: ot::ResponseFunctionWebSearchSourceType::Url,
                                                url: result.url,
                                            })
                                            .collect::<Vec<_>>();
                                        if let Some(record) = recorded_calls.get(&block.tool_use_id)
                                            && let Some(ResponseInputItem::FunctionWebSearch(call)) =
                                                input_items.get_mut(record.item_index)
                                        {
                                            let (query, queries) = match &call.action {
                                                ot::ResponseFunctionWebSearchAction::Search {
                                                    query,
                                                    queries,
                                                    ..
                                                } => (query.clone(), queries.clone()),
                                                _ => (None, None),
                                            };
                                            call.action =
                                                ot::ResponseFunctionWebSearchAction::Search {
                                                    query,
                                                    queries,
                                                    sources: (!sources.is_empty())
                                                        .then_some(sources),
                                                };
                                            call.status =
                                                ot::ResponseFunctionWebSearchStatus::Completed;
                                        }
                                        ot::ResponseFunctionWebSearchStatus::Completed
                                    }
                                    ct::BetaWebSearchToolResultBlockParamContent::Error(_) => {
                                        if let Some(record) = recorded_calls.get(&block.tool_use_id)
                                            && let Some(ResponseInputItem::FunctionWebSearch(call)) =
                                                input_items.get_mut(record.item_index)
                                        {
                                            call.status =
                                                ot::ResponseFunctionWebSearchStatus::Failed;
                                        }
                                        ot::ResponseFunctionWebSearchStatus::Failed
                                    }
                                };
                                if !recorded_calls.contains_key(&block.tool_use_id) {
                                    input_items.push(ResponseInputItem::FunctionWebSearch(
                                        ot::ResponseFunctionWebSearch {
                                            id: Some(block.tool_use_id),
                                            action: ot::ResponseFunctionWebSearchAction::Search {
                                                query: None,
                                                queries: None,
                                                sources: None,
                                            },
                                            status,
                                            type_: ot::ResponseFunctionWebSearchType::WebSearchCall,
                                        },
                                    ));
                                }
                            }
                            ct::BetaContentBlockParam::WebFetchToolResult(block) => {
                                flush_input_parts(
                                    &mut input_items,
                                    ResponseInputMessageRole::User,
                                    &mut message_parts,
                                );
                                match block.content {
                                    ct::BetaWebFetchToolResultBlockParamContent::Result(result) => {
                                        if let Some(record) = recorded_calls.get(&block.tool_use_id)
                                            && let Some(ResponseInputItem::FunctionWebSearch(call)) =
                                                input_items.get_mut(record.item_index)
                                        {
                                            call.action =
                                                ot::ResponseFunctionWebSearchAction::OpenPage {
                                                    url: Some(result.url.clone()),
                                                };
                                            call.status =
                                                ot::ResponseFunctionWebSearchStatus::Completed;
                                        } else {
                                            input_items.push(ResponseInputItem::FunctionWebSearch(
                                                ot::ResponseFunctionWebSearch {
                                                    id: Some(block.tool_use_id),
                                                    action: ot::ResponseFunctionWebSearchAction::OpenPage {
                                                        url: Some(result.url),
                                                    },
                                                    status: ot::ResponseFunctionWebSearchStatus::Completed,
                                                    type_: ot::ResponseFunctionWebSearchType::WebSearchCall,
                                                },
                                            ));
                                        }
                                    }
                                    ct::BetaWebFetchToolResultBlockParamContent::Error(_) => {
                                        if let Some(record) = recorded_calls.get(&block.tool_use_id)
                                            && let Some(ResponseInputItem::FunctionWebSearch(call)) =
                                                input_items.get_mut(record.item_index)
                                        {
                                            call.status =
                                                ot::ResponseFunctionWebSearchStatus::Failed;
                                        } else {
                                            input_items.push(ResponseInputItem::FunctionWebSearch(
                                                ot::ResponseFunctionWebSearch {
                                                    id: Some(block.tool_use_id),
                                                    action: ot::ResponseFunctionWebSearchAction::OpenPage {
                                                        url: None,
                                                    },
                                                    status: ot::ResponseFunctionWebSearchStatus::Failed,
                                                    type_: ot::ResponseFunctionWebSearchType::WebSearchCall,
                                                },
                                            ));
                                        }
                                    }
                                }
                            }
                            ct::BetaContentBlockParam::CodeExecutionToolResult(block) => {
                                flush_input_parts(
                                    &mut input_items,
                                    ResponseInputMessageRole::User,
                                    &mut message_parts,
                                );
                                let (output_text, status) = match block.content {
                                    ct::BetaCodeExecutionToolResultBlockParamContent::Result(
                                        result,
                                    ) => (
                                        shell_output_text(result.stdout, result.stderr),
                                        ot::ResponseCodeInterpreterToolCallStatus::Completed,
                                    ),
                                    ct::BetaCodeExecutionToolResultBlockParamContent::Error(
                                        err,
                                    ) => (
                                        format!("code_execution_error:{:?}", err.error_code),
                                        ot::ResponseCodeInterpreterToolCallStatus::Failed,
                                    ),
                                };
                                if let Some(record) = recorded_calls.get(&block.tool_use_id)
                                    && let Some(ResponseInputItem::CodeInterpreterToolCall(call)) =
                                        input_items.get_mut(record.item_index)
                                {
                                    call.outputs = (!output_text.is_empty()).then_some(vec![
                                        ot::ResponseCodeInterpreterOutputItem::Logs {
                                            logs: output_text,
                                        },
                                    ]);
                                    call.status = status;
                                } else {
                                    input_items.push(ResponseInputItem::CodeInterpreterToolCall(
                                        ot::ResponseCodeInterpreterToolCall {
                                            id: block.tool_use_id,
                                            code: String::new(),
                                            container_id: String::new(),
                                            outputs: (!output_text.is_empty()).then_some(vec![
                                                ot::ResponseCodeInterpreterOutputItem::Logs {
                                                    logs: output_text,
                                                },
                                            ]),
                                            status,
                                            type_: ot::ResponseCodeInterpreterToolCallType::CodeInterpreterCall,
                                        },
                                    ));
                                }
                            }
                            ct::BetaContentBlockParam::BashCodeExecutionToolResult(block) => {
                                flush_input_parts(
                                    &mut input_items,
                                    ResponseInputMessageRole::User,
                                    &mut message_parts,
                                );
                                let (stdout, stderr, outcome) = match block.content {
                                    ct::BetaBashCodeExecutionToolResultBlockParamContent::Result(result) => (
                                        result.stdout,
                                        result.stderr,
                                        ot::ResponseShellCallOutcome::Exit { exit_code: 0 },
                                    ),
                                    ct::BetaBashCodeExecutionToolResultBlockParamContent::Error(err) => (
                                        String::new(),
                                        format!("bash_code_execution_error:{:?}", err.error_code),
                                        if matches!(
                                            err.error_code,
                                            ct::BetaBashCodeExecutionToolResultErrorCode::ExecutionTimeExceeded
                                        ) {
                                            ot::ResponseShellCallOutcome::Timeout
                                        } else {
                                            ot::ResponseShellCallOutcome::Exit { exit_code: 1 }
                                        },
                                    ),
                                };
                                input_items.push(ResponseInputItem::ShellCallOutput(
                                    ot::ResponseShellCallOutput {
                                        call_id: block.tool_use_id,
                                        output: vec![ot::ResponseFunctionShellCallOutputContent {
                                            outcome,
                                            stderr,
                                            stdout,
                                        }],
                                        type_: ot::ResponseShellCallOutputType::ShellCallOutput,
                                        id: None,
                                        max_output_length: None,
                                        status: Some(ot::ResponseItemStatus::Completed),
                                    },
                                ));
                            }
                            ct::BetaContentBlockParam::TextEditorCodeExecutionToolResult(block) => {
                                flush_input_parts(
                                    &mut input_items,
                                    ResponseInputMessageRole::User,
                                    &mut message_parts,
                                );
                                let output = match block.content {
                                    ct::BetaTextEditorCodeExecutionToolResultBlockParamContent::View(view) => view.content,
                                    ct::BetaTextEditorCodeExecutionToolResultBlockParamContent::Create(create) => {
                                        format!("file_updated:{}", create.is_file_update)
                                    }
                                    ct::BetaTextEditorCodeExecutionToolResultBlockParamContent::StrReplace(replace) => {
                                        replace.lines.unwrap_or_default().join("\n")
                                    }
                                    ct::BetaTextEditorCodeExecutionToolResultBlockParamContent::Error(err) => err
                                        .error_message
                                        .unwrap_or_else(|| {
                                            format!(
                                                "text_editor_code_execution_error:{:?}",
                                                err.error_code
                                            )
                                        }),
                                };
                                input_items.push(ResponseInputItem::CustomToolCallOutput(
                                    ot::ResponseCustomToolCallOutput {
                                        call_id: block.tool_use_id,
                                        output: ot::ResponseCustomToolCallOutputContent::Text(output),
                                        type_: ot::ResponseCustomToolCallOutputType::CustomToolCallOutput,
                                        id: None,
                                    },
                                ));
                            }
                            ct::BetaContentBlockParam::ToolSearchToolResult(block) => {
                                flush_input_parts(
                                    &mut input_items,
                                    ResponseInputMessageRole::User,
                                    &mut message_parts,
                                );
                                match block.content {
                                    ct::BetaToolSearchToolResultBlockParamContent::Result(
                                        result,
                                    ) => {
                                        let results = result
                                            .tool_references
                                            .into_iter()
                                            .map(|reference| ot::ResponseFileSearchResult {
                                                filename: Some(reference.tool_name.clone()),
                                                text: Some(reference.tool_name),
                                                ..Default::default()
                                            })
                                            .collect::<Vec<_>>();
                                        if let Some(record) = recorded_calls.get(&block.tool_use_id)
                                            && let Some(ResponseInputItem::FileSearchToolCall(call)) =
                                                input_items.get_mut(record.item_index)
                                        {
                                            call.results = Some(results);
                                            call.status =
                                                ot::ResponseFileSearchToolCallStatus::Completed;
                                        } else {
                                            input_items.push(ResponseInputItem::FileSearchToolCall(
                                                ot::ResponseFileSearchToolCall {
                                                    id: block.tool_use_id,
                                                    queries: Vec::new(),
                                                    status: ot::ResponseFileSearchToolCallStatus::Completed,
                                                    type_: ot::ResponseFileSearchToolCallType::FileSearchCall,
                                                    results: Some(results),
                                                },
                                            ));
                                        }
                                    }
                                    ct::BetaToolSearchToolResultBlockParamContent::Error(err) => {
                                        if let Some(record) = recorded_calls.get(&block.tool_use_id)
                                            && let Some(ResponseInputItem::FileSearchToolCall(call)) =
                                                input_items.get_mut(record.item_index)
                                        {
                                            call.status =
                                                ot::ResponseFileSearchToolCallStatus::Failed;
                                            call.results =
                                                Some(vec![ot::ResponseFileSearchResult {
                                                    text: Some(format!(
                                                        "tool_search_error:{:?}",
                                                        err.error_code
                                                    )),
                                                    ..Default::default()
                                                }]);
                                        } else {
                                            input_items.push(ResponseInputItem::FileSearchToolCall(
                                                ot::ResponseFileSearchToolCall {
                                                    id: block.tool_use_id,
                                                    queries: Vec::new(),
                                                    status: ot::ResponseFileSearchToolCallStatus::Failed,
                                                    type_: ot::ResponseFileSearchToolCallType::FileSearchCall,
                                                    results: Some(vec![ot::ResponseFileSearchResult {
                                                        text: Some(format!(
                                                            "tool_search_error:{:?}",
                                                            err.error_code
                                                        )),
                                                        ..Default::default()
                                                    }]),
                                                },
                                            ));
                                        }
                                    }
                                }
                            }
                            ct::BetaContentBlockParam::Compaction(block) => {
                                flush_input_parts(
                                    &mut input_items,
                                    ResponseInputMessageRole::User,
                                    &mut message_parts,
                                );
                                input_items.push(ResponseInputItem::CompactionItem(
                                    ot::ResponseCompactionItemParam {
                                        encrypted_content: block.content.unwrap_or_default(),
                                        type_: ot::ResponseCompactionItemType::Compaction,
                                        id: None,
                                        created_by: None,
                                    },
                                ));
                            }
                            other => {
                                message_parts.push(input_text_content(json_string(&other)));
                            }
                        }
                    }
                    flush_input_parts(
                        &mut input_items,
                        ResponseInputMessageRole::User,
                        &mut message_parts,
                    );
                }
                (BetaMessageRole::Assistant, ct::BetaMessageContent::Text(text)) => {
                    if !text.is_empty() {
                        input_items.push(output_message_item(
                            format!("msg_{assistant_message_index}"),
                            text,
                        ));
                        assistant_message_index += 1;
                    }
                }
                (BetaMessageRole::Assistant, ct::BetaMessageContent::Blocks(blocks)) => {
                    for block in blocks {
                        match block {
                            ct::BetaContentBlockParam::Text(block) => {
                                if !block.text.is_empty() {
                                    input_items.push(output_message_item(
                                        format!("msg_{assistant_message_index}"),
                                        block.text,
                                    ));
                                    assistant_message_index += 1;
                                }
                            }
                            ct::BetaContentBlockParam::Thinking(block) => {
                                input_items.push(ResponseInputItem::ReasoningItem(
                                    ot::ResponseReasoningItem {
                                        id: Some(block.signature),
                                        summary: vec![ot::ResponseSummaryTextContent {
                                            text: block.thinking,
                                            type_: ot::ResponseSummaryTextContentType::SummaryText,
                                        }],
                                        type_: ot::ResponseReasoningItemType::Reasoning,
                                        content: None,
                                        encrypted_content: None,
                                        status: Some(ot::ResponseItemStatus::Completed),
                                    },
                                ));
                            }
                            ct::BetaContentBlockParam::RedactedThinking(block) => {
                                input_items.push(ResponseInputItem::ReasoningItem(
                                    ot::ResponseReasoningItem {
                                        id: Some(format!("redacted_reasoning_{reasoning_index}")),
                                        summary: Vec::new(),
                                        type_: ot::ResponseReasoningItemType::Reasoning,
                                        content: None,
                                        encrypted_content: Some(block.data),
                                        status: Some(ot::ResponseItemStatus::Completed),
                                    },
                                ));
                                reasoning_index += 1;
                            }
                            ct::BetaContentBlockParam::ToolUse(block) => {
                                let tool_name = block.name.clone();
                                let call_id = block.id.clone();
                                let input_json = json_string(&block.input);
                                let mut actual_kind = tool_registry
                                    .get(&tool_name)
                                    .copied()
                                    .unwrap_or(ClaudeToolKind::Function);
                                let item = match actual_kind {
                                    ClaudeToolKind::Function => ResponseInputItem::FunctionToolCall(
                                        ot::ResponseFunctionToolCall {
                                            arguments: input_json,
                                            call_id: call_id.clone(),
                                            name: tool_name,
                                            type_: ot::ResponseFunctionToolCallType::FunctionCall,
                                            id: Some(call_id.clone()),
                                            status: Some(ot::ResponseItemStatus::Completed),
                                        },
                                    ),
                                    ClaudeToolKind::Custom | ClaudeToolKind::ApplyPatch => {
                                        actual_kind = ClaudeToolKind::Custom;
                                        ResponseInputItem::CustomToolCall(ot::ResponseCustomToolCall {
                                            call_id: call_id.clone(),
                                            input: input_json,
                                            name: tool_name,
                                            type_: ot::ResponseCustomToolCallType::CustomToolCall,
                                            id: Some(call_id.clone()),
                                        })
                                    }
                                    ClaudeToolKind::Computer => {
                                        if let Some(action) = computer_action_from_input(&block.input) {
                                            ResponseInputItem::ComputerToolCall(
                                                ot::ResponseComputerToolCall {
                                                    id: call_id.clone(),
                                                    action,
                                                    call_id: call_id.clone(),
                                                    pending_safety_checks: Vec::new(),
                                                    status: ot::ResponseItemStatus::Completed,
                                                    type_: ot::ResponseComputerToolCallType::ComputerCall,
                                                },
                                            )
                                        } else {
                                            actual_kind = ClaudeToolKind::Custom;
                                            ResponseInputItem::CustomToolCall(ot::ResponseCustomToolCall {
                                                call_id: call_id.clone(),
                                                input: input_json,
                                                name: tool_name,
                                                type_: ot::ResponseCustomToolCallType::CustomToolCall,
                                                id: Some(call_id.clone()),
                                            })
                                        }
                                    }
                                    ClaudeToolKind::CodeInterpreter => ResponseInputItem::CodeInterpreterToolCall(
                                        ot::ResponseCodeInterpreterToolCall {
                                            id: call_id.clone(),
                                            code: str_field(&block.input, "code")
                                                .unwrap_or_default()
                                                .to_string(),
                                            container_id: str_field(&block.input, "container_id")
                                                .unwrap_or_default()
                                                .to_string(),
                                            outputs: None,
                                            status: ot::ResponseCodeInterpreterToolCallStatus::Completed,
                                            type_: ot::ResponseCodeInterpreterToolCallType::CodeInterpreterCall,
                                        },
                                    ),
                                    ClaudeToolKind::Shell => ResponseInputItem::ShellCall(
                                        ot::ResponseShellCall {
                                            action: ot::ResponseShellCallAction {
                                                commands: shell_commands(&block.input),
                                                max_output_length: None,
                                                timeout_ms: block
                                                    .input
                                                    .get("timeout_ms")
                                                    .and_then(|value| value.as_u64()),
                                            },
                                            call_id: call_id.clone(),
                                            type_: ot::ResponseShellCallType::ShellCall,
                                            id: Some(call_id.clone()),
                                            environment: None,
                                            status: Some(ot::ResponseItemStatus::Completed),
                                        },
                                    ),
                                    ClaudeToolKind::FileSearch => ResponseInputItem::FileSearchToolCall(
                                        ot::ResponseFileSearchToolCall {
                                            id: call_id.clone(),
                                            queries: file_search_queries(&block.input),
                                            status: ot::ResponseFileSearchToolCallStatus::Completed,
                                            type_: ot::ResponseFileSearchToolCallType::FileSearchCall,
                                            results: None,
                                        },
                                    ),
                                    ClaudeToolKind::WebSearch => ResponseInputItem::FunctionWebSearch(
                                        ot::ResponseFunctionWebSearch {
                                            id: Some(call_id.clone()),
                                            action: ot::ResponseFunctionWebSearchAction::Search {
                                                query: str_field(&block.input, "query")
                                                    .map(ToString::to_string),
                                                queries: {
                                                    let queries = string_list(block.input.get("queries"));
                                                    (queries.len() > 1).then_some(queries)
                                                },
                                                sources: None,
                                            },
                                            status: ot::ResponseFunctionWebSearchStatus::Completed,
                                            type_: ot::ResponseFunctionWebSearchType::WebSearchCall,
                                        },
                                    ),
                                    ClaudeToolKind::WebFetch => ResponseInputItem::FunctionWebSearch(
                                        ot::ResponseFunctionWebSearch {
                                            id: Some(call_id.clone()),
                                            action: ot::ResponseFunctionWebSearchAction::OpenPage {
                                                url: str_field(&block.input, "url")
                                                    .map(ToString::to_string),
                                            },
                                            status: ot::ResponseFunctionWebSearchStatus::Completed,
                                            type_: ot::ResponseFunctionWebSearchType::WebSearchCall,
                                        },
                                    ),
                                    ClaudeToolKind::Mcp => ResponseInputItem::FunctionToolCall(
                                        ot::ResponseFunctionToolCall {
                                            arguments: input_json,
                                            call_id: call_id.clone(),
                                            name: tool_name,
                                            type_: ot::ResponseFunctionToolCallType::FunctionCall,
                                            id: Some(call_id.clone()),
                                            status: Some(ot::ResponseItemStatus::Completed),
                                        },
                                    ),
                                };
                                let item_index = input_items.len();
                                input_items.push(item);
                                recorded_calls.insert(
                                    call_id,
                                    RecordedToolCall {
                                        item_index,
                                        kind: actual_kind,
                                    },
                                );
                            }
                            ct::BetaContentBlockParam::ServerToolUse(block) => {
                                let call_id = block.id.clone();
                                let item = match block.name {
                                    ct::BetaServerToolUseName::CodeExecution => {
                                        ResponseInputItem::CodeInterpreterToolCall(
                                            ot::ResponseCodeInterpreterToolCall {
                                                id: call_id.clone(),
                                                code: str_field(&block.input, "code")
                                                    .unwrap_or_default()
                                                    .to_string(),
                                                container_id: str_field(&block.input, "container_id")
                                                    .unwrap_or_default()
                                                    .to_string(),
                                                outputs: None,
                                                status: ot::ResponseCodeInterpreterToolCallStatus::Completed,
                                                type_: ot::ResponseCodeInterpreterToolCallType::CodeInterpreterCall,
                                            },
                                        )
                                    }
                                    ct::BetaServerToolUseName::WebSearch => {
                                        ResponseInputItem::FunctionWebSearch(ot::ResponseFunctionWebSearch {
                                            id: Some(call_id.clone()),
                                            action: ot::ResponseFunctionWebSearchAction::Search {
                                                query: str_field(&block.input, "query")
                                                    .map(ToString::to_string),
                                                queries: {
                                                    let queries = string_list(block.input.get("queries"));
                                                    (queries.len() > 1).then_some(queries)
                                                },
                                                sources: None,
                                            },
                                            status: ot::ResponseFunctionWebSearchStatus::Completed,
                                            type_: ot::ResponseFunctionWebSearchType::WebSearchCall,
                                        })
                                    }
                                    ct::BetaServerToolUseName::WebFetch => {
                                        ResponseInputItem::FunctionWebSearch(ot::ResponseFunctionWebSearch {
                                            id: Some(call_id.clone()),
                                            action: ot::ResponseFunctionWebSearchAction::OpenPage {
                                                url: str_field(&block.input, "url")
                                                    .map(ToString::to_string),
                                            },
                                            status: ot::ResponseFunctionWebSearchStatus::Completed,
                                            type_: ot::ResponseFunctionWebSearchType::WebSearchCall,
                                        })
                                    }
                                    ct::BetaServerToolUseName::BashCodeExecution => {
                                        ResponseInputItem::ShellCall(ot::ResponseShellCall {
                                            action: ot::ResponseShellCallAction {
                                                commands: shell_commands(&block.input),
                                                max_output_length: None,
                                                timeout_ms: block
                                                    .input
                                                    .get("timeout_ms")
                                                    .and_then(|value| value.as_u64()),
                                            },
                                            call_id: call_id.clone(),
                                            type_: ot::ResponseShellCallType::ShellCall,
                                            id: Some(call_id.clone()),
                                            environment: None,
                                            status: Some(ot::ResponseItemStatus::Completed),
                                        })
                                    }
                                    ct::BetaServerToolUseName::TextEditorCodeExecution => {
                                        ResponseInputItem::CustomToolCall(ot::ResponseCustomToolCall {
                                            call_id: call_id.clone(),
                                            input: json_string(&block.input),
                                            name: "text_editor_code_execution".to_string(),
                                            type_: ot::ResponseCustomToolCallType::CustomToolCall,
                                            id: Some(call_id.clone()),
                                        })
                                    }
                                    ct::BetaServerToolUseName::ToolSearchToolRegex => {
                                        ResponseInputItem::FileSearchToolCall(ot::ResponseFileSearchToolCall {
                                            id: call_id.clone(),
                                            queries: file_search_queries(&block.input),
                                            status: ot::ResponseFileSearchToolCallStatus::Completed,
                                            type_: ot::ResponseFileSearchToolCallType::FileSearchCall,
                                            results: None,
                                        })
                                    }
                                    ct::BetaServerToolUseName::ToolSearchToolBm25 => {
                                        ResponseInputItem::FileSearchToolCall(ot::ResponseFileSearchToolCall {
                                            id: call_id.clone(),
                                            queries: file_search_queries(&block.input),
                                            status: ot::ResponseFileSearchToolCallStatus::Completed,
                                            type_: ot::ResponseFileSearchToolCallType::FileSearchCall,
                                            results: None,
                                        })
                                    }
                                };
                                let kind = match block.name {
                                    ct::BetaServerToolUseName::CodeExecution => {
                                        ClaudeToolKind::CodeInterpreter
                                    }
                                    ct::BetaServerToolUseName::WebSearch => {
                                        ClaudeToolKind::WebSearch
                                    }
                                    ct::BetaServerToolUseName::WebFetch => ClaudeToolKind::WebFetch,
                                    ct::BetaServerToolUseName::BashCodeExecution => {
                                        ClaudeToolKind::Shell
                                    }
                                    ct::BetaServerToolUseName::TextEditorCodeExecution => {
                                        ClaudeToolKind::Custom
                                    }
                                    ct::BetaServerToolUseName::ToolSearchToolRegex
                                    | ct::BetaServerToolUseName::ToolSearchToolBm25 => {
                                        ClaudeToolKind::FileSearch
                                    }
                                };
                                let item_index = input_items.len();
                                input_items.push(item);
                                recorded_calls
                                    .insert(call_id, RecordedToolCall { item_index, kind });
                            }
                            ct::BetaContentBlockParam::McpToolUse(block) => {
                                let item_index = input_items.len();
                                input_items.push(ResponseInputItem::McpCall(ot::ResponseMcpCall {
                                    id: block.id.clone(),
                                    arguments: json_string(&block.input),
                                    name: block.name,
                                    server_label: block.server_name,
                                    type_: ot::ResponseMcpCallType::McpCall,
                                    approval_request_id: None,
                                    error: None,
                                    output: None,
                                    status: Some(ot::ResponseToolCallStatus::Completed),
                                }));
                                recorded_calls.insert(
                                    block.id,
                                    RecordedToolCall {
                                        item_index,
                                        kind: ClaudeToolKind::Mcp,
                                    },
                                );
                            }
                            ct::BetaContentBlockParam::Compaction(block) => {
                                input_items.push(ResponseInputItem::CompactionItem(
                                    ot::ResponseCompactionItemParam {
                                        encrypted_content: block.content.unwrap_or_default(),
                                        type_: ot::ResponseCompactionItemType::Compaction,
                                        id: None,
                                        created_by: None,
                                    },
                                ));
                            }
                            ct::BetaContentBlockParam::ContainerUpload(block) => {
                                input_items.push(output_message_item(
                                    format!("msg_{assistant_message_index}"),
                                    format!("container_upload:{}", block.file_id),
                                ));
                                assistant_message_index += 1;
                            }
                            other => {
                                input_items.push(output_message_item(
                                    format!("msg_{assistant_message_index}"),
                                    json_string(&other),
                                ));
                                assistant_message_index += 1;
                            }
                        }
                    }
                }
            }
        }

        let mut converted_tools = Vec::new();
        if let Some(tools) = body.tools {
            for tool in tools {
                match tool {
                    BetaToolUnion::Custom(tool) => {
                        if matches!(tool.type_, Some(ct::BetaCustomToolType::Custom)) {
                            converted_tools.push(ResponseTool::Custom(ot::ResponseCustomTool {
                                name: tool.name,
                                type_: ot::ResponseCustomToolType::Custom,
                                defer_loading: None,
                                description: tool.description,
                                format: Some(ot::ResponseCustomToolInputFormat::Text(
                                    ot::ResponseCustomToolTextFormat {
                                        type_: ot::ResponseCustomToolTextFormatType::Text,
                                    },
                                )),
                            }));
                        } else {
                            converted_tools.push(ResponseTool::Function(ResponseFunctionTool {
                                name: tool.name,
                                parameters: tool_input_schema_to_json_object(tool.input_schema),
                                strict: tool.common.strict,
                                type_: ResponseFunctionToolType::Function,
                                defer_loading: None,
                                description: tool.description,
                            }));
                        }
                    }
                    BetaToolUnion::CodeExecution20250522(_)
                    | BetaToolUnion::CodeExecution20250825(_) => {
                        converted_tools.push(ResponseTool::CodeInterpreter(
                            ResponseCodeInterpreterTool {
                                container: ResponseCodeInterpreterContainer::Auto(
                                    ResponseCodeInterpreterToolAuto {
                                        type_: ResponseCodeInterpreterToolAutoType::Auto,
                                        file_ids: None,
                                        memory_limit: None,
                                        network_policy: None,
                                    },
                                ),
                                type_: ResponseCodeInterpreterToolType::CodeInterpreter,
                            },
                        ));
                    }
                    BetaToolUnion::ComputerUse20241022(tool) => {
                        converted_tools.push(ResponseTool::Computer(ResponseComputerTool {
                            display_height: Some(tool.display_height_px),
                            display_width: Some(tool.display_width_px),
                            environment: Some(ResponseComputerEnvironment::Browser),
                            type_: ResponseComputerToolType::ComputerUsePreview,
                        }));
                    }
                    BetaToolUnion::ComputerUse20250124(tool) => {
                        converted_tools.push(ResponseTool::Computer(ResponseComputerTool {
                            display_height: Some(tool.display_height_px),
                            display_width: Some(tool.display_width_px),
                            environment: Some(ResponseComputerEnvironment::Browser),
                            type_: ResponseComputerToolType::ComputerUsePreview,
                        }));
                    }
                    BetaToolUnion::ComputerUse20251124(tool) => {
                        converted_tools.push(ResponseTool::Computer(ResponseComputerTool {
                            display_height: Some(tool.display_height_px),
                            display_width: Some(tool.display_width_px),
                            environment: Some(ResponseComputerEnvironment::Browser),
                            type_: ResponseComputerToolType::ComputerUsePreview,
                        }));
                    }
                    BetaToolUnion::WebSearch20250305(tool) => {
                        converted_tools.push(ResponseTool::WebSearch(ResponseWebSearchTool {
                            type_: ResponseWebSearchToolType::WebSearch,
                            filters: tool.allowed_domains.map(|allowed_domains| {
                                ResponseWebSearchFilters {
                                    allowed_domains: Some(allowed_domains),
                                }
                            }),
                            search_context_size: None,
                            user_location: tool.user_location.map(|location| {
                                ResponseApproximateLocation {
                                    city: location.city,
                                    country: location.country,
                                    region: location.region,
                                    timezone: location.timezone,
                                    type_: Some(ResponseApproximateLocationType::Approximate),
                                }
                            }),
                        }));
                    }
                    BetaToolUnion::WebFetch20250910(tool) => {
                        converted_tools.push(ResponseTool::WebSearch(ResponseWebSearchTool {
                            type_: ResponseWebSearchToolType::WebSearch,
                            filters: tool.allowed_domains.map(|allowed_domains| {
                                ResponseWebSearchFilters {
                                    allowed_domains: Some(allowed_domains),
                                }
                            }),
                            search_context_size: None,
                            user_location: None,
                        }));
                    }
                    BetaToolUnion::Bash20241022(_) | BetaToolUnion::Bash20250124(_) => {
                        converted_tools.push(ResponseTool::Shell(ResponseFunctionShellTool {
                            type_: ResponseFunctionShellToolType::Shell,
                            environment: None,
                        }));
                    }
                    BetaToolUnion::ToolSearchBm25_20251119(_)
                    | BetaToolUnion::ToolSearchRegex20251119(_) => {
                        converted_tools.push(ResponseTool::FileSearch(
                            ot::ResponseFileSearchTool {
                                type_: ot::ResponseFileSearchToolType::FileSearch,
                                vector_store_ids: Vec::new(),
                                filters: None,
                                max_num_results: None,
                                ranking_options: None,
                            },
                        ));
                    }
                    BetaToolUnion::TextEditor20241022(_)
                    | BetaToolUnion::TextEditor20250124(_)
                    | BetaToolUnion::TextEditor20250429(_)
                    | BetaToolUnion::TextEditor20250728(_) => {
                        converted_tools.push(ResponseTool::ApplyPatch(ResponseApplyPatchTool {
                            type_: ResponseApplyPatchToolType::ApplyPatch,
                        }));
                    }
                    BetaToolUnion::McpToolset(tool) => {
                        let allowed_tools = tool.configs.and_then(|configs| {
                            let names = configs
                                .into_iter()
                                .filter_map(|(name, config)| {
                                    if config.enabled.unwrap_or(true) {
                                        Some(name)
                                    } else {
                                        None
                                    }
                                })
                                .collect::<Vec<_>>();
                            if names.is_empty() {
                                None
                            } else {
                                Some(ResponseMcpAllowedTools::ToolNames(names))
                            }
                        });
                        converted_tools.push(ResponseTool::Mcp(ResponseMcpTool {
                            server_label: tool.mcp_server_name,
                            type_: ResponseMcpToolType::Mcp,
                            allowed_tools,
                            authorization: None,
                            connector_id: None,
                            defer_loading: None,
                            headers: None,
                            require_approval: None,
                            server_description: None,
                            server_url: None,
                        }));
                    }
                    BetaToolUnion::Memory20250818(_) => {}
                }
            }
        }
        if let Some(servers) = body.mcp_servers {
            for server in servers {
                converted_tools.push(ResponseTool::Mcp(ResponseMcpTool {
                    server_label: server.name,
                    type_: ResponseMcpToolType::Mcp,
                    allowed_tools: server
                        .tool_configuration
                        .as_ref()
                        .and_then(|config| config.allowed_tools.clone())
                        .map(ResponseMcpAllowedTools::ToolNames),
                    authorization: server.authorization_token,
                    connector_id: None,
                    defer_loading: None,
                    headers: None,
                    require_approval: None,
                    server_description: None,
                    server_url: Some(server.url),
                }));
            }
        }
        let tools = if converted_tools.is_empty() {
            None
        } else {
            Some(converted_tools)
        };

        let metadata = if let Some(user_id) = body
            .metadata
            .as_ref()
            .and_then(|value| value.user_id.clone())
        {
            let mut map = Metadata::new();
            map.insert("user_id".to_string(), user_id);
            Some(map)
        } else {
            None
        };
        let service_tier = match body.service_tier {
            Some(BetaServiceTierParam::Auto) => Some(ResponseServiceTier::Auto),
            Some(BetaServiceTierParam::StandardOnly) => Some(ResponseServiceTier::Default),
            None => match body.speed {
                Some(BetaSpeed::Fast) => Some(ResponseServiceTier::Priority),
                Some(BetaSpeed::Standard) | None => None,
            },
        };

        Ok(Self {
            method: HttpMethod::Post,
            path: PathParameters::default(),
            query: QueryParameters::default(),
            headers: RequestHeaders::default(),
            body: RequestBody {
                context_management,
                input: if input_items.is_empty() {
                    None
                } else {
                    Some(ResponseInput::Items(input_items))
                },
                instructions,
                max_output_tokens: Some(body.max_tokens),
                metadata,
                model: Some(model),
                parallel_tool_calls,
                reasoning,
                service_tier,
                stream: body.stream,
                temperature: body.temperature,
                text,
                tool_choice,
                tools,
                top_p: body.top_p,
                truncation,
                ..RequestBody::default()
            },
        })
    }
}