use serde::{Deserialize, Serialize};
use thiserror::Error;
use super::ids::ScenarioId;
use super::record::EvalRecordObservation;
pub const MAX_HISTORY_TURNS: usize = super::scenario::MAX_TURNS_HARD_CAP as usize * 2;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[cfg_attr(feature = "server", derive(utoipa::ToSchema))]
#[serde(rename_all = "snake_case")]
pub enum SimulatedUserMode {
Server,
Client,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
#[cfg_attr(feature = "server", derive(utoipa::ToSchema))]
#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
pub enum TurnDirective {
AgentTurn {
scenario_id: ScenarioId,
turn: u32,
message: String,
history: Vec<ConversationTurn>,
},
UserTurnNeeded {
scenario_id: ScenarioId,
turn: u32,
history: Vec<ConversationTurn>,
},
ScenarioComplete {
scenario_id: ScenarioId,
},
RunComplete,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[cfg_attr(feature = "server", derive(utoipa::ToSchema))]
#[serde(deny_unknown_fields)]
pub struct ConversationTurn {
pub role: TurnRole,
pub content: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, schemars::JsonSchema)]
#[cfg_attr(feature = "server", derive(utoipa::ToSchema))]
#[serde(rename_all = "snake_case")]
pub enum TurnRole {
User,
Agent,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct AgentTurnSubmission {
pub scenario_id: ScenarioId,
pub turn: u32,
pub response: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub records: Vec<EvalRecordObservation>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
#[cfg_attr(feature = "server", derive(utoipa::ToSchema))]
#[serde(deny_unknown_fields)]
pub struct UserTurnSubmission {
pub scenario_id: ScenarioId,
pub turn: u32,
pub message: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[cfg_attr(feature = "server", derive(utoipa::ToSchema))]
#[serde(deny_unknown_fields)]
pub struct SimulatedUserTurn {
pub message: String,
pub goal_achieved: bool,
}
#[derive(Debug, Error)]
pub enum ProtocolError {
#[error("conversation history length {got} exceeds MAX_HISTORY_TURNS {max}")]
HistoryTooLong {
got: usize,
max: usize,
},
}
impl TurnDirective {
pub fn validate(&self) -> Result<(), ProtocolError> {
let history_len = match self {
TurnDirective::AgentTurn { history, .. }
| TurnDirective::UserTurnNeeded { history, .. } => history.len(),
TurnDirective::ScenarioComplete { .. } | TurnDirective::RunComplete => 0,
};
if history_len > MAX_HISTORY_TURNS {
return Err(ProtocolError::HistoryTooLong {
got: history_len,
max: MAX_HISTORY_TURNS,
});
}
Ok(())
}
}