1use bevy_ecs::prelude::*;
18use bevy_ecs::reflect::{AppTypeRegistry, ReflectComponent};
19use bevy_reflect::PartialReflect;
20use rig_core::conversation::MessageId;
21
22use super::agent::{Agent, AgentId, CacheKey, Conversation, Notice};
23use super::model::Connection;
24use super::restore::SavedKeys;
25use super::session_log::{ReflectSaved, SessionLog, save_conversation};
26
27#[derive(EntityEvent, Clone, Debug)]
39pub struct ForkAgent {
40 #[event_target]
42 pub agent: Entity,
43 pub through: Option<MessageId>,
45}
46
47#[derive(Event, Clone, Debug)]
49pub struct AgentForked {
50 pub from: Entity,
52 pub fork: Entity,
54 pub through: Option<MessageId>,
56}
57
58pub(crate) fn fork_agent(fork: On<ForkAgent>, mut commands: Commands) {
60 let (from, through) = (fork.agent, fork.through);
61 commands.queue(
62 move |world: &mut World| match fork_now(world, from, through) {
63 Ok(fork) => world.trigger(AgentForked {
64 from,
65 fork,
66 through,
67 }),
68 Err(why) => {
69 let why = format!("Could not fork the agent: {why}.");
70 world.write_message(Notice::error(from, why));
71 }
72 },
73 );
74}
75
76fn fork_now(world: &mut World, from: Entity, through: Option<MessageId>) -> Result<Entity, String> {
78 let id = world
79 .get::<AgentId>(from)
80 .cloned()
81 .ok_or("it is no agent")?;
82 let log = world
83 .get_resource::<SessionLog>()
84 .filter(|log| log.0.is_live())
85 .cloned()
86 .ok_or("the session is not logged")?;
87 let registry = world
88 .get_resource::<AppTypeRegistry>()
89 .cloned()
90 .ok_or("the app has no type registry")?;
91 if let Some(mut conversation) = world.get_mut::<Conversation>(from) {
92 let saved = save_conversation(&log, from, &id, conversation.bypass_change_detection());
93 world.write_message_batch(saved);
94 }
95 let to = AgentId::default();
96 let copy = log
97 .0
98 .fork_log(&id.0, &to.0, through)
99 .map_err(|failure| failure.to_string())?;
100 let registry = registry.read();
101 let saved: Vec<(&ReflectComponent, Box<dyn PartialReflect>)> = registry
102 .iter_with_data::<ReflectSaved>()
103 .filter_map(|(registration, _)| {
104 let component = registration.data::<ReflectComponent>()?;
105 let value = component.reflect(world.get_entity(from).ok()?)?;
106 let value = match value.reflect_clone() {
107 Ok(value) => value.into_partial_reflect(),
108 Err(_) => value.to_dynamic().ok()?,
109 };
110 Some((component, value))
111 })
112 .collect();
113 let key = CacheKey(CacheKey::of(world.get::<CacheKey>(from), &id).to_owned());
114 let connection = world.get::<Connection>(from).cloned();
115 let conversation = Conversation(copy.conversation);
116 let mut spawned = world.spawn((Name::new("agent"), Agent, to.clone(), conversation));
117 for (component, value) in &saved {
118 component.insert(&mut spawned, value.as_ref(), ®istry);
119 }
120 spawned.insert(key);
121 if let Some(connection) = connection {
122 spawned.insert(connection);
123 }
124 let fork = spawned.id();
125 let keys = world.get_resource::<SavedKeys>();
128 for key in copy.saved.keys() {
129 let path = keys.and_then(|keys| keys.path(key)).unwrap_or(key);
130 let registered = registry.get_with_type_path(path);
131 let component = registered.filter(|it| it.data::<ReflectSaved>().is_some());
132 let component = component.and_then(|it| it.data::<ReflectComponent>());
133 let held = |component: &ReflectComponent| {
134 let entity = world.get_entity(fork).ok();
135 entity.is_some_and(|entity| component.contains(entity))
136 };
137 if component.is_some_and(|component| !held(component)) {
138 log.0.save_value(&to.0, key, None, false);
139 }
140 }
141 Ok(fork)
142}
143
144#[cfg(test)]
145mod tests;