mirror of
https://github.com/openai/codex.git
synced 2026-10-21 17:48:44 +00:00
## Why The 999-byte response budget and blanket string truncation could discard assistant answers alongside supporting history. Thread lists could also lose entries when shortened to fit the budget. ## What changed - Raise the response budget to 8,000 bytes and remove the separate 2,000-character cap on assistant messages in `read_thread` and `wait_threads`. - Compact oversized read responses around each turn's latest assistant message, and retain every wait target and returned list entry with pagination cursors intact. - Shorten only content fields when further reduction is needed, preserving valid JSON and protocol values such as IDs, cursors, phases, and statuses. Mark compacted responses and shortened content with `truncated`. - Request archived thread pages at the requested limit without retrying with smaller pages to meet the response budget. ## Testing Add regression coverage for oversized wait responses with Unicode and escaped text, list entry and cursor preservation, and an oversized read whose assistant answer survives into the next model request. Extend read and wait tests to verify assistant messages longer than 2,000 characters are retained when they fit. GitOrigin-RevId: 459fa522d02fbe4cd877b9afe8e66c9358b7bbce
66 lines
2.4 KiB
Rust
66 lines
2.4 KiB
Rust
use super::*;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
#[test]
|
|
fn oversized_wait_preserves_every_target_and_marks_shortened_answers() {
|
|
let answer = "\"\\\n🦀".repeat(10_000);
|
|
let polls = (0..8).map(|i| json!({
|
|
"schemaVersion": 1,
|
|
"thread": {"id": format!("thread-{i}"), "status": {"type": "idle"}},
|
|
"cursor": format!("cursor-{i}"),
|
|
"changed": true,
|
|
"latestTurn": {"id": format!("turn-{i}"), "status": "failed", "error": {"message": "Execution failed"}},
|
|
"latestAssistantMessage": {"id": format!("answer-{i}"), "text": answer, "phase": "final_answer"}
|
|
})).collect::<Vec<_>>();
|
|
let input =
|
|
json!({"timedOut": false, "wake": {"threadId": "thread-0"}, "polls": polls, "errors": []});
|
|
let text = serialize(input.clone()).unwrap();
|
|
assert!(text.len() <= MAX_RESPONSE_BYTES);
|
|
let output: Value = serde_json::from_str(&text).unwrap();
|
|
let mut expected = input;
|
|
expected["truncated"] = json!(true);
|
|
for (index, poll) in expected["polls"]
|
|
.as_array_mut()
|
|
.unwrap()
|
|
.iter_mut()
|
|
.enumerate()
|
|
{
|
|
poll["truncated"] = json!(true);
|
|
let retained = output["polls"][index]["latestAssistantMessage"]["text"]
|
|
.as_str()
|
|
.unwrap();
|
|
assert!(!retained.is_empty());
|
|
assert!(answer.starts_with(retained));
|
|
poll["latestAssistantMessage"]["text"] = json!(retained);
|
|
poll["latestAssistantMessage"]["truncated"] = json!(true);
|
|
}
|
|
assert_eq!(output, expected);
|
|
}
|
|
|
|
#[test]
|
|
fn oversized_list_preserves_entries_and_pagination() {
|
|
let entries = (0..50)
|
|
.map(|i| {
|
|
json!({
|
|
"id": format!("thread-{i}"), "kind": "codex", "title": "Title".repeat(400),
|
|
"summary": "Summary".repeat(100), "status": "idle", "cwd": "/large/path".repeat(100)
|
|
})
|
|
})
|
|
.collect::<Vec<_>>();
|
|
let text = serialize(json!({"threads": entries, "nextCursor": "next-page"})).unwrap();
|
|
assert!(text.len() <= MAX_RESPONSE_BYTES);
|
|
let output: Value = serde_json::from_str(&text).unwrap();
|
|
assert_eq!(output["nextCursor"], "next-page");
|
|
assert_eq!(
|
|
output["threads"]
|
|
.as_array()
|
|
.unwrap()
|
|
.iter()
|
|
.map(|thread| thread["id"].clone())
|
|
.collect::<Vec<_>>(),
|
|
(0..50)
|
|
.map(|i| json!(format!("thread-{i}")))
|
|
.collect::<Vec<_>>()
|
|
);
|
|
}
|