1use std::{fmt, sync::Arc};
14
15use serde::{Deserialize, Serialize};
16
17use crate::{
18 completion::{CompletionRequest, CompletionResponse, Message, ModelRef, ProviderCapabilities},
19 embeddings::EmbeddingResponse,
20 error::ErrorReport,
21 id::ConversationId,
22 operation::RerankRequest,
23 rerank::RerankResponse,
24 streaming::Transcript,
25 tool::ToolResult,
26 vector_store::request::{Filter, VectorSearchRequest},
27 wasm_compat::WasmCompatSend,
28};
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
36#[serde(transparent)]
37pub struct EffectId(u64);
38
39impl EffectId {
40 pub const fn from_raw(raw: u64) -> Self {
42 Self(raw)
43 }
44
45 pub const fn as_u64(self) -> u64 {
47 self.0
48 }
49}
50
51impl fmt::Display for EffectId {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 write!(f, "effect:{}", self.0)
54 }
55}
56
57#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
59pub struct HandlerKey(Arc<str>);
60
61impl HandlerKey {
62 pub fn new(key: impl Into<Arc<str>>) -> Self {
64 Self(key.into())
65 }
66
67 pub fn as_str(&self) -> &str {
69 &self.0
70 }
71
72 pub fn parts(&self) -> KeyParts {
75 KeyParts::parse(&self.0)
76 }
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, Hash)]
95pub struct KeyParts {
96 pub owner: Option<Arc<str>>,
98 pub kind: Option<Arc<str>>,
100 pub label: Arc<str>,
102 pub generation: Option<u64>,
106}
107
108impl KeyParts {
109 pub fn parse(key: &str) -> Self {
111 let (owner, rest) = match key.split_once('/') {
112 Some((owner, rest)) if !owner.is_empty() && !rest.is_empty() => {
113 (Some(Arc::from(owner)), rest)
114 }
115 _ => (None, key),
116 };
117 let (rest, generation) = match rest.rsplit_once('#') {
118 Some((head, digits))
119 if !digits.is_empty()
120 && (digits.len() == 1 || !digits.starts_with('0'))
121 && digits.bytes().all(|b| b.is_ascii_digit()) =>
122 {
123 match digits.parse::<u64>() {
124 Ok(generation) => (head, Some(generation)),
125 Err(_) => (rest, None),
126 }
127 }
128 _ => (rest, None),
129 };
130 let (kind, label) = match rest.split_once(':') {
131 Some((kind, label)) if !kind.is_empty() && !label.is_empty() => {
132 (Some(Arc::from(kind)), label)
133 }
134 _ => (None, rest),
135 };
136 Self {
137 owner,
138 kind,
139 label: Arc::from(label),
140 generation,
141 }
142 }
143
144 pub fn to_key(&self) -> HandlerKey {
146 HandlerKey::from(self.to_string())
147 }
148}
149
150impl fmt::Display for KeyParts {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 if let Some(owner) = &self.owner {
153 write!(f, "{owner}/")?;
154 }
155 if let Some(kind) = &self.kind {
156 write!(f, "{kind}:")?;
157 }
158 f.write_str(&self.label)?;
159 if let Some(generation) = self.generation {
160 write!(f, "#{generation}")?;
161 }
162 Ok(())
163 }
164}
165
166impl fmt::Debug for HandlerKey {
167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168 write!(f, "HandlerKey({:?})", &*self.0)
169 }
170}
171
172impl fmt::Display for HandlerKey {
173 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174 f.write_str(&self.0)
175 }
176}
177
178impl From<&str> for HandlerKey {
179 fn from(key: &str) -> Self {
180 Self(Arc::from(key))
181 }
182}
183
184impl From<String> for HandlerKey {
185 fn from(key: String) -> Self {
186 Self(Arc::from(key))
187 }
188}
189
190impl AsRef<str> for HandlerKey {
191 fn as_ref(&self) -> &str {
192 &self.0
193 }
194}
195
196impl Serialize for HandlerKey {
198 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
199 serializer.serialize_str(&self.0)
200 }
201}
202
203impl<'de> Deserialize<'de> for HandlerKey {
204 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
205 let key = <std::borrow::Cow<'de, str>>::deserialize(deserializer)?;
206 Ok(Self(Arc::from(&*key)))
207 }
208}
209
210mod arc_str {
212 use std::sync::Arc;
213
214 use serde::{Deserialize, Deserializer, Serializer};
215
216 pub(super) fn serialize<S: Serializer>(
217 value: &Arc<str>,
218 serializer: S,
219 ) -> Result<S::Ok, S::Error> {
220 serializer.serialize_str(value)
221 }
222
223 pub(super) fn deserialize<'de, D: Deserializer<'de>>(
224 deserializer: D,
225 ) -> Result<Arc<str>, D::Error> {
226 let value = <std::borrow::Cow<'de, str>>::deserialize(deserializer)?;
227 Ok(Arc::from(&*value))
228 }
229}
230
231mod opt_arc_str {
233 use std::sync::Arc;
234
235 use serde::{Deserialize, Deserializer, Serializer};
236
237 pub(super) fn serialize<S: Serializer>(
238 value: &Option<Arc<str>>,
239 serializer: S,
240 ) -> Result<S::Ok, S::Error> {
241 match value {
242 Some(value) => serializer.serialize_some(&**value),
243 None => serializer.serialize_none(),
244 }
245 }
246
247 pub(super) fn deserialize<'de, D: Deserializer<'de>>(
248 deserializer: D,
249 ) -> Result<Option<Arc<str>>, D::Error> {
250 let value = <Option<std::borrow::Cow<'de, str>>>::deserialize(deserializer)?;
251 Ok(value.map(|value| Arc::from(&*value)))
252 }
253}
254
255#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
257#[serde(rename_all = "snake_case")]
258pub enum EffectFamily {
259 Completion,
261 Tool,
263 Embed,
265 Rerank,
267 Memory,
269 Retrieve,
271 Custom,
273}
274
275impl EffectFamily {
276 pub const fn name(self) -> &'static str {
278 match self {
279 Self::Completion => "completion",
280 Self::Tool => "tool_call",
281 Self::Embed => "embed",
282 Self::Rerank => "rerank",
283 Self::Memory => "memory",
284 Self::Retrieve => "retrieve",
285 Self::Custom => "custom",
286 }
287 }
288}
289
290impl fmt::Display for EffectFamily {
291 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292 f.write_str(self.name())
293 }
294}
295
296pub trait Family: sealed::Sealed + Clone + Copy + Send + Sync + 'static {
300 const FAMILY: EffectFamily;
302 type Request: WasmCompatSend + 'static;
304 type Answer: WasmCompatSend + 'static;
306 fn wrap(request: Self::Request) -> Result<EffectKind, ErrorReport>;
311 fn unwrap(outcome: Outcome) -> Result<Self::Answer, ErrorReport>;
313 fn mismatch(outcome: &Outcome) -> ErrorReport {
315 ErrorReport::new(
316 crate::error::ErrorKind::Internal,
317 format!(
318 "expected a {} outcome, the handler answered {}",
319 Self::FAMILY,
320 outcome.family()
321 ),
322 )
323 }
324}
325
326mod sealed {
327 pub trait Sealed {}
328}
329
330pub trait Served: sealed::Sealed + 'static {
333 const SERVED: Option<EffectFamily>;
335}
336
337#[diagnostic::do_not_recommend]
340impl<F: Family> Served for F {
341 const SERVED: Option<EffectFamily> = Some(F::FAMILY);
342}
343
344#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
350pub struct ToolCallRequest {
351 pub name: String,
353 pub args: String,
355}
356
357pub trait CustomEffect: Serialize + serde::de::DeserializeOwned + WasmCompatSend + 'static {
362 const KIND: &'static str;
365 type Answer: Serialize + serde::de::DeserializeOwned + WasmCompatSend + 'static;
367}
368
369mod key;
370
371pub use key::Key;
372
373pub fn model_key(label: &str) -> HandlerKey {
375 HandlerKey::from(format!("model:{label}"))
376}
377
378pub fn tool_key(name: &str) -> HandlerKey {
380 HandlerKey::from(format!("tool:{name}"))
381}
382
383pub fn embed_key(label: &str) -> HandlerKey {
385 HandlerKey::from(format!("embed:{label}"))
386}
387
388pub fn rerank_key(label: &str) -> HandlerKey {
390 HandlerKey::from(format!("rerank:{label}"))
391}
392
393pub fn memory_key(label: &str) -> HandlerKey {
396 HandlerKey::from(format!("memory:{label}"))
397}
398
399pub fn retrieve_key(label: &str) -> HandlerKey {
402 HandlerKey::from(format!("retrieve:{label}"))
403}
404
405pub mod family {
412 use std::marker::PhantomData;
413
414 use super::{
415 CustomEffect, EffectFamily, EffectKind, EmbedInputs, EmbedOutputs, Family, MemoryOp,
416 MemoryOutcome, Outcome, RetrieveQuery, RetrievedDocuments, ToolCallRequest, sealed::Sealed,
417 };
418 use crate::{
419 completion::{CompletionRequest, CompletionResponse},
420 error::{ErrorKind, ErrorReport},
421 operation::RerankRequest,
422 rerank::RerankResponse,
423 tool::ToolResult,
424 };
425
426 macro_rules! marker {
427 ($($(#[$doc:meta])* $name:ident => $family:ident, $request:ty, $answer:ty, $wrap:expr, $unwrap:expr;)+) => {$(
428 $(#[$doc])*
429 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
430 pub struct $name;
431
432 impl Sealed for $name {}
433
434 impl Family for $name {
435 const FAMILY: EffectFamily = EffectFamily::$family;
436 type Request = $request;
437 type Answer = $answer;
438
439 fn wrap(request: Self::Request) -> Result<EffectKind, ErrorReport> {
440 let wrap: fn(Self::Request) -> EffectKind = $wrap;
441 Ok(wrap(request))
442 }
443
444 fn unwrap(outcome: Outcome) -> Result<Self::Answer, ErrorReport> {
445 let unwrap: fn(Outcome) -> Result<Self::Answer, ErrorReport> = $unwrap;
446 unwrap(outcome)
447 }
448 }
449 )+};
450 }
451
452 marker! {
453 Completion => Completion, CompletionRequest, CompletionResponse,
456 |request| EffectKind::Completion { request, stream: false },
457 |outcome| match outcome {
458 Outcome::Completion(response) => Ok(response),
459 other => Err(Completion::mismatch(&other)),
460 };
461 Tool => Tool, ToolCallRequest, ToolResult,
463 |request| EffectKind::ToolCall { name: request.name, args: request.args },
464 |outcome| match outcome {
465 Outcome::ToolResult { result } => Ok(result),
466 other => Err(Tool::mismatch(&other)),
467 };
468 Embed => Embed, EmbedInputs, EmbedOutputs,
470 |inputs| EffectKind::Embed { inputs },
471 |outcome| match outcome {
472 Outcome::Embeddings(outputs) => Ok(outputs),
473 other => Err(Embed::mismatch(&other)),
474 };
475 Rerank => Rerank, RerankRequest, RerankResponse,
477 |request| EffectKind::Rerank { request },
478 |outcome| match outcome {
479 Outcome::Reranked(response) => Ok(response),
480 other => Err(Rerank::mismatch(&other)),
481 };
482 Memory => Memory, MemoryOp, MemoryOutcome,
484 |op| EffectKind::Memory { op },
485 |outcome| match outcome {
486 Outcome::Memory(answer) => Ok(answer),
487 other => Err(Memory::mismatch(&other)),
488 };
489 Retrieve => Retrieve, RetrieveQuery, RetrievedDocuments,
491 |query| EffectKind::Retrieve { query },
492 |outcome| match outcome {
493 Outcome::Documents(documents) => Ok(documents),
494 other => Err(Retrieve::mismatch(&other)),
495 };
496 }
497
498 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
501 pub struct Dynamic;
502
503 impl Sealed for Dynamic {}
504
505 impl super::Served for Dynamic {
506 const SERVED: Option<EffectFamily> = None;
507 }
508
509 pub struct Custom<E: CustomEffect>(PhantomData<fn() -> E>);
512
513 impl<E: CustomEffect> Custom<E> {
514 pub const fn new() -> Self {
516 Self(PhantomData)
517 }
518 }
519
520 impl<E: CustomEffect> Clone for Custom<E> {
523 fn clone(&self) -> Self {
524 *self
525 }
526 }
527 impl<E: CustomEffect> Copy for Custom<E> {}
528 impl<E: CustomEffect> Default for Custom<E> {
529 fn default() -> Self {
530 Self::new()
531 }
532 }
533 impl<E: CustomEffect> std::fmt::Debug for Custom<E> {
534 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
535 write!(f, "Custom<{}>", E::KIND)
536 }
537 }
538 impl<E: CustomEffect> PartialEq for Custom<E> {
539 fn eq(&self, _: &Self) -> bool {
540 true
541 }
542 }
543 impl<E: CustomEffect> Eq for Custom<E> {}
544 impl<E: CustomEffect> std::hash::Hash for Custom<E> {
545 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
546 E::KIND.hash(state);
547 }
548 }
549
550 impl<E: CustomEffect> Sealed for Custom<E> {}
551
552 impl<E: CustomEffect> Family for Custom<E> {
553 const FAMILY: EffectFamily = EffectFamily::Custom;
554 type Request = E;
555 type Answer = E::Answer;
556
557 fn wrap(request: E) -> Result<EffectKind, ErrorReport> {
558 serde_json::to_value(&request)
561 .map(|payload| EffectKind::Custom {
562 kind: std::sync::Arc::from(E::KIND),
563 payload,
564 })
565 .map_err(|error| {
566 ErrorReport::new(
567 ErrorKind::Request,
568 format!("the `{}` effect did not serialize: {error}", E::KIND),
569 )
570 })
571 }
572
573 fn unwrap(outcome: Outcome) -> Result<E::Answer, ErrorReport> {
574 match outcome {
575 Outcome::Custom { payload: value } => {
576 serde_json::from_value(value).map_err(|error| {
577 ErrorReport::new(
578 ErrorKind::Internal,
579 format!(
580 "the answer to the `{}` effect did not deserialize: {error}",
581 E::KIND
582 ),
583 )
584 })
585 }
586 other => Err(Self::mismatch(&other)),
587 }
588 }
589 }
590}
591
592#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
594pub struct HandlerDescriptor {
595 pub key: HandlerKey,
597 pub family: FamilyDescriptor,
599 #[serde(default, skip_serializing_if = "Vec::is_empty")]
602 pub layers: Vec<String>,
603}
604
605#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
609#[serde(tag = "family", rename_all = "snake_case")]
610pub enum FamilyDescriptor {
611 Completion {
613 model: ModelRef,
615 capabilities: ProviderCapabilities,
617 },
618 Tool {
620 name: String,
622 description: String,
624 parameters: serde_json::Value,
626 #[serde(default, skip_serializing_if = "Option::is_none")]
628 embedding: Option<ToolEmbeddingDescriptor>,
629 },
630 Embed {
632 model: String,
634 #[serde(default, skip_serializing_if = "Option::is_none")]
636 dims: Option<usize>,
637 max_documents: usize,
639 modality: EmbedModality,
641 },
642 Rerank {
644 model: String,
646 max_documents: usize,
648 },
649 Memory {},
651 Retrieve {},
653 Custom {
655 kind: String,
657 },
658}
659
660impl FamilyDescriptor {
661 pub const fn family(&self) -> EffectFamily {
663 match self {
664 Self::Completion { .. } => EffectFamily::Completion,
665 Self::Tool { .. } => EffectFamily::Tool,
666 Self::Embed { .. } => EffectFamily::Embed,
667 Self::Rerank { .. } => EffectFamily::Rerank,
668 Self::Memory {} => EffectFamily::Memory,
669 Self::Retrieve {} => EffectFamily::Retrieve,
670 Self::Custom { .. } => EffectFamily::Custom,
671 }
672 }
673}
674
675#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
678pub struct ToolEmbeddingDescriptor {
679 pub context: serde_json::Value,
681 pub embedding_docs: Vec<String>,
683}
684
685#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
687#[serde(rename_all = "snake_case")]
688pub enum EmbedModality {
689 Text,
691 Image,
693}
694
695#[derive(Debug, Clone, Serialize, Deserialize)]
697#[serde(tag = "effect", rename_all = "snake_case")]
698pub enum EffectKind {
699 Completion {
701 request: CompletionRequest,
703 stream: bool,
705 },
706 ToolCall {
709 name: String,
711 args: String,
713 },
714 Embed {
716 inputs: EmbedInputs,
718 },
719 Rerank {
721 request: RerankRequest,
723 },
724 Memory {
726 op: MemoryOp,
728 },
729 Retrieve {
731 query: RetrieveQuery,
733 },
734 Custom {
736 #[serde(with = "arc_str")]
738 kind: Arc<str>,
739 payload: serde_json::Value,
741 },
742}
743
744impl EffectKind {
745 pub const fn family(&self) -> EffectFamily {
747 match self {
748 Self::Completion { .. } => EffectFamily::Completion,
749 Self::ToolCall { .. } => EffectFamily::Tool,
750 Self::Embed { .. } => EffectFamily::Embed,
751 Self::Rerank { .. } => EffectFamily::Rerank,
752 Self::Memory { .. } => EffectFamily::Memory,
753 Self::Retrieve { .. } => EffectFamily::Retrieve,
754 Self::Custom { .. } => EffectFamily::Custom,
755 }
756 }
757
758 pub fn name(&self) -> &str {
761 match self {
762 Self::Custom { kind, .. } => kind,
763 Self::Completion { .. }
764 | Self::ToolCall { .. }
765 | Self::Embed { .. }
766 | Self::Rerank { .. }
767 | Self::Memory { .. }
768 | Self::Retrieve { .. } => self.family().name(),
769 }
770 }
771
772 pub const fn streams(&self) -> bool {
775 match self {
776 Self::Completion { stream, .. } => *stream,
777 Self::ToolCall { .. }
778 | Self::Embed { .. }
779 | Self::Rerank { .. }
780 | Self::Memory { .. }
781 | Self::Retrieve { .. }
782 | Self::Custom { .. } => false,
783 }
784 }
785}
786
787#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
790#[serde(tag = "modality", content = "inputs", rename_all = "snake_case")]
791pub enum EmbedInputs {
792 Texts(Vec<String>),
794 Images(Vec<Vec<u8>>),
796}
797
798#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
801#[serde(tag = "op", rename_all = "snake_case")]
802pub enum MemoryOp {
803 Load {
805 conversation: ConversationId,
807 },
808 Append {
810 conversation: ConversationId,
812 messages: Vec<Message>,
814 },
815 Clear {
817 conversation: ConversationId,
819 },
820}
821
822#[derive(Debug, Clone, Serialize, Deserialize)]
827#[serde(tag = "query", rename_all = "snake_case")]
828pub enum RetrieveQuery {
829 TopN {
831 req: VectorSearchRequest<Filter<serde_json::Value>>,
833 },
834 TopNIds {
836 req: VectorSearchRequest<Filter<serde_json::Value>>,
838 },
839}
840
841#[derive(Debug, Clone, Serialize, Deserialize)]
843#[serde(tag = "outcome", rename_all = "snake_case")]
844pub enum Outcome {
845 Completion(CompletionResponse),
847 ToolResult {
851 result: ToolResult,
853 },
854 Embeddings(EmbedOutputs),
856 Reranked(RerankResponse),
858 Memory(MemoryOutcome),
860 Documents(RetrievedDocuments),
862 Custom {
864 payload: serde_json::Value,
866 },
867}
868
869impl Outcome {
870 pub const fn family(&self) -> EffectFamily {
872 match self {
873 Self::Completion(_) => EffectFamily::Completion,
874 Self::ToolResult { .. } => EffectFamily::Tool,
875 Self::Embeddings(_) => EffectFamily::Embed,
876 Self::Reranked(_) => EffectFamily::Rerank,
877 Self::Memory(_) => EffectFamily::Memory,
878 Self::Documents(_) => EffectFamily::Retrieve,
879 Self::Custom { .. } => EffectFamily::Custom,
880 }
881 }
882}
883
884#[derive(Debug, Clone, Serialize, Deserialize)]
886#[serde(tag = "modality", content = "response", rename_all = "snake_case")]
887pub enum EmbedOutputs {
888 Texts(EmbeddingResponse),
890 Images(EmbeddingResponse),
892}
893
894#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
896#[serde(tag = "memory", rename_all = "snake_case")]
897pub enum MemoryOutcome {
898 Loaded {
900 messages: Vec<Message>,
902 },
903 Appended,
905 Cleared,
907}
908
909#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
911#[serde(tag = "retrieved", content = "results", rename_all = "snake_case")]
912pub enum RetrievedDocuments {
913 Scored(Vec<(f64, String, serde_json::Value)>),
915 Ids(Vec<(f64, String)>),
917}
918
919#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
922pub struct Delivery {
923 pub batch: u64,
926 pub id: EffectId,
928 pub kind: DeliveryKind,
930}
931
932#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
934#[serde(tag = "delivery", rename_all = "snake_case")]
935pub enum DeliveryKind {
936 Outcome,
938 Cancelled,
941 Stream {
944 items: usize,
946 },
947}
948
949#[derive(Debug, Clone, Serialize, Deserialize)]
951pub struct EffectRecord {
952 #[serde(deserialize_with = "Option::deserialize")]
958 pub tool_output: Option<crate::tool::ToolResultContext>,
959 pub id: EffectId,
961 pub key: HandlerKey,
963 pub kind: EffectKind,
965 pub outcome: Result<Outcome, ErrorReport>,
967 #[serde(default, skip_serializing_if = "Option::is_none")]
973 pub events: Option<Transcript>,
974 #[serde(default, skip_serializing_if = "Option::is_none")]
978 pub stream_origin: Option<crate::message::Origin>,
979 #[serde(default, skip_serializing_if = "Option::is_none")]
981 pub parent: Option<EffectId>,
982 #[serde(default, skip_serializing_if = "Option::is_none", with = "opt_arc_str")]
985 pub scope: Option<std::sync::Arc<str>>,
986}
987
988#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
991#[serde(transparent)]
992pub struct EffectRow(std::collections::BTreeMap<HandlerKey, EffectFamily>);
993
994#[derive(Debug, Clone, PartialEq, Eq)]
997pub struct RowGap {
998 pub key: HandlerKey,
1000 pub needed: EffectFamily,
1002 pub served: Option<EffectFamily>,
1004}
1005
1006impl std::fmt::Display for RowGap {
1007 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1008 match self.served {
1009 Some(served) => write!(
1010 f,
1011 "`{}` is needed as {} but served as {served}",
1012 self.key, self.needed
1013 ),
1014 None => write!(f, "`{}` ({}) is not served", self.key, self.needed),
1015 }
1016 }
1017}
1018
1019#[derive(Debug, Clone, PartialEq, Eq)]
1021pub enum RowDiff {
1022 Missing {
1024 key: HandlerKey,
1026 family: EffectFamily,
1028 },
1029 Extra {
1031 key: HandlerKey,
1033 family: EffectFamily,
1035 },
1036 Family {
1038 key: HandlerKey,
1040 this: EffectFamily,
1042 other: EffectFamily,
1043 },
1044}
1045
1046impl std::fmt::Display for RowDiff {
1047 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1048 match self {
1049 Self::Missing { key, family } => write!(f, "`{key}` ({family}) is missing"),
1050 Self::Extra { key, family } => write!(f, "`{key}` ({family}) is extra"),
1051 Self::Family { key, this, other } => {
1052 write!(f, "`{key}` is {this} here and {other} there")
1053 }
1054 }
1055 }
1056}
1057
1058impl EffectRow {
1059 pub fn new() -> Self {
1061 Self::default()
1062 }
1063
1064 pub fn insert(&mut self, key: HandlerKey, family: EffectFamily) -> Option<EffectFamily> {
1066 self.0.insert(key, family)
1067 }
1068
1069 pub fn insert_if_absent(&mut self, key: HandlerKey, family: EffectFamily) {
1071 self.0.entry(key).or_insert(family);
1072 }
1073
1074 pub fn remove(&mut self, key: &HandlerKey) -> Option<EffectFamily> {
1076 self.0.remove(key)
1077 }
1078
1079 pub fn get(&self, key: &HandlerKey) -> Option<&EffectFamily> {
1081 self.0.get(key)
1082 }
1083
1084 pub fn contains_key(&self, key: &HandlerKey) -> bool {
1086 self.0.contains_key(key)
1087 }
1088
1089 pub fn keys(&self) -> impl Iterator<Item = &HandlerKey> {
1091 self.0.keys()
1092 }
1093
1094 pub fn iter(&self) -> impl Iterator<Item = (&HandlerKey, &EffectFamily)> {
1096 self.0.iter()
1097 }
1098
1099 pub fn len(&self) -> usize {
1101 self.0.len()
1102 }
1103
1104 pub fn is_empty(&self) -> bool {
1106 self.0.is_empty()
1107 }
1108
1109 pub fn is_subset_of(&self, handlers: &[HandlerDescriptor]) -> Result<(), RowGap> {
1112 for (key, needed) in &self.0 {
1113 let served = handlers
1114 .iter()
1115 .find(|descriptor| &descriptor.key == key)
1116 .map(|descriptor| descriptor.family.family());
1117 match served {
1118 Some(served) if served == *needed => {}
1119 served => {
1120 return Err(RowGap {
1121 key: key.clone(),
1122 needed: *needed,
1123 served,
1124 });
1125 }
1126 }
1127 }
1128 Ok(())
1129 }
1130
1131 pub fn diff(&self, other: &EffectRow) -> Vec<RowDiff> {
1134 let mut diffs = Vec::new();
1135 for (key, family) in &self.0 {
1136 match other.0.get(key) {
1137 None => diffs.push(RowDiff::Missing {
1138 key: key.clone(),
1139 family: *family,
1140 }),
1141 Some(theirs) if theirs != family => diffs.push(RowDiff::Family {
1142 key: key.clone(),
1143 this: *family,
1144 other: *theirs,
1145 }),
1146 Some(_) => {}
1147 }
1148 }
1149 for (key, family) in &other.0 {
1150 if !self.0.contains_key(key) {
1151 diffs.push(RowDiff::Extra {
1152 key: key.clone(),
1153 family: *family,
1154 });
1155 }
1156 }
1157 diffs
1158 }
1159}
1160
1161impl FromIterator<(HandlerKey, EffectFamily)> for EffectRow {
1162 fn from_iter<I: IntoIterator<Item = (HandlerKey, EffectFamily)>>(iter: I) -> Self {
1163 Self(iter.into_iter().collect())
1164 }
1165}
1166
1167impl<'a> IntoIterator for &'a EffectRow {
1168 type Item = (&'a HandlerKey, &'a EffectFamily);
1169 type IntoIter = std::collections::btree_map::Iter<'a, HandlerKey, EffectFamily>;
1170
1171 fn into_iter(self) -> Self::IntoIter {
1172 self.0.iter()
1173 }
1174}
1175
1176impl IntoIterator for EffectRow {
1177 type Item = (HandlerKey, EffectFamily);
1178 type IntoIter = std::collections::btree_map::IntoIter<HandlerKey, EffectFamily>;
1179
1180 fn into_iter(self) -> Self::IntoIter {
1181 self.0.into_iter()
1182 }
1183}
1184
1185const _: fn() = || {
1187 fn assert_wire<T: Clone + Send + Sync + 'static + Serialize + serde::de::DeserializeOwned>() {}
1188 assert_wire::<EffectId>();
1189 assert_wire::<HandlerKey>();
1190 assert_wire::<EffectFamily>();
1191 assert_wire::<HandlerDescriptor>();
1192 assert_wire::<FamilyDescriptor>();
1193 assert_wire::<ToolEmbeddingDescriptor>();
1194 assert_wire::<EmbedModality>();
1195 assert_wire::<EffectKind>();
1196 assert_wire::<EmbedInputs>();
1197 assert_wire::<RerankRequest>();
1198 assert_wire::<MemoryOp>();
1199 assert_wire::<RetrieveQuery>();
1200 assert_wire::<Outcome>();
1201 assert_wire::<EmbedOutputs>();
1202 assert_wire::<MemoryOutcome>();
1203 assert_wire::<RetrievedDocuments>();
1204 assert_wire::<EffectRecord>();
1205 assert_wire::<EffectRow>();
1206 assert_wire::<Transcript>();
1207};
1208
1209#[cfg(test)]
1210mod tests;