refactor: remove threads layer, use sessions directly for ACP (#9078)
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
@@ -108,6 +108,9 @@ pub async fn run_list_sessions<C: Connection>() {
|
||||
if let Some(ref mut meta) = s.meta {
|
||||
assert!(meta.get("createdAt").and_then(|v| v.as_str()).is_some());
|
||||
meta.remove("createdAt");
|
||||
// Provider/model metadata varies by test fixture; not relevant here.
|
||||
meta.remove("providerId");
|
||||
meta.remove("modelId");
|
||||
}
|
||||
}
|
||||
let mut expected_meta = serde_json::Map::new();
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
use goose::conversation::message::Message;
|
||||
use goose::session::session_manager::SessionStorage;
|
||||
use goose::session::thread_manager::ThreadManager;
|
||||
use rmcp::model::CallToolRequestParams;
|
||||
use std::sync::Arc;
|
||||
use tempfile::TempDir;
|
||||
|
||||
async fn setup() -> (ThreadManager, TempDir) {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let storage = SessionStorage::create(tmp.path()).await.unwrap();
|
||||
let tm = ThreadManager::new(Arc::new(storage));
|
||||
(tm, tmp)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn consecutive_text_chunks_are_coalesced() {
|
||||
let (tm, _tmp) = setup().await;
|
||||
let thread = tm.create_thread(None, None, None).await.unwrap();
|
||||
|
||||
// Simulate streaming: three consecutive assistant text chunks.
|
||||
tm.append_message(
|
||||
&thread.id,
|
||||
Some("s1"),
|
||||
&Message::assistant().with_text("Hello"),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
tm.append_message(
|
||||
&thread.id,
|
||||
Some("s1"),
|
||||
&Message::assistant().with_text(" world"),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
tm.append_message(&thread.id, Some("s1"), &Message::assistant().with_text("!"))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let messages = tm.list_messages(&thread.id).await.unwrap();
|
||||
assert_eq!(messages.len(), 1, "should coalesce into a single row");
|
||||
assert_eq!(messages[0].as_concat_text(), "Hello world!");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn role_change_prevents_coalescing() {
|
||||
let (tm, _tmp) = setup().await;
|
||||
let thread = tm.create_thread(None, None, None).await.unwrap();
|
||||
|
||||
tm.append_message(&thread.id, Some("s1"), &Message::user().with_text("Hi"))
|
||||
.await
|
||||
.unwrap();
|
||||
tm.append_message(
|
||||
&thread.id,
|
||||
Some("s1"),
|
||||
&Message::assistant().with_text("Hey"),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let messages = tm.list_messages(&thread.id).await.unwrap();
|
||||
assert_eq!(messages.len(), 2, "different roles should not coalesce");
|
||||
assert_eq!(messages[0].as_concat_text(), "Hi");
|
||||
assert_eq!(messages[1].as_concat_text(), "Hey");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn non_text_content_breaks_coalescing() {
|
||||
let (tm, _tmp) = setup().await;
|
||||
let thread = tm.create_thread(None, None, None).await.unwrap();
|
||||
|
||||
// Text, then tool request, then more text — should be 3 rows.
|
||||
tm.append_message(
|
||||
&thread.id,
|
||||
Some("s1"),
|
||||
&Message::assistant().with_text("Let me check"),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let tool_msg = Message::assistant().with_tool_request(
|
||||
"call_1",
|
||||
Ok(CallToolRequestParams::new("shell").with_arguments(
|
||||
serde_json::json!({"command": "ls"})
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.clone(),
|
||||
)),
|
||||
);
|
||||
tm.append_message(&thread.id, Some("s1"), &tool_msg)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
tm.append_message(
|
||||
&thread.id,
|
||||
Some("s1"),
|
||||
&Message::assistant().with_text("Done"),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let messages = tm.list_messages(&thread.id).await.unwrap();
|
||||
assert_eq!(messages.len(), 3, "tool request should break coalescing");
|
||||
assert_eq!(messages[0].as_concat_text(), "Let me check");
|
||||
assert_eq!(messages[2].as_concat_text(), "Done");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn text_after_tool_response_not_coalesced_with_tool() {
|
||||
let (tm, _tmp) = setup().await;
|
||||
let thread = tm.create_thread(None, None, None).await.unwrap();
|
||||
|
||||
// A tool request message (non-text) followed by text — should not coalesce.
|
||||
let tool_msg = Message::assistant().with_tool_request(
|
||||
"call_1",
|
||||
Ok(CallToolRequestParams::new("shell").with_arguments(
|
||||
serde_json::json!({"command": "ls"})
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.clone(),
|
||||
)),
|
||||
);
|
||||
tm.append_message(&thread.id, Some("s1"), &tool_msg)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
tm.append_message(
|
||||
&thread.id,
|
||||
Some("s1"),
|
||||
&Message::assistant().with_text("Result"),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let messages = tm.list_messages(&thread.id).await.unwrap();
|
||||
assert_eq!(
|
||||
messages.len(),
|
||||
2,
|
||||
"text should not coalesce with non-text predecessor"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn empty_message_not_coalesced() {
|
||||
let (tm, _tmp) = setup().await;
|
||||
let thread = tm.create_thread(None, None, None).await.unwrap();
|
||||
|
||||
tm.append_message(
|
||||
&thread.id,
|
||||
Some("s1"),
|
||||
&Message::assistant().with_text("Hello"),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// An empty assistant message (no content items).
|
||||
let empty = Message::assistant();
|
||||
tm.append_message(&thread.id, Some("s1"), &empty)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let messages = tm.list_messages(&thread.id).await.unwrap();
|
||||
// Empty message should be inserted as a new row (not coalesced).
|
||||
assert_eq!(messages.len(), 2);
|
||||
assert_eq!(messages[0].as_concat_text(), "Hello");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn metadata_change_prevents_coalescing() {
|
||||
let (tm, _tmp) = setup().await;
|
||||
let thread = tm.create_thread(None, None, None).await.unwrap();
|
||||
|
||||
tm.append_message(
|
||||
&thread.id,
|
||||
Some("s1"),
|
||||
&Message::assistant().with_text("Visible"),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
tm.append_message(
|
||||
&thread.id,
|
||||
Some("s1"),
|
||||
&Message::assistant().with_text(" hidden").agent_only(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let messages = tm.list_messages(&thread.id).await.unwrap();
|
||||
assert_eq!(
|
||||
messages.len(),
|
||||
2,
|
||||
"metadata boundary should break coalescing"
|
||||
);
|
||||
assert!(messages[0].metadata.user_visible);
|
||||
assert!(!messages[1].metadata.user_visible);
|
||||
assert_eq!(messages[0].as_concat_text(), "Visible");
|
||||
assert_eq!(messages[1].as_concat_text(), " hidden");
|
||||
}
|
||||
Reference in New Issue
Block a user