211 lines
7.1 KiB
Rust
211 lines
7.1 KiB
Rust
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());
|
|
}
|