mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-19 19:04:48 +00:00
pi tests
This commit is contained in:
parent
e2e7f11b9a
commit
bd030904bc
12 changed files with 371 additions and 262 deletions
|
|
@ -12,6 +12,7 @@ use crate::{
|
|||
pub struct PiEventConverter {
|
||||
tool_result_buffers: HashMap<String, String>,
|
||||
tool_result_started: HashSet<String>,
|
||||
message_completed: HashSet<String>,
|
||||
message_errors: HashSet<String>,
|
||||
message_reasoning: HashMap<String, String>,
|
||||
message_text: HashMap<String, String>,
|
||||
|
|
@ -121,6 +122,7 @@ impl PiEventConverter {
|
|||
return Ok(Vec::new());
|
||||
}
|
||||
let message_id = self.ensure_message_id(extract_message_id(raw));
|
||||
self.message_completed.remove(&message_id);
|
||||
self.message_started.insert(message_id.clone());
|
||||
let content = message.and_then(parse_message_content).unwrap_or_default();
|
||||
let entry = self.message_text.entry(message_id.clone()).or_default();
|
||||
|
|
@ -210,15 +212,24 @@ impl PiEventConverter {
|
|||
self.clear_last_message_id(Some(&message_id));
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
if self.message_completed.contains(&message_id) {
|
||||
self.clear_last_message_id(Some(&message_id));
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
let message = raw
|
||||
.get("message")
|
||||
.or_else(|| assistant_event.get("message"));
|
||||
let conversion = self.complete_message(Some(message_id.clone()), message);
|
||||
self.message_completed.insert(message_id.clone());
|
||||
self.clear_last_message_id(Some(&message_id));
|
||||
Ok(vec![conversion])
|
||||
}
|
||||
"error" => {
|
||||
let message_id = self.ensure_message_id(message_id);
|
||||
if self.message_completed.contains(&message_id) {
|
||||
self.clear_last_message_id(Some(&message_id));
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
let error_text = assistant_event
|
||||
.get("error")
|
||||
.or_else(|| raw.get("error"))
|
||||
|
|
@ -228,6 +239,7 @@ impl PiEventConverter {
|
|||
self.message_text.remove(&message_id);
|
||||
self.message_errors.insert(message_id.clone());
|
||||
self.message_started.remove(&message_id);
|
||||
self.message_completed.insert(message_id.clone());
|
||||
self.clear_last_message_id(Some(&message_id));
|
||||
let item = UniversalItem {
|
||||
item_id: String::new(),
|
||||
|
|
@ -261,7 +273,12 @@ impl PiEventConverter {
|
|||
self.clear_last_message_id(Some(&message_id));
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
if self.message_completed.contains(&message_id) {
|
||||
self.clear_last_message_id(Some(&message_id));
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
let conversion = self.complete_message(Some(message_id.clone()), message);
|
||||
self.message_completed.insert(message_id.clone());
|
||||
self.clear_last_message_id(Some(&message_id));
|
||||
Ok(vec![conversion])
|
||||
}
|
||||
|
|
@ -479,7 +496,7 @@ fn status_event(label: &str, raw: &Value) -> EventConversion {
|
|||
kind: ItemKind::Status,
|
||||
role: Some(ItemRole::System),
|
||||
content: vec![ContentPart::Status {
|
||||
label: format!("pi.{label}"),
|
||||
label: pi_status_label(label),
|
||||
detail,
|
||||
}],
|
||||
status: ItemStatus::Completed,
|
||||
|
|
@ -490,6 +507,14 @@ fn status_event(label: &str, raw: &Value) -> EventConversion {
|
|||
)
|
||||
}
|
||||
|
||||
fn pi_status_label(label: &str) -> String {
|
||||
match label {
|
||||
"turn_end" => "turn.completed".to_string(),
|
||||
"agent_end" => "session.idle".to_string(),
|
||||
_ => format!("pi.{label}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn item_delta(message_id: Option<String>, delta: String) -> EventConversion {
|
||||
EventConversion::new(
|
||||
UniversalEventType::ItemDelta,
|
||||
|
|
|
|||
|
|
@ -216,6 +216,56 @@ fn pi_unknown_event_returns_error() {
|
|||
assert!(converter.event_to_universal(&event).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pi_turn_and_agent_end_emit_terminal_status_labels() {
|
||||
let mut converter = PiEventConverter::default();
|
||||
|
||||
let turn_end = parse_event(json!({
|
||||
"type": "turn_end",
|
||||
"sessionId": "session-1"
|
||||
}));
|
||||
let turn_events = converter
|
||||
.event_to_universal(&turn_end)
|
||||
.expect("turn_end conversions");
|
||||
assert_eq!(turn_events[0].event_type, UniversalEventType::ItemCompleted);
|
||||
if let UniversalEventData::Item(item) = &turn_events[0].data {
|
||||
assert_eq!(item.item.kind, ItemKind::Status);
|
||||
assert!(
|
||||
matches!(
|
||||
item.item.content.first(),
|
||||
Some(ContentPart::Status { label, .. }) if label == "turn.completed"
|
||||
),
|
||||
"turn_end should map to turn.completed status"
|
||||
);
|
||||
} else {
|
||||
panic!("expected item event");
|
||||
}
|
||||
|
||||
let agent_end = parse_event(json!({
|
||||
"type": "agent_end",
|
||||
"sessionId": "session-1"
|
||||
}));
|
||||
let agent_events = converter
|
||||
.event_to_universal(&agent_end)
|
||||
.expect("agent_end conversions");
|
||||
assert_eq!(
|
||||
agent_events[0].event_type,
|
||||
UniversalEventType::ItemCompleted
|
||||
);
|
||||
if let UniversalEventData::Item(item) = &agent_events[0].data {
|
||||
assert_eq!(item.item.kind, ItemKind::Status);
|
||||
assert!(
|
||||
matches!(
|
||||
item.item.content.first(),
|
||||
Some(ContentPart::Status { label, .. }) if label == "session.idle"
|
||||
),
|
||||
"agent_end should map to session.idle status"
|
||||
);
|
||||
} else {
|
||||
panic!("expected item event");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pi_message_done_completes_without_message_end() {
|
||||
let mut converter = PiEventConverter::default();
|
||||
|
|
@ -263,6 +313,63 @@ fn pi_message_done_completes_without_message_end() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pi_message_done_then_message_end_does_not_double_complete() {
|
||||
let mut converter = PiEventConverter::default();
|
||||
|
||||
let start_event = parse_event(json!({
|
||||
"type": "message_start",
|
||||
"sessionId": "session-1",
|
||||
"messageId": "msg-1",
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": [{ "type": "text", "text": "Hello" }]
|
||||
}
|
||||
}));
|
||||
let _ = converter
|
||||
.event_to_universal(&start_event)
|
||||
.expect("start conversions");
|
||||
|
||||
let update_event = parse_event(json!({
|
||||
"type": "message_update",
|
||||
"sessionId": "session-1",
|
||||
"messageId": "msg-1",
|
||||
"assistantMessageEvent": { "type": "text_delta", "delta": " world" }
|
||||
}));
|
||||
let _ = converter
|
||||
.event_to_universal(&update_event)
|
||||
.expect("update conversions");
|
||||
|
||||
let done_event = parse_event(json!({
|
||||
"type": "message_update",
|
||||
"sessionId": "session-1",
|
||||
"messageId": "msg-1",
|
||||
"assistantMessageEvent": { "type": "done" }
|
||||
}));
|
||||
let done_events = converter
|
||||
.event_to_universal(&done_event)
|
||||
.expect("done conversions");
|
||||
assert_eq!(done_events.len(), 1);
|
||||
assert_eq!(done_events[0].event_type, UniversalEventType::ItemCompleted);
|
||||
|
||||
let end_event = parse_event(json!({
|
||||
"type": "message_end",
|
||||
"sessionId": "session-1",
|
||||
"messageId": "msg-1",
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": [{ "type": "text", "text": "Hello world" }]
|
||||
}
|
||||
}));
|
||||
let end_events = converter
|
||||
.event_to_universal(&end_event)
|
||||
.expect("end conversions");
|
||||
assert!(
|
||||
end_events.is_empty(),
|
||||
"message_end after done should not emit a second completion"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pi_message_end_error_surfaces_failed_status_and_error_text() {
|
||||
let mut converter = PiEventConverter::default();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue