use std::ops::{Deref, DerefMut};
use bevy_ecs::prelude::*;
use bevy_reflect::prelude::*;
use rig_core::completion::{Message, Usage};
use rig_core::conversation::{self, MessageId, short_id};
use rig_core::effect::EffectId;
use rig_memory::Anchor;
use serde::{Deserialize, Serialize};
use super::model::ReasoningSetting;
use super::session_log::{ReflectSaved, save_discarded};
use super::stage::moved;
use super::turn::{NO_MODEL, Recovery, start_turn};
#[derive(Component, Reflect, Default)]
#[reflect(Component)]
#[require(
AgentId,
Conversation,
ReasoningSetting,
LastUsage,
SystemPrompt,
ToolAccess
)]
pub struct Agent;
#[derive(Component, Reflect, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[component(immutable)]
#[reflect(Component)]
#[serde(transparent)]
pub struct AgentId(pub String);
impl Default for AgentId {
fn default() -> Self {
Self(uuid::Uuid::new_v4().to_string())
}
}
impl From<AgentId> for String {
fn from(id: AgentId) -> Self {
id.0
}
}
impl AgentId {
pub fn short(&self) -> &str {
short_id(&self.0)
}
pub fn find_in<'a>(
&self,
agents: impl IntoIterator<Item = (Entity, &'a Self)>,
) -> Option<Entity> {
let mut agents = agents.into_iter();
agents.find_map(|(agent, id)| (id == self).then_some(agent))
}
}
#[derive(Component, Reflect, Clone, Debug, Default, PartialEq, Eq)]
#[reflect(Component, Saved, Default, Clone, Debug)]
pub struct CacheKey(pub String);
impl CacheKey {
pub fn of<'a>(key: Option<&'a Self>, id: &'a AgentId) -> &'a str {
match key {
Some(Self(key)) if !key.is_empty() => key,
_ => &id.0,
}
}
}
#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
#[relationship(relationship_target = Spawned)]
pub struct SpawnedBy(pub Entity);
#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
#[relationship_target(relationship = SpawnedBy, linked_spawn)]
pub struct Spawned(Vec<Entity>);
#[derive(Component, Reflect, Clone, Copy, Debug, Serialize, Deserialize)]
#[reflect(opaque, Component, Clone, Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct EffectParent(pub EffectId);
#[derive(Component, Reflect, Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[component(on_discard = save_discarded)]
#[reflect(opaque, Component, Default, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Conversation(pub conversation::Conversation);
impl Deref for Conversation {
type Target = conversation::Conversation;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Conversation {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<conversation::Conversation> for Conversation {
fn from(conversation: conversation::Conversation) -> Self {
Self(conversation)
}
}
#[derive(Component, Reflect, Clone, Copy, Debug, Default, Serialize, Deserialize)]
#[reflect(opaque, Component, Saved, Default, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct LastUsage(pub Option<Anchor>);
impl LastUsage {
pub fn usage(&self) -> Option<&Usage> {
self.0.as_ref().map(|anchor| &anchor.usage)
}
pub fn context(&self) -> Option<u64> {
self.usage()?.context_tokens()
}
}
#[derive(Component, Reflect, Clone, Default, Serialize, Deserialize)]
#[reflect(opaque, Component, Saved, Default, Clone, Serialize, Deserialize)]
#[serde(from = "Option<String>", into = "Option<String>")]
pub struct SystemPrompt(pub String);
impl From<SystemPrompt> for Option<String> {
fn from(prompt: SystemPrompt) -> Self {
(!prompt.0.is_empty()).then_some(prompt.0)
}
}
impl From<Option<String>> for SystemPrompt {
fn from(prompt: Option<String>) -> Self {
Self(prompt.unwrap_or_default())
}
}
#[derive(Component, Reflect, Clone, Default, Serialize, Deserialize)]
#[reflect(opaque, Component, Saved, Default, Clone, Serialize, Deserialize)]
pub enum ToolAccess {
#[default]
All,
Only(Vec<String>),
}
impl ToolAccess {
pub fn allows(&self, name: &str) -> bool {
match self {
Self::All => true,
Self::Only(names) => names.iter().any(|allowed| allowed == name),
}
}
}
#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
#[relationship(relationship_target = ActiveTurn)]
#[require(Recovery, Turn, Name = Name::new("turn"))]
pub struct TurnOf(pub Entity);
#[derive(Component, Reflect, Clone, Debug, Default)]
#[component(on_add = start_turn)]
#[reflect(Component, Default)]
pub struct Turn {
pub index: u64,
#[reflect(ignore)]
pub start: MessageId,
#[reflect(ignore)]
pub reply: Option<EffectId>,
}
#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
#[relationship_target(relationship = TurnOf, linked_spawn)]
pub struct ActiveTurn(Entity);
impl ActiveTurn {
pub fn turn(&self) -> Entity {
self.0
}
}
#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
#[relationship(relationship_target = Calls)]
pub struct CallOf(pub Entity);
#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
#[relationship_target(relationship = CallOf, linked_spawn)]
pub struct Calls(Vec<Entity>);
#[derive(Component, Reflect, Default)]
#[reflect(Component, Default)]
pub struct Partial {
pub text: String,
pub reasoning: String,
}
#[derive(EntityEvent, Reflect, Clone, Debug)]
#[reflect(Event, Clone, Debug)]
pub struct Retry {
pub entity: Entity,
}
#[derive(EntityEvent, Reflect, Clone, Debug)]
#[reflect(Event, Clone, Debug)]
pub struct StopTurn {
pub entity: Entity,
pub reason: Option<String>,
}
impl StopTurn {
pub fn new(agent: Entity) -> Self {
Self {
entity: agent,
reason: None,
}
}
}
#[derive(Reflect, Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum NoticeLevel {
#[default]
Info,
Warn,
Error,
}
#[derive(Message, Clone, Debug)]
pub struct Notice {
pub agent: Option<Entity>,
pub text: String,
pub level: NoticeLevel,
}
impl Notice {
fn new(agent: Option<Entity>, text: String, level: NoticeLevel) -> Self {
Self { agent, text, level }
}
pub fn info(agent: impl Into<Option<Entity>>, text: impl Into<String>) -> Self {
Self::new(agent.into(), text.into(), NoticeLevel::Info)
}
pub fn warn(agent: impl Into<Option<Entity>>, text: impl Into<String>) -> Self {
Self::new(agent.into(), text.into(), NoticeLevel::Warn)
}
pub fn error(agent: impl Into<Option<Entity>>, text: impl Into<String>) -> Self {
Self::new(agent.into(), text.into(), NoticeLevel::Error)
}
pub fn turn_running(agent: impl Into<Option<Entity>>) -> Self {
Self::info(agent, "A turn is running; stop it first.")
}
pub fn no_model(agent: Entity) -> Self {
Self::info(agent, NO_MODEL)
}
}
#[derive(Component, Clone, Debug)]
#[component(on_insert = moved::<TurnOutcome>, on_remove = moved::<TurnOutcome>)]
pub enum TurnOutcome {
Answered(Message),
Failed(String),
Stopped(Option<String>),
}
impl TurnOutcome {
pub(crate) fn stops(&self) -> Option<String> {
match self {
Self::Answered(_) => None,
Self::Failed(why) => Some(format!("the turn failed: {why}")),
Self::Stopped(None) => Some("the user stopped the turn".to_owned()),
Self::Stopped(Some(why)) => Some(why.clone()),
}
}
}