Skip to content

Commit 48941d5

Browse files
author
root
committed
fix: preserve reasoning in stream format transforms
1 parent 05d7adf commit 48941d5

5 files changed

Lines changed: 475 additions & 44 deletions

File tree

src/proxy.rs

Lines changed: 68 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,11 @@ impl StreamPrecheck {
10281028
self.content_chars += delta.chars().count();
10291029
}
10301030
}
1031+
Some("response.reasoning_text.delta" | "response.reasoning_summary_text.delta") => {
1032+
if let Some(delta) = payload.get("delta").and_then(Value::as_str) {
1033+
self.content_chars += delta.chars().count();
1034+
}
1035+
}
10311036
Some("response.function_call_arguments.delta" | "response.function_call.delta") => {
10321037
self.has_tool_call = true;
10331038
}
@@ -1052,6 +1057,9 @@ impl StreamPrecheck {
10521057
if let Some(text) = delta.get("content").and_then(Value::as_str) {
10531058
self.content_chars += text.chars().count();
10541059
}
1060+
if let Some(text) = delta.get("reasoning_content").and_then(Value::as_str) {
1061+
self.content_chars += text.chars().count();
1062+
}
10551063
if delta.get("tool_calls").is_some() {
10561064
self.has_tool_call = true;
10571065
}
@@ -1062,20 +1070,19 @@ impl StreamPrecheck {
10621070
crate::format::RequestFormat::ClaudeChat => {
10631071
match payload.get("type").and_then(Value::as_str) {
10641072
Some("content_block_delta") => {
1065-
if payload
1066-
.get("delta")
1067-
.and_then(Value::as_object)
1068-
.and_then(|delta| delta.get("type"))
1069-
.and_then(Value::as_str)
1070-
== Some("text_delta")
1071-
{
1072-
if let Some(text) = payload
1073-
.get("delta")
1074-
.and_then(Value::as_object)
1075-
.and_then(|delta| delta.get("text"))
1076-
.and_then(Value::as_str)
1077-
{
1078-
self.content_chars += text.chars().count();
1073+
if let Some(delta) = payload.get("delta").and_then(Value::as_object) {
1074+
match delta.get("type").and_then(Value::as_str) {
1075+
Some("text_delta") => {
1076+
if let Some(text) = delta.get("text").and_then(Value::as_str) {
1077+
self.content_chars += text.chars().count();
1078+
}
1079+
}
1080+
Some("thinking_delta") => {
1081+
if let Some(text) = delta.get("thinking").and_then(Value::as_str) {
1082+
self.content_chars += text.chars().count();
1083+
}
1084+
}
1085+
_ => {}
10791086
}
10801087
}
10811088
}
@@ -1285,6 +1292,22 @@ fn validate_response_content(
12851292
if let Some(items) = object.get("output").and_then(Value::as_array) {
12861293
for item in items {
12871294
match item.get("type").and_then(Value::as_str) {
1295+
Some("reasoning") => {
1296+
if let Some(parts) = item.get("summary").and_then(Value::as_array) {
1297+
for part in parts {
1298+
if let Some(text) = part.get("text").and_then(Value::as_str) {
1299+
accumulated_content.push_str(text);
1300+
}
1301+
}
1302+
}
1303+
if let Some(parts) = item.get("content").and_then(Value::as_array) {
1304+
for part in parts {
1305+
if let Some(text) = part.get("text").and_then(Value::as_str) {
1306+
accumulated_content.push_str(text);
1307+
}
1308+
}
1309+
}
1310+
}
12881311
Some("function_call" | "function_call_output" | "tool_result") => {
12891312
has_tool_call = true;
12901313
}
@@ -1318,6 +1341,11 @@ fn validate_response_content(
13181341
accumulated_content.push_str(text);
13191342
}
13201343
}
1344+
Some("thinking") => {
1345+
if let Some(text) = block.get("thinking").and_then(Value::as_str) {
1346+
accumulated_content.push_str(text);
1347+
}
1348+
}
13211349
Some("tool_use") => has_tool_call = true,
13221350
_ => {}
13231351
}
@@ -1331,6 +1359,11 @@ fn validate_response_content(
13311359
if let Some(text) = message.get("content").and_then(Value::as_str) {
13321360
accumulated_content.push_str(text);
13331361
}
1362+
if let Some(text) =
1363+
message.get("reasoning_content").and_then(Value::as_str)
1364+
{
1365+
accumulated_content.push_str(text);
1366+
}
13341367
if message.get("tool_calls").is_some() {
13351368
has_tool_call = true;
13361369
}
@@ -1396,6 +1429,11 @@ fn validate_stream_content(
13961429
accumulated_content.push_str(delta);
13971430
}
13981431
}
1432+
Some("response.reasoning_text.delta" | "response.reasoning_summary_text.delta") => {
1433+
if let Some(delta) = payload.get("delta").and_then(Value::as_str) {
1434+
accumulated_content.push_str(delta);
1435+
}
1436+
}
13991437
Some("response.function_call_arguments.delta" | "response.function_call.delta") => {
14001438
has_tool_call = true;
14011439
}
@@ -1420,6 +1458,9 @@ fn validate_stream_content(
14201458
if let Some(text) = delta.get("content").and_then(Value::as_str) {
14211459
accumulated_content.push_str(text);
14221460
}
1461+
if let Some(text) = delta.get("reasoning_content").and_then(Value::as_str) {
1462+
accumulated_content.push_str(text);
1463+
}
14231464
if delta.get("tool_calls").is_some() {
14241465
has_tool_call = true;
14251466
}
@@ -1430,20 +1471,19 @@ fn validate_stream_content(
14301471
crate::format::RequestFormat::ClaudeChat => {
14311472
match payload.get("type").and_then(Value::as_str) {
14321473
Some("content_block_delta") => {
1433-
if payload
1434-
.get("delta")
1435-
.and_then(Value::as_object)
1436-
.and_then(|delta| delta.get("type"))
1437-
.and_then(Value::as_str)
1438-
== Some("text_delta")
1439-
{
1440-
if let Some(text) = payload
1441-
.get("delta")
1442-
.and_then(Value::as_object)
1443-
.and_then(|delta| delta.get("text"))
1444-
.and_then(Value::as_str)
1445-
{
1446-
accumulated_content.push_str(text);
1474+
if let Some(delta) = payload.get("delta").and_then(Value::as_object) {
1475+
match delta.get("type").and_then(Value::as_str) {
1476+
Some("text_delta") => {
1477+
if let Some(text) = delta.get("text").and_then(Value::as_str) {
1478+
accumulated_content.push_str(text);
1479+
}
1480+
}
1481+
Some("thinking_delta") => {
1482+
if let Some(text) = delta.get("thinking").and_then(Value::as_str) {
1483+
accumulated_content.push_str(text);
1484+
}
1485+
}
1486+
_ => {}
14471487
}
14481488
}
14491489
}

src/response.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,16 @@ fn openai_chat_to_claude_response(body: Value) -> Result<Value, ApiError> {
102102
}
103103
}
104104

105+
if let Some(reasoning) = message.get("reasoning_content").and_then(Value::as_str) {
106+
if !reasoning.is_empty() {
107+
content.push(json!({
108+
"type": "thinking",
109+
"thinking": reasoning,
110+
"signature": ""
111+
}));
112+
}
113+
}
114+
105115
if content.is_empty() {
106116
content.push(json!({
107117
"type": "text",
@@ -302,7 +312,7 @@ fn openai_responses_to_openai_chat(body: Value) -> Result<Value, ApiError> {
302312
.collect::<Vec<_>>()
303313
.join("\n");
304314
if !text_content.is_empty() {
305-
message.insert("content".to_string(), json!(text_content));
315+
message.insert("reasoning_content".to_string(), json!(text_content));
306316
}
307317
}
308318
_ => {}

0 commit comments

Comments
 (0)