blob: 53cbda16bca2cfdf3998a8e1de5da98bb22c09d7 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
use super::wire::{Kind, Row};
use crate::{Error, jsonl, transcript::identity};
use std::collections::{BTreeMap, BTreeSet};
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
enum Parent {
Node(String),
Root(u64),
}
struct Node {
line: u64,
parent: Parent,
prompt: bool,
}
#[derive(Default)]
pub(super) struct Index {
nodes: BTreeMap<String, Node>,
dropped: BTreeSet<String>,
root: u64,
}
impl Index {
pub fn accept(&mut self, row: &Row<'_>, line: &jsonl::Line<'_>) -> Result<(), Error> {
if let Some(id) = &row.uuid {
identity(id)?;
if self.nodes.contains_key(id) {
return Ok(());
}
}
// Separate compaction roots; logicalParentUuid is not a parent link.
if row.compacted() {
self.root = line.number;
}
let Some(id) = &row.uuid else { return Ok(()) };
let parent = match &row.parent_uuid {
Some(parent) => {
identity(parent)?;
Parent::Node(parent.clone())
}
None => Parent::Root(self.root),
};
let prompt = row.kind == Kind::User && !row.is_meta && !row.is_compact_summary && {
let message = row.message(line)?;
!message.content.has_results() && !super::wire::synthetic(&message.content.text())
};
self.nodes.insert(
id.clone(),
Node {
line: line.number,
parent,
prompt,
},
);
Ok(())
}
pub fn resolve(mut self) -> Self {
let mut children: BTreeMap<&str, Vec<&str>> = BTreeMap::new();
let mut latest: BTreeMap<&Parent, (&str, u64)> = BTreeMap::new();
let mut withdrawn = Vec::new();
for (id, node) in &self.nodes {
if let Parent::Node(parent) = &node.parent {
children.entry(parent).or_default().push(id);
}
if node.prompt {
match latest.entry(&node.parent) {
std::collections::btree_map::Entry::Vacant(entry) => {
entry.insert((id, node.line));
}
std::collections::btree_map::Entry::Occupied(mut entry) => {
if entry.get().1 < node.line {
withdrawn.push(entry.get().0);
entry.insert((id, node.line));
} else {
withdrawn.push(id);
}
}
}
}
}
while let Some(id) = withdrawn.pop() {
if self.dropped.insert(id.to_owned())
&& let Some(children) = children.get(id)
{
withdrawn.extend(children);
}
}
self
}
pub fn keep(&self, row: &Row<'_>, line: u64) -> bool {
row.uuid.as_ref().is_none_or(|id| {
self.nodes.get(id).is_some_and(|node| node.line == line) && !self.dropped.contains(id)
})
}
}