ACP Wrap up
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
use galaxy_agent_core::{
|
||||
AgentEvent, RuntimeActivity, RuntimeActivityStatus, RuntimeCapabilities, StopReason, Usage,
|
||||
};
|
||||
use warp_multi_agent_api::{client_action, message, response_event};
|
||||
|
||||
use super::{RuntimeResponseConfig, RuntimeResponseTranslator};
|
||||
use crate::ai::agent::runtime_activity;
|
||||
|
||||
fn provider_translator() -> RuntimeResponseTranslator {
|
||||
RuntimeResponseTranslator::new(RuntimeResponseConfig {
|
||||
task_id: "task".to_owned(),
|
||||
conversation_id: "conversation".to_owned(),
|
||||
needs_create_task: false,
|
||||
user_query: None,
|
||||
model_id: "model".to_owned(),
|
||||
max_context_tokens: Some(1_000),
|
||||
capabilities: RuntimeCapabilities::provider(),
|
||||
empty_output_message: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn session_translator() -> RuntimeResponseTranslator {
|
||||
RuntimeResponseTranslator::new(RuntimeResponseConfig {
|
||||
task_id: "task".to_owned(),
|
||||
conversation_id: String::new(),
|
||||
needs_create_task: false,
|
||||
user_query: None,
|
||||
model_id: "session-runtime".to_owned(),
|
||||
max_context_tokens: None,
|
||||
capabilities: RuntimeCapabilities::session_runtime(),
|
||||
empty_output_message: Some("> runtime completed without text".to_owned()),
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn provider_and_session_runtimes_share_text_translation() {
|
||||
for mut translator in [provider_translator(), session_translator()] {
|
||||
let first = translator
|
||||
.translate(AgentEvent::TextDelta {
|
||||
text: "one".to_owned(),
|
||||
})
|
||||
.expect("first delta");
|
||||
let second = translator
|
||||
.translate(AgentEvent::TextDelta {
|
||||
text: " two".to_owned(),
|
||||
})
|
||||
.expect("second delta");
|
||||
|
||||
let Some(response_event::Type::ClientActions(first_actions)) = &first[1].r#type else {
|
||||
panic!("expected first client action");
|
||||
};
|
||||
assert!(matches!(
|
||||
first_actions.actions[0].action,
|
||||
Some(client_action::Action::AddMessagesToTask(_))
|
||||
));
|
||||
let Some(response_event::Type::ClientActions(second_actions)) = &second[0].r#type else {
|
||||
panic!("expected append client action");
|
||||
};
|
||||
assert!(matches!(
|
||||
second_actions.actions[0].action,
|
||||
Some(client_action::Action::AppendToMessageContent(_))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reasoning_uses_the_native_reasoning_message_contract() {
|
||||
let mut translator = provider_translator();
|
||||
let first = translator
|
||||
.translate(AgentEvent::ReasoningDelta {
|
||||
text: "think".to_owned(),
|
||||
})
|
||||
.expect("reasoning");
|
||||
let second = translator
|
||||
.translate(AgentEvent::ReasoningDelta {
|
||||
text: " more".to_owned(),
|
||||
})
|
||||
.expect("reasoning append");
|
||||
|
||||
let Some(response_event::Type::ClientActions(actions)) = &first[1].r#type else {
|
||||
panic!("expected reasoning action");
|
||||
};
|
||||
let Some(client_action::Action::AddMessagesToTask(add)) = &actions.actions[0].action else {
|
||||
panic!("expected reasoning message");
|
||||
};
|
||||
assert!(matches!(
|
||||
add.messages[0].message,
|
||||
Some(message::Message::AgentReasoning(_))
|
||||
));
|
||||
let Some(response_event::Type::ClientActions(actions)) = &second[0].r#type else {
|
||||
panic!("expected reasoning append");
|
||||
};
|
||||
assert!(matches!(
|
||||
actions.actions[0].action,
|
||||
Some(client_action::Action::AppendToMessageContent(_))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_activity_updates_the_same_structured_message() {
|
||||
let mut translator = session_translator();
|
||||
let started = translator
|
||||
.translate(AgentEvent::RuntimeActivityUpdated {
|
||||
activity: RuntimeActivity {
|
||||
id: "tool-1".to_owned(),
|
||||
title: "Run tests".to_owned(),
|
||||
status: Some(RuntimeActivityStatus::InProgress),
|
||||
output: None,
|
||||
},
|
||||
})
|
||||
.expect("started");
|
||||
let Some(response_event::Type::ClientActions(actions)) = &started[1].r#type else {
|
||||
panic!("expected activity action");
|
||||
};
|
||||
let Some(client_action::Action::AddMessagesToTask(add)) = &actions.actions[0].action else {
|
||||
panic!("expected activity message");
|
||||
};
|
||||
let message_id = add.messages[0].id.clone();
|
||||
|
||||
let completed = translator
|
||||
.translate(AgentEvent::RuntimeActivityUpdated {
|
||||
activity: RuntimeActivity {
|
||||
id: "tool-1".to_owned(),
|
||||
title: String::new(),
|
||||
status: Some(RuntimeActivityStatus::Completed),
|
||||
output: Some("ok".to_owned()),
|
||||
},
|
||||
})
|
||||
.expect("completed");
|
||||
let Some(response_event::Type::ClientActions(actions)) = &completed[0].r#type else {
|
||||
panic!("expected activity update");
|
||||
};
|
||||
let Some(client_action::Action::UpdateTaskMessage(update)) = &actions.actions[0].action else {
|
||||
panic!("expected in-place update");
|
||||
};
|
||||
let message = update.message.as_ref().expect("updated message");
|
||||
assert_eq!(message.id, message_id);
|
||||
assert_eq!(
|
||||
runtime_activity::decode(&message.server_message_data),
|
||||
Some(RuntimeActivity {
|
||||
id: "tool-1".to_owned(),
|
||||
title: "Run tests".to_owned(),
|
||||
status: Some(RuntimeActivityStatus::Completed),
|
||||
output: Some("ok".to_owned()),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_shape_follows_history_ownership_capability() {
|
||||
let mut provider = provider_translator();
|
||||
provider
|
||||
.translate(AgentEvent::UsageUpdated {
|
||||
usage: Usage {
|
||||
input_tokens: 250,
|
||||
output_tokens: 10,
|
||||
..Usage::default()
|
||||
},
|
||||
})
|
||||
.expect("provider usage");
|
||||
let provider_finished = provider
|
||||
.translate(AgentEvent::TurnStopped {
|
||||
reason: StopReason::Completed,
|
||||
})
|
||||
.expect("provider finished");
|
||||
let Some(response_event::Type::Finished(finished)) = &provider_finished[1].r#type else {
|
||||
panic!("expected provider finish");
|
||||
};
|
||||
assert_eq!(finished.token_usage[0].total_input, 250);
|
||||
|
||||
let mut session = session_translator();
|
||||
session
|
||||
.translate(AgentEvent::ContextUsageUpdated {
|
||||
used_tokens: 25,
|
||||
context_size: 100,
|
||||
})
|
||||
.expect("context usage");
|
||||
let session_finished = session
|
||||
.translate(AgentEvent::TurnStopped {
|
||||
reason: StopReason::Completed,
|
||||
})
|
||||
.expect("session finished");
|
||||
let Some(response_event::Type::Finished(finished)) = &session_finished[2].r#type else {
|
||||
panic!("expected session finish");
|
||||
};
|
||||
assert_eq!(finished.token_usage[0].total_input, 0);
|
||||
assert_eq!(
|
||||
finished
|
||||
.conversation_usage_metadata
|
||||
.as_ref()
|
||||
.expect("context metadata")
|
||||
.context_window_usage,
|
||||
0.25
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn capabilities_reject_events_owned_by_the_other_runtime_shape() {
|
||||
assert!(provider_translator()
|
||||
.translate(AgentEvent::ContextUsageUpdated {
|
||||
used_tokens: 1,
|
||||
context_size: 2,
|
||||
})
|
||||
.is_err());
|
||||
assert!(session_translator()
|
||||
.translate(AgentEvent::UsageUpdated {
|
||||
usage: Usage::default(),
|
||||
})
|
||||
.is_err());
|
||||
}
|
||||
Reference in New Issue
Block a user