1use bevy_reflect::Reflect;
11use std::sync::Arc;
12
13use bevy_ecs::{lifecycle::HookContext, prelude::*, world::DeferredWorld};
14use bevy_tasks::Task;
15use rig_core::{
16 effect::{CustomEffect, EffectId, EffectKind, Family, HandlerKey, Key, Outcome},
17 error::ErrorReport,
18 streaming::{StreamEvents, Transcript},
19 tool::{PublishedContext, ToolContext},
20};
21use serde::{Deserialize, Serialize};
22
23#[derive(Component, Debug, Clone, Serialize, Deserialize, Reflect)]
28#[require(Seq)]
29#[component(on_add = stamp_seq)]
30#[reflect(Component)]
31pub struct PendingEffect {
32 #[reflect(remote = crate::bus::reflect::HandlerKeyReflect)]
34 pub key: HandlerKey,
35 #[reflect(remote = crate::bus::reflect::EffectKindReflect)]
37 pub kind: EffectKind,
38}
39
40impl PendingEffect {
41 pub fn new(key: impl Into<HandlerKey>, kind: EffectKind) -> Self {
43 Self {
44 key: key.into(),
45 kind,
46 }
47 }
48
49 pub fn typed<F: Family>(key: &Key<F>, request: F::Request) -> Result<Self, ErrorReport> {
51 Ok(Self {
52 key: key.raw().clone(),
53 kind: F::wrap(request)?,
54 })
55 }
56
57 pub fn custom<E: CustomEffect>(
60 key: impl Into<HandlerKey>,
61 effect: &E,
62 ) -> Result<Self, ErrorReport> {
63 let payload = serde_json::to_value(effect).map_err(|error| {
64 ErrorReport::new(
65 rig_core::error::ErrorKind::Request,
66 format!("the custom effect `{}` did not serialize: {error}", E::KIND),
67 )
68 })?;
69 Ok(Self {
70 key: key.into(),
71 kind: EffectKind::Custom {
72 kind: Arc::from(E::KIND),
73 payload,
74 },
75 })
76 }
77
78 pub const fn is_stream(&self) -> bool {
80 matches!(self.kind, EffectKind::Completion { stream: true, .. })
81 }
82}
83
84#[derive(
88 Component,
89 Debug,
90 Clone,
91 Copy,
92 Default,
93 PartialEq,
94 Eq,
95 PartialOrd,
96 Ord,
97 Hash,
98 Serialize,
99 Deserialize,
100 Reflect,
101)]
102#[reflect(Component)]
103pub struct Seq(pub u64);
104
105#[derive(Resource, Debug, Default, Reflect)]
107#[reflect(Resource)]
108pub struct SeqCounter(pub u64);
109
110fn stamp_seq(mut world: DeferredWorld<'_>, context: HookContext) {
111 let next = {
112 let mut counter = world.resource_mut::<SeqCounter>();
113 let next = counter.0;
114 counter.0 += 1;
115 next
116 };
117 if let Some(mut seq) = world.get_mut::<Seq>(context.entity) {
118 seq.0 = next;
119 } else {
120 world.commands().entity(context.entity).insert(Seq(next));
121 }
122}
123
124#[derive(Resource, Debug, Default, Reflect)]
133#[reflect(Resource)]
134pub struct IdCounter(pub u64);
135
136#[derive(Component, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Reflect)]
140#[component(on_insert = bump_ids_past_reserved)]
141#[reflect(Component)]
142pub struct Reserved(#[reflect(remote = crate::bus::reflect::EffectIdReflect)] pub EffectId);
143
144fn bump_ids_past_reserved(mut world: DeferredWorld<'_>, context: HookContext) {
145 if let Some(Reserved(id)) = world.get::<Reserved>(context.entity).copied() {
146 let mut counter = world.resource_mut::<IdCounter>();
147 counter.0 = counter.0.max(id.as_u64().saturating_add(1));
148 }
149}
150
151fn bump_ids_past_issued(mut world: DeferredWorld<'_>, context: HookContext) {
152 if let Some(Issued(id)) = world.get::<Issued>(context.entity).copied() {
153 let mut counter = world.resource_mut::<IdCounter>();
154 counter.0 = counter.0.max(id.as_u64().saturating_add(1));
155 }
156}
157
158#[derive(Component, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Reflect)]
162#[component(on_insert = bump_ids_past_issued)]
163#[reflect(Component)]
164pub struct Issued(#[reflect(remote = crate::bus::reflect::EffectIdReflect)] pub EffectId);
165
166#[derive(Component, Debug, Clone, Copy, Default, Serialize, Deserialize, Reflect)]
175#[reflect(Component)]
176pub struct Held;
177
178#[derive(Component, Debug, Clone, Reflect)]
183#[reflect(Component)]
184pub struct InFlight {
185 #[reflect(remote = crate::bus::reflect::HandlerKeyReflect)]
187 pub key: HandlerKey,
188}
189
190#[derive(Component)]
193pub struct Serving;
194
195#[derive(Component)]
199pub struct Streaming {
200 pub events: futures::channel::mpsc::Receiver<
202 Result<rig_core::streaming::Relayed, rig_core::error::ErrorReport>,
203 >,
204 pub fold: rig_core::serve::StreamTap,
206 pub delivered: usize,
208}
209
210fn spawn_stream_worker(
214 mut stream: StreamEvents,
215 capacity: usize,
216 wake: super::plugin::Wake,
217) -> (
218 futures::channel::mpsc::Receiver<
219 Result<rig_core::streaming::Relayed, rig_core::error::ErrorReport>,
220 >,
221 Task<()>,
222) {
223 use futures::{SinkExt, StreamExt};
224 let (mut sender, events) = futures::channel::mpsc::channel(capacity.max(1));
225 let task = bevy_tasks::IoTaskPool::get().spawn(async move {
226 while let Some(item) = stream.next().await {
227 if sender.send(item).await.is_err() {
228 return;
229 }
230 wake.signal();
231 }
232 wake.signal();
233 });
234 (events, task)
235}
236
237#[derive(bevy_ecs::system::SystemParam)]
241pub struct Tasks<'w> {
242 executions: NonSendMut<'w, Executions>,
243}
244
245impl Tasks<'_> {
246 pub fn serving(&mut self, entity: Entity, task: Task<rig_core::serve::Reply>) -> Serving {
248 self.executions.tasks.insert(entity, task);
249 Serving
250 }
251
252 pub fn poll(&mut self, entity: Entity) -> Option<rig_core::serve::Reply> {
254 let task = self.executions.tasks.get_mut(&entity)?;
255 let reply = bevy_tasks::futures::check_ready(task)?;
256 self.executions.tasks.remove(&entity);
257 Some(reply)
258 }
259
260 pub fn streaming(
262 &mut self,
263 entity: Entity,
264 stream: StreamEvents,
265 capacity: usize,
266 wake: super::plugin::Wake,
267 ) -> Streaming {
268 let (events, task) = spawn_stream_worker(stream, capacity, wake);
269 self.executions.streams.insert(entity, task);
270 Streaming {
271 events,
272 fold: rig_core::serve::StreamTap::new(),
273 delivered: 0,
274 }
275 }
276
277 pub fn forget_stream(&mut self, entity: Entity) {
279 self.executions.streams.remove(&entity);
280 }
281
282 pub fn with<T>(world: &mut World, f: impl FnOnce(&mut Tasks<'_>) -> T) -> Option<T> {
285 let mut state = bevy_ecs::system::SystemState::<Tasks>::new(world);
286 let mut tasks = state.get_mut(world).ok()?;
287 Some(f(&mut tasks))
288 }
289}
290
291#[derive(Default)]
295pub struct Executions {
296 pub tasks: std::collections::HashMap<Entity, Task<rig_core::serve::Reply>>,
298 pub streams: std::collections::HashMap<Entity, Task<()>>,
300}
301
302pub fn drop_execution(removed: On<Remove, InFlight>, mut executions: NonSendMut<Executions>) {
304 let entity = removed.event().entity;
305 executions.tasks.remove(&entity);
306 executions.streams.remove(&entity);
307}
308
309#[derive(Component, Debug, Default, Clone, Serialize, Deserialize, Reflect)]
319#[reflect(Component)]
320pub struct Streamed {
321 #[serde(default, skip_serializing_if = "Vec::is_empty")]
325 #[reflect(remote = crate::bus::reflect::StreamErrorsReflect)]
326 pub errors: Vec<(usize, ErrorReport)>,
327 #[reflect(remote = crate::bus::reflect::StreamEventsReflect)]
329 pub events: Transcript,
330 pub text: String,
332 #[serde(default, skip_serializing_if = "Option::is_none")]
334 #[reflect(remote = crate::agent::content::reflect::OriginReflect)]
335 pub origin: Option<rig_core::message::Origin>,
336 #[reflect(remote = crate::bus::reflect::StreamedOutcomeReflect)]
339 pub outcome: Option<Result<Outcome, ErrorReport>>,
340}
341
342#[derive(Component, Debug, Clone, Serialize, Deserialize, Reflect)]
346#[reflect(Component)]
347pub struct EffectOutcome(
348 #[reflect(remote = crate::bus::reflect::OutcomeReflect)] pub Result<Outcome, ErrorReport>,
349);
350
351#[derive(Component, Debug)]
358#[component(on_add = stamp_world_outcome)]
359pub struct WorldOutcome {
360 pub outcome: Result<Outcome, ErrorReport>,
362 order: u64,
363}
364
365impl WorldOutcome {
366 pub fn new(outcome: Result<Outcome, ErrorReport>) -> Self {
368 Self { outcome, order: 0 }
369 }
370
371 pub fn order(&self) -> u64 {
373 self.order
374 }
375}
376
377#[derive(Resource, Default)]
379pub struct WorldOutcomeCounter(pub u64);
380
381fn stamp_world_outcome(mut world: DeferredWorld<'_>, context: HookContext) {
382 let order = {
383 let mut counter = world.resource_mut::<WorldOutcomeCounter>();
384 let order = counter.0;
385 counter.0 += 1;
386 order
387 };
388 if let Some(mut outcome) = world.get_mut::<WorldOutcome>(context.entity) {
389 outcome.order = order;
390 }
391}
392
393impl EffectOutcome {
394 pub fn typed<F: Family>(&self) -> Result<F::Answer, ErrorReport> {
396 F::unwrap(self.0.clone()?)
397 }
398
399 pub fn custom<E: CustomEffect>(&self) -> Result<E::Answer, ErrorReport> {
402 match self.0.clone()? {
403 Outcome::Custom { payload: value } => serde_json::from_value(value).map_err(|error| {
404 ErrorReport::new(
405 rig_core::error::ErrorKind::Response,
406 format!("the answer to `{}` did not deserialize: {error}", E::KIND),
407 )
408 }),
409 other => Err(ErrorReport::new(
410 rig_core::error::ErrorKind::Response,
411 format!(
412 "`{}` was answered with a {} outcome",
413 E::KIND,
414 other.family()
415 ),
416 )),
417 }
418 }
419}
420
421#[derive(Component, Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, Reflect)]
424#[reflect(Component)]
425pub struct ToolInputs(#[reflect(remote = crate::bus::reflect::ToolContextReflect)] pub ToolContext);
426
427#[derive(Component, Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, Reflect)]
431#[reflect(Component)]
432pub struct ToolOutputs(
433 #[reflect(remote = crate::bus::reflect::ToolContextReflect)] pub ToolContext,
434);
435
436#[derive(Component, Clone)]
440pub struct Publishing(pub Arc<PublishedContext>);
441
442#[derive(Component, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Reflect)]
445#[reflect(Component)]
446pub struct Scope(pub String);
447
448pub trait WorldEffect: CustomEffect + Send + Sync {
452 type Reply: Serialize + serde::de::DeserializeOwned + Send + Sync + 'static;
454}
455
456impl<E> WorldEffect for E
457where
458 E: CustomEffect + Send + Sync,
459 E::Answer: Send + Sync,
460{
461 type Reply = E::Answer;
462}
463
464#[derive(Component, Debug, Clone)]
468pub struct Asked<E: WorldEffect>(pub E);
469
470#[derive(Component, Debug, Clone)]
473pub struct Answer<E: WorldEffect>(pub E::Reply);
474
475#[derive(Component, Debug, Clone, Serialize, Deserialize)]
478pub struct Typed<F: Family>(pub Key<F>);
479
480impl<F: Family> Typed<F> {
481 pub fn pending(&self, request: F::Request) -> Result<PendingEffect, ErrorReport> {
483 PendingEffect::typed(&self.0, request)
484 }
485
486 pub fn key(&self) -> &HandlerKey {
488 self.0.raw()
489 }
490}
491
492const _: () = {
495 const fn assert_send_sync<T: Send + Sync + 'static>() {}
496 assert_send_sync::<PendingEffect>();
497 assert_send_sync::<Seq>();
498 assert_send_sync::<Issued>();
499 assert_send_sync::<Reserved>();
500 assert_send_sync::<Held>();
501 assert_send_sync::<InFlight>();
502 assert_send_sync::<Streamed>();
503 assert_send_sync::<EffectOutcome>();
504 assert_send_sync::<Scope>();
505 assert_send_sync::<ToolInputs>();
506 assert_send_sync::<ToolOutputs>();
507 assert_send_sync::<Publishing>();
508 assert_send_sync::<Serving>();
509 assert_send_sync::<Streaming>();
510 assert_send_sync::<Typed<rig_core::effect::family::Completion>>();
511};