use bevy_reflect::Reflect;
use std::sync::Arc;
use bevy_ecs::{lifecycle::HookContext, prelude::*, world::DeferredWorld};
use bevy_tasks::Task;
use rig_core::{
effect::{CustomEffect, EffectId, EffectKind, Family, HandlerKey, Key, Outcome},
error::ErrorReport,
streaming::{StreamEvents, Transcript},
tool::{PublishedContext, ToolContext},
};
use serde::{Deserialize, Serialize};
#[derive(Component, Debug, Clone, Serialize, Deserialize, Reflect)]
#[require(Seq)]
#[component(on_add = stamp_seq)]
#[reflect(Component)]
pub struct PendingEffect {
#[reflect(remote = crate::bus::reflect::HandlerKeyReflect)]
pub key: HandlerKey,
#[reflect(remote = crate::bus::reflect::EffectKindReflect)]
pub kind: EffectKind,
}
impl PendingEffect {
pub fn new(key: impl Into<HandlerKey>, kind: EffectKind) -> Self {
Self {
key: key.into(),
kind,
}
}
pub fn typed<F: Family>(key: &Key<F>, request: F::Request) -> Result<Self, ErrorReport> {
Ok(Self {
key: key.raw().clone(),
kind: F::wrap(request)?,
})
}
pub fn custom<E: CustomEffect>(
key: impl Into<HandlerKey>,
effect: &E,
) -> Result<Self, ErrorReport> {
let payload = serde_json::to_value(effect).map_err(|error| {
ErrorReport::new(
rig_core::error::ErrorKind::Request,
format!("the custom effect `{}` did not serialize: {error}", E::KIND),
)
})?;
Ok(Self {
key: key.into(),
kind: EffectKind::Custom {
kind: Arc::from(E::KIND),
payload,
},
})
}
pub const fn is_stream(&self) -> bool {
matches!(self.kind, EffectKind::Completion { stream: true, .. })
}
}
#[derive(
Component,
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
Reflect,
)]
#[reflect(Component)]
pub struct Seq(pub u64);
#[derive(Resource, Debug, Default, Reflect)]
#[reflect(Resource)]
pub struct SeqCounter(pub u64);
fn stamp_seq(mut world: DeferredWorld<'_>, context: HookContext) {
let next = {
let mut counter = world.resource_mut::<SeqCounter>();
let next = counter.0;
counter.0 += 1;
next
};
if let Some(mut seq) = world.get_mut::<Seq>(context.entity) {
seq.0 = next;
} else {
world.commands().entity(context.entity).insert(Seq(next));
}
}
#[derive(Resource, Debug, Default, Reflect)]
#[reflect(Resource)]
pub struct IdCounter(pub u64);
#[derive(Component, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Reflect)]
#[component(on_insert = bump_ids_past_reserved)]
#[reflect(Component)]
pub struct Reserved(#[reflect(remote = crate::bus::reflect::EffectIdReflect)] pub EffectId);
fn bump_ids_past_reserved(mut world: DeferredWorld<'_>, context: HookContext) {
if let Some(Reserved(id)) = world.get::<Reserved>(context.entity).copied() {
let mut counter = world.resource_mut::<IdCounter>();
counter.0 = counter.0.max(id.as_u64().saturating_add(1));
}
}
fn bump_ids_past_issued(mut world: DeferredWorld<'_>, context: HookContext) {
if let Some(Issued(id)) = world.get::<Issued>(context.entity).copied() {
let mut counter = world.resource_mut::<IdCounter>();
counter.0 = counter.0.max(id.as_u64().saturating_add(1));
}
}
#[derive(Component, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Reflect)]
#[component(on_insert = bump_ids_past_issued)]
#[reflect(Component)]
pub struct Issued(#[reflect(remote = crate::bus::reflect::EffectIdReflect)] pub EffectId);
#[derive(Component, Debug, Clone, Copy, Default, Serialize, Deserialize, Reflect)]
#[reflect(Component)]
pub struct Held;
#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)]
pub struct InFlight {
#[reflect(remote = crate::bus::reflect::HandlerKeyReflect)]
pub key: HandlerKey,
}
#[derive(Component)]
pub struct Serving;
#[derive(Component)]
pub struct Streaming {
pub events: futures::channel::mpsc::Receiver<
Result<rig_core::streaming::Relayed, rig_core::error::ErrorReport>,
>,
pub fold: rig_core::serve::StreamTap,
pub delivered: usize,
}
fn spawn_stream_worker(
mut stream: StreamEvents,
capacity: usize,
wake: super::plugin::Wake,
) -> (
futures::channel::mpsc::Receiver<
Result<rig_core::streaming::Relayed, rig_core::error::ErrorReport>,
>,
Task<()>,
) {
use futures::{SinkExt, StreamExt};
let (mut sender, events) = futures::channel::mpsc::channel(capacity.max(1));
let task = bevy_tasks::IoTaskPool::get().spawn(async move {
while let Some(item) = stream.next().await {
if sender.send(item).await.is_err() {
return;
}
wake.signal();
}
wake.signal();
});
(events, task)
}
#[derive(bevy_ecs::system::SystemParam)]
pub struct Tasks<'w> {
executions: NonSendMut<'w, Executions>,
}
impl Tasks<'_> {
pub fn serving(&mut self, entity: Entity, task: Task<rig_core::serve::Reply>) -> Serving {
self.executions.tasks.insert(entity, task);
Serving
}
pub fn poll(&mut self, entity: Entity) -> Option<rig_core::serve::Reply> {
let task = self.executions.tasks.get_mut(&entity)?;
let reply = bevy_tasks::futures::check_ready(task)?;
self.executions.tasks.remove(&entity);
Some(reply)
}
pub fn streaming(
&mut self,
entity: Entity,
stream: StreamEvents,
capacity: usize,
wake: super::plugin::Wake,
) -> Streaming {
let (events, task) = spawn_stream_worker(stream, capacity, wake);
self.executions.streams.insert(entity, task);
Streaming {
events,
fold: rig_core::serve::StreamTap::new(),
delivered: 0,
}
}
pub fn forget_stream(&mut self, entity: Entity) {
self.executions.streams.remove(&entity);
}
pub fn with<T>(world: &mut World, f: impl FnOnce(&mut Tasks<'_>) -> T) -> Option<T> {
let mut state = bevy_ecs::system::SystemState::<Tasks>::new(world);
let mut tasks = state.get_mut(world).ok()?;
Some(f(&mut tasks))
}
}
#[derive(Default)]
pub struct Executions {
pub tasks: std::collections::HashMap<Entity, Task<rig_core::serve::Reply>>,
pub streams: std::collections::HashMap<Entity, Task<()>>,
}
pub fn drop_execution(removed: On<Remove, InFlight>, mut executions: NonSendMut<Executions>) {
let entity = removed.event().entity;
executions.tasks.remove(&entity);
executions.streams.remove(&entity);
}
#[derive(Component, Debug, Default, Clone, Serialize, Deserialize, Reflect)]
#[reflect(Component)]
pub struct Streamed {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
#[reflect(remote = crate::bus::reflect::StreamErrorsReflect)]
pub errors: Vec<(usize, ErrorReport)>,
#[reflect(remote = crate::bus::reflect::StreamEventsReflect)]
pub events: Transcript,
pub text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[reflect(remote = crate::agent::content::reflect::OriginReflect)]
pub origin: Option<rig_core::message::Origin>,
#[reflect(remote = crate::bus::reflect::StreamedOutcomeReflect)]
pub outcome: Option<Result<Outcome, ErrorReport>>,
}
#[derive(Component, Debug, Clone, Serialize, Deserialize, Reflect)]
#[reflect(Component)]
pub struct EffectOutcome(
#[reflect(remote = crate::bus::reflect::OutcomeReflect)] pub Result<Outcome, ErrorReport>,
);
#[derive(Component, Debug)]
#[component(on_add = stamp_world_outcome)]
pub struct WorldOutcome {
pub outcome: Result<Outcome, ErrorReport>,
order: u64,
}
impl WorldOutcome {
pub fn new(outcome: Result<Outcome, ErrorReport>) -> Self {
Self { outcome, order: 0 }
}
pub fn order(&self) -> u64 {
self.order
}
}
#[derive(Resource, Default)]
pub struct WorldOutcomeCounter(pub u64);
fn stamp_world_outcome(mut world: DeferredWorld<'_>, context: HookContext) {
let order = {
let mut counter = world.resource_mut::<WorldOutcomeCounter>();
let order = counter.0;
counter.0 += 1;
order
};
if let Some(mut outcome) = world.get_mut::<WorldOutcome>(context.entity) {
outcome.order = order;
}
}
impl EffectOutcome {
pub fn typed<F: Family>(&self) -> Result<F::Answer, ErrorReport> {
F::unwrap(self.0.clone()?)
}
pub fn custom<E: CustomEffect>(&self) -> Result<E::Answer, ErrorReport> {
match self.0.clone()? {
Outcome::Custom { payload: value } => serde_json::from_value(value).map_err(|error| {
ErrorReport::new(
rig_core::error::ErrorKind::Response,
format!("the answer to `{}` did not deserialize: {error}", E::KIND),
)
}),
other => Err(ErrorReport::new(
rig_core::error::ErrorKind::Response,
format!(
"`{}` was answered with a {} outcome",
E::KIND,
other.family()
),
)),
}
}
}
#[derive(Component, Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, Reflect)]
#[reflect(Component)]
pub struct ToolInputs(#[reflect(remote = crate::bus::reflect::ToolContextReflect)] pub ToolContext);
#[derive(Component, Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, Reflect)]
#[reflect(Component)]
pub struct ToolOutputs(
#[reflect(remote = crate::bus::reflect::ToolContextReflect)] pub ToolContext,
);
#[derive(Component, Clone)]
pub struct Publishing(pub Arc<PublishedContext>);
#[derive(Component, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Reflect)]
#[reflect(Component)]
pub struct Scope(pub String);
pub trait WorldEffect: CustomEffect + Send + Sync {
type Reply: Serialize + serde::de::DeserializeOwned + Send + Sync + 'static;
}
impl<E> WorldEffect for E
where
E: CustomEffect + Send + Sync,
E::Answer: Send + Sync,
{
type Reply = E::Answer;
}
#[derive(Component, Debug, Clone)]
pub struct Asked<E: WorldEffect>(pub E);
#[derive(Component, Debug, Clone)]
pub struct Answer<E: WorldEffect>(pub E::Reply);
#[derive(Component, Debug, Clone, Serialize, Deserialize)]
pub struct Typed<F: Family>(pub Key<F>);
impl<F: Family> Typed<F> {
pub fn pending(&self, request: F::Request) -> Result<PendingEffect, ErrorReport> {
PendingEffect::typed(&self.0, request)
}
pub fn key(&self) -> &HandlerKey {
self.0.raw()
}
}
const _: () = {
const fn assert_send_sync<T: Send + Sync + 'static>() {}
assert_send_sync::<PendingEffect>();
assert_send_sync::<Seq>();
assert_send_sync::<Issued>();
assert_send_sync::<Reserved>();
assert_send_sync::<Held>();
assert_send_sync::<InFlight>();
assert_send_sync::<Streamed>();
assert_send_sync::<EffectOutcome>();
assert_send_sync::<Scope>();
assert_send_sync::<ToolInputs>();
assert_send_sync::<ToolOutputs>();
assert_send_sync::<Publishing>();
assert_send_sync::<Serving>();
assert_send_sync::<Streaming>();
assert_send_sync::<Typed<rig_core::effect::family::Completion>>();
};