Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .repository-projection.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
"projection": "deixic-code",
"projectionSchemaVersion": 1,
"sourceRepository": "dx-corp/mono",
"sourceSha": "cad6ae4ef4abdce19fa502b83038a07eac298090",
"sourceSha": "0bab655ecdd7a8bf4b0fa0df5045a932278966a7",
"destinationRepository": "dx-corp/code",
"priorProjectedBase": "3b9bb6470e3cb58d5bbfda3b92a5a9159f26bac6",
"priorProjectedBase": "b8ba8d5dd808d6e4ccb65996f40974fc3a5a2371",
"definitionDigest": "cb9d429542ebb0a2de9b42a7aad60d9d8696a648ceba47c30f05c0b285ca0db7",
"toolDigest": "c9112982c70b80737d8faac67d58b06d55bde0dc297eb1009c1b02296f34f4a9",
"contentDigest": "a480358decd3772d195d8b5289b5cf3643920d2a23aa5935fa5df41a5127cde9",
"contentDigest": "4644f31a04a199598153f60167cf36e998791c146b6caa5866efc1a9a79b4a3d",
"publicationEligible": true
}
68 changes: 68 additions & 0 deletions vendor/dex-loop/src/summary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1032,6 +1032,74 @@ mod tests {
.chain(stream::pending())
}
}

#[tokio::test(start_paused = true)]
async fn semantic_summary_can_recover_after_the_mechanical_digest_fills() {
struct DelayedSummary(Duration);
impl Model for DelayedSummary {
fn stream<'a>(
&'a self,
ctx: &'a Context,
tools: &'a [&'a ToolSpec],
) -> impl Stream<Item = Result<ModelChunk, ModelError>> + Send + 'a {
assert!(tools.is_empty());
let Message::User { text, .. } = &ctx.history()[0].message else {
panic!("summary input");
};
assert!(text.contains("Never publish without my approval. Budget is $10."));
assert!(!text.contains("current request remains verbatim"));
stream::iter([Ok(ModelChunk::Text("Approval required. ".into()))]).chain(
stream::once(async move {
tokio::time::sleep(self.0).await;
Ok(ModelChunk::Text(
"Budget is $10; analysis is unfinished.".into(),
))
}),
)
}
}

let mut entries = history();
entries.extend((3..40).map(|cursor| Entry {
cursor: Cursor(cursor),
message: Message::Tool {
call: crate::CallId::new(format!("historical-call-{cursor}")),
name: ToolName::new("dex.describe"),
outcome: Outcome::Succeeded,
output: Output::Text("catalog entry ".repeat(100)),
},
}));
let (_, references, from, to) = material(&entries).unwrap();
assert!(mechanical_digest(&entries, &references, from, to).is_none());
let ctx = context();
let original = ctx.clone();
for (deadline, latency, succeeds) in [(10, 45, false), (300, 45, true), (300, 305, false)] {
let result = ModelSummarizer::new(DelayedSummary(Duration::from_secs(latency)))
.with_timeout(Duration::from_secs(deadline))
.summarize(&ctx, &entries)
.await;
assert_eq!(result.text.is_some(), succeeds);
if let Some(text) = result.text {
assert!(text.len() <= SUMMARY_BYTES);
let envelope: Value = serde_json::from_str(&text).unwrap();
assert_eq!(
envelope["references"],
json!(["attachment@v1", "result@v1"])
);
assert_eq!(envelope["covers_from_cursor"], 1);
assert_eq!(envelope["covers_to_cursor"], 39);
assert_eq!(
envelope["summary"],
"Approval required. Budget is $10; analysis is unfinished."
);
}
assert_eq!(
ctx, original,
"summary planning cannot mutate owner history"
);
}
}

#[tokio::test]
async fn timed_out_summary_falls_back_and_keeps_already_observed_usage() {
let summarizer = ModelSummarizer {
Expand Down
Loading