use std::collections::BTreeMap;
use serde_json::Value;
use super::{ProviderConversationItem, ProviderToolResult};
use crate::tools::skill_provenance::SkillReadProvenance;
const MAX_WITNESSES: usize = 256;
const MAX_DESCRIPTORS: usize = 8;
const MAX_CALL_ID_BYTES: usize = 128;
const MAX_CANDIDATE_CALLS: usize = 4096;
const MAX_PROJECTED_BYTES: usize = 1024 * 1024;
#[derive(Default)]
struct CallEvidence {
calls: u8,
results: u8,
read_call_index: Option<usize>,
}
struct Witness<'a> {
provenance: &'a SkillReadProvenance,
body: &'a str,
call_id: &'a str,
section: usize,
}
pub(super) fn project<'a>(
items: impl Iterator<Item = &'a ProviderConversationItem> + Clone,
) -> BTreeMap<usize, ProviderConversationItem> {
let mut calls = BTreeMap::<&str, CallEvidence>::new();
for item in items.clone() {
if let ProviderConversationItem::ToolResult(result) = item
&& eligible_result(result)
&& !calls.contains_key(result.call_id.as_str())
{
if calls.len() == MAX_CANDIDATE_CALLS {
return BTreeMap::new();
}
calls.insert(&result.call_id, CallEvidence::default());
}
}
if calls.is_empty() {
return BTreeMap::new();
}
for (index, item) in items.clone().enumerate() {
match item {
ProviderConversationItem::ToolResult(result) => {
record_result(&mut calls, &result.call_id);
}
ProviderConversationItem::ResponseItem(item) => {
match item.get("type").and_then(Value::as_str) {
Some("function_call") => {
if let Some(call) = item
.get("call_id")
.and_then(Value::as_str)
.and_then(|id| calls.get_mut(id))
{
call.calls = call.calls.saturating_add(1);
if item.get("name").and_then(Value::as_str) == Some("read") {
call.read_call_index = Some(index);
}
}
}
Some("function_call_output") => {
if let Some(id) = item.get("call_id").and_then(Value::as_str) {
record_result(&mut calls, id);
}
}
_ => {
if item.get("role").and_then(Value::as_str) == Some("tool")
&& let Some(id) = item.get("tool_call_id").and_then(Value::as_str)
{
record_result(&mut calls, id);
}
if item.get("role").and_then(Value::as_str) == Some("assistant") {
for call in item
.get("tool_calls")
.and_then(Value::as_array)
.into_iter()
.flatten()
{
if let Some(evidence) = call
.get("id")
.and_then(Value::as_str)
.and_then(|id| calls.get_mut(id))
{
evidence.calls = evidence.calls.saturating_add(1);
}
}
}
}
}
}
_ => {}
}
}
let mut witnesses = Vec::new();
let mut replacements = BTreeMap::new();
let mut remaining_bytes = MAX_PROJECTED_BYTES;
for (index, item) in items.enumerate() {
let ProviderConversationItem::ToolResult(result) = item else {
continue;
};
if !eligible_result(result) {
continue;
}
let Some(evidence) = calls.get(result.call_id.as_str()) else {
continue;
};
if evidence.calls != 1
|| evidence.results != 1
|| evidence
.read_call_index
.is_none_or(|call_index| call_index >= index)
{
continue;
}
if let Some(projected) = project_result(result, &mut witnesses, &mut remaining_bytes) {
replacements.insert(index, ProviderConversationItem::ToolResult(projected));
}
}
replacements
}
fn eligible_result(result: &ProviderToolResult) -> bool {
result.success
&& result.tool_name == "read"
&& !result.skill_reads.is_empty()
&& result.skill_reads.len() <= MAX_DESCRIPTORS
&& !result.call_id.is_empty()
&& result.call_id.len() <= MAX_CALL_ID_BYTES
&& !result
.call_id
.chars()
.any(|c| c.is_whitespace() || c.is_control())
}
fn record_result(calls: &mut BTreeMap<&str, CallEvidence>, id: &str) {
if let Some(call) = calls.get_mut(id) {
call.results = call.results.saturating_add(1);
}
}
fn project_result<'a>(
result: &'a ProviderToolResult,
witnesses: &mut Vec<Witness<'a>>,
remaining_bytes: &mut usize,
) -> Option<ProviderToolResult> {
let mut descriptors = result.skill_reads.iter().collect::<Vec<_>>();
if descriptors
.iter()
.any(|read| !read.validates(&result.output))
{
return None;
}
descriptors.sort_by_key(|read| read.start);
if descriptors
.windows(2)
.any(|pair| pair[0].end > pair[1].start)
{
return None;
}
let mut edits = Vec::new();
let mut output_len = result.output.len();
for (section, read) in descriptors.into_iter().enumerate() {
let body = result.output.get(read.start..read.end)?;
let witness = witnesses.iter().find(|witness| {
witness.provenance.identity == read.identity
&& witness.provenance.sha256 == read.sha256
&& witness.body == body
});
if let Some(witness) = witness {
let marker = format!(
"[Unchanged skill body: see {:?}, tool call {:?}, skill section {}.]",
witness.provenance.source, witness.call_id, witness.section
);
if marker.len() < body.len() {
output_len -= body.len() - marker.len();
edits.push((read.start, read.end, marker));
}
} else if witnesses.len() < MAX_WITNESSES {
witnesses.push(Witness {
provenance: read,
body,
call_id: &result.call_id,
section: section + 1,
});
}
}
if edits.is_empty() {
return None;
}
let metadata_bytes = result.call_id.len()
+ result.tool_name.len()
+ result
.skill_reads
.iter()
.map(|read| read.identity.len() + read.source.len() + read.sha256.len())
.sum::<usize>();
let projected_bytes = output_len.checked_add(metadata_bytes)?;
if projected_bytes > *remaining_bytes {
return None;
}
let mut output = String::with_capacity(output_len);
let mut cursor = 0;
for (start, end, marker) in edits {
output.push_str(&result.output[cursor..start]);
output.push_str(&marker);
cursor = end;
}
output.push_str(&result.output[cursor..]);
*remaining_bytes -= projected_bytes;
Some(ProviderToolResult {
call_id: result.call_id.clone(),
tool_name: result.tool_name.clone(),
success: result.success,
output,
skill_reads: result.skill_reads.clone(),
})
}