Skip to main content

gglib_core/request_pipeline/
truncation_parts.rs

1//! The elision of one message, in either content shape.
2//!
3//! Split out of [`super::truncation`], which is at its file budget, and kept
4//! beside it because the rule is the same for both shapes: when a message's
5//! text is over the threshold, every piece of it becomes the placeholder and
6//! the shape stays. A multi-part message keeps the parts that are not text
7//! (an `image_url` beside the tool output) where they were, and stays
8//! readable by the client that sent it in that shape.
9//!
10//! Until this existed only the string form was elided and the array form was
11//! skipped, so a long array-form history was refused as over budget while
12//! every one of its messages was eligible. VS Code's LLM gateway sends the
13//! array form on every message.
14
15use serde_json::Value;
16
17use super::content::{for_each_text_mut, text_len, text_parts};
18use super::truncation::{TOOL_CONTENT_THRESHOLD_CHARS, TRUNCATION_PLACEHOLDER};
19
20/// Replace this message's text with the placeholder when it exceeds
21/// [`TOOL_CONTENT_THRESHOLD_CHARS`], and say how many characters that
22/// reclaimed.
23///
24/// `None` when nothing was changed: the message is under the threshold,
25/// carries no text, or is split into so many short text parts that one
26/// placeholder per part would not be smaller than the text it replaces. Each
27/// piece of text becomes the placeholder, so a message of several text parts
28/// carries the placeholder once per part, and the estimate counts it once per
29/// part.
30pub(super) fn elide(msg: &mut Value) -> Option<usize> {
31    let content = msg.get_mut("content")?;
32    let text_chars = text_len(content);
33    let placeholders = text_parts(content) * TRUNCATION_PLACEHOLDER.len();
34    if text_chars <= TOOL_CONTENT_THRESHOLD_CHARS || text_chars <= placeholders {
35        return None;
36    }
37    for_each_text_mut(content, &mut |text| {
38        TRUNCATION_PLACEHOLDER.clone_into(text);
39    });
40    Some(text_chars - placeholders)
41}
42
43#[cfg(test)]
44#[path = "truncation_parts_tests.rs"]
45mod truncation_parts_tests;