1use std::{fmt, sync::Arc};
14
15use serde::{Deserialize, Serialize};
16
17use crate::{
18 completion::{CompletionRequest, CompletionResponse, Message, ModelRef, ProviderCapabilities},
19 embeddings::{EmbeddingResponse, ImageEmbeddingResponse},
20 error::ErrorReport,
21 id::ConversationId,
22 rerank::RerankResponse,
23 streaming::Transcript,
24 tool::ToolResult,
25 vector_store::request::{Filter, VectorSearchRequest},
26 wasm_compat::WasmCompatSend,
27};
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
35#[serde(transparent)]
36pub struct EffectId(u64);
37
38impl EffectId {
39 pub const fn from_raw(raw: u64) -> Self {
41 Self(raw)
42 }
43
44 pub const fn as_u64(self) -> u64 {
46 self.0
47 }
48}
49
50impl fmt::Display for EffectId {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 write!(f, "effect:{}", self.0)
53 }
54}
55
56#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
58pub struct HandlerKey(Arc<str>);
59
60impl HandlerKey {
61 pub fn new(key: impl Into<Arc<str>>) -> Self {
63 Self(key.into())
64 }
65
66 pub fn as_str(&self) -> &str {
68 &self.0
69 }
70
71 pub fn parts(&self) -> KeyParts {
74 KeyParts::parse(&self.0)
75 }
76}
77
78#[derive(Debug, Clone, PartialEq, Eq, Hash)]
94pub struct KeyParts {
95 pub owner: Option<Arc<str>>,
97 pub kind: Option<Arc<str>>,
99 pub label: Arc<str>,
101 pub generation: Option<u64>,
105}
106
107impl KeyParts {
108 pub fn parse(key: &str) -> Self {
110 let (owner, rest) = match key.split_once('/') {
111 Some((owner, rest)) if !owner.is_empty() && !rest.is_empty() => {
112 (Some(Arc::from(owner)), rest)
113 }
114 _ => (None, key),
115 };
116 let (rest, generation) = match rest.rsplit_once('#') {
117 Some((head, digits))
118 if !digits.is_empty()
119 && (digits.len() == 1 || !digits.starts_with('0'))
120 && digits.bytes().all(|b| b.is_ascii_digit()) =>
121 {
122 match digits.parse::<u64>() {
123 Ok(generation) => (head, Some(generation)),
124 Err(_) => (rest, None),
125 }
126 }
127 _ => (rest, None),
128 };
129 let (kind, label) = match rest.split_once(':') {
130 Some((kind, label)) if !kind.is_empty() && !label.is_empty() => {
131 (Some(Arc::from(kind)), label)
132 }
133 _ => (None, rest),
134 };
135 Self {
136 owner,
137 kind,
138 label: Arc::from(label),
139 generation,
140 }
141 }
142
143 pub fn to_key(&self) -> HandlerKey {
145 HandlerKey::from(self.to_string())
146 }
147}
148
149impl fmt::Display for KeyParts {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 if let Some(owner) = &self.owner {
152 write!(f, "{owner}/")?;
153 }
154 if let Some(kind) = &self.kind {
155 write!(f, "{kind}:")?;
156 }
157 f.write_str(&self.label)?;
158 if let Some(generation) = self.generation {
159 write!(f, "#{generation}")?;
160 }
161 Ok(())
162 }
163}
164
165impl fmt::Debug for HandlerKey {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 write!(f, "HandlerKey({:?})", &*self.0)
168 }
169}
170
171impl fmt::Display for HandlerKey {
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 f.write_str(&self.0)
174 }
175}
176
177impl From<&str> for HandlerKey {
178 fn from(key: &str) -> Self {
179 Self(Arc::from(key))
180 }
181}
182
183impl From<String> for HandlerKey {
184 fn from(key: String) -> Self {
185 Self(Arc::from(key))
186 }
187}
188
189impl AsRef<str> for HandlerKey {
190 fn as_ref(&self) -> &str {
191 &self.0
192 }
193}
194
195impl Serialize for HandlerKey {
197 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
198 serializer.serialize_str(&self.0)
199 }
200}
201
202impl<'de> Deserialize<'de> for HandlerKey {
203 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
204 let key = <std::borrow::Cow<'de, str>>::deserialize(deserializer)?;
205 Ok(Self(Arc::from(&*key)))
206 }
207}
208
209mod arc_str {
211 use std::sync::Arc;
212
213 use serde::{Deserialize, Deserializer, Serializer};
214
215 pub(super) fn serialize<S: Serializer>(
216 value: &Arc<str>,
217 serializer: S,
218 ) -> Result<S::Ok, S::Error> {
219 serializer.serialize_str(value)
220 }
221
222 pub(super) fn deserialize<'de, D: Deserializer<'de>>(
223 deserializer: D,
224 ) -> Result<Arc<str>, D::Error> {
225 let value = <std::borrow::Cow<'de, str>>::deserialize(deserializer)?;
226 Ok(Arc::from(&*value))
227 }
228}
229
230mod opt_arc_str {
232 use std::sync::Arc;
233
234 use serde::{Deserialize, Deserializer, Serializer};
235
236 pub(super) fn serialize<S: Serializer>(
237 value: &Option<Arc<str>>,
238 serializer: S,
239 ) -> Result<S::Ok, S::Error> {
240 match value {
241 Some(value) => serializer.serialize_some(&**value),
242 None => serializer.serialize_none(),
243 }
244 }
245
246 pub(super) fn deserialize<'de, D: Deserializer<'de>>(
247 deserializer: D,
248 ) -> Result<Option<Arc<str>>, D::Error> {
249 let value = <Option<std::borrow::Cow<'de, str>>>::deserialize(deserializer)?;
250 Ok(value.map(|value| Arc::from(&*value)))
251 }
252}
253
254#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
256#[serde(rename_all = "snake_case")]
257pub enum EffectFamily {
258 Completion,
260 Tool,
262 Embed,
264 Rerank,
266 Memory,
268 Retrieve,
270 Custom,
272}
273
274impl EffectFamily {
275 pub const fn name(self) -> &'static str {
277 match self {
278 Self::Completion => "completion",
279 Self::Tool => "tool_call",
280 Self::Embed => "embed",
281 Self::Rerank => "rerank",
282 Self::Memory => "memory",
283 Self::Retrieve => "retrieve",
284 Self::Custom => "custom",
285 }
286 }
287}
288
289impl fmt::Display for EffectFamily {
290 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291 f.write_str(self.name())
292 }
293}
294
295pub trait Family: sealed::Sealed + Clone + Copy + Send + Sync + 'static {
299 const FAMILY: EffectFamily;
301 type Request: WasmCompatSend + 'static;
303 type Answer: WasmCompatSend + 'static;
305 fn wrap(request: Self::Request) -> Result<EffectKind, ErrorReport>;
310 fn unwrap(outcome: Outcome) -> Result<Self::Answer, ErrorReport>;
312 fn mismatch(outcome: &Outcome) -> ErrorReport {
314 ErrorReport::new(
315 crate::error::ErrorKind::Internal,
316 format!(
317 "expected a {} outcome, the handler answered {}",
318 Self::FAMILY,
319 outcome.family()
320 ),
321 )
322 }
323}
324
325mod sealed {
326 pub trait Sealed {}
327}
328
329pub trait Served: sealed::Sealed + 'static {
332 const SERVED: Option<EffectFamily>;
334}
335
336#[diagnostic::do_not_recommend]
339impl<F: Family> Served for F {
340 const SERVED: Option<EffectFamily> = Some(F::FAMILY);
341}
342
343#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
349pub struct ToolCallRequest {
350 pub name: String,
352 pub args: String,
354}
355
356pub trait CustomEffect: Serialize + serde::de::DeserializeOwned + WasmCompatSend + 'static {
361 const KIND: &'static str;
364 type Answer: Serialize + serde::de::DeserializeOwned + WasmCompatSend + 'static;
366}
367
368mod key;
369
370pub use key::Key;
371
372pub fn model_key(label: &str) -> HandlerKey {
374 HandlerKey::from(format!("model:{label}"))
375}
376
377pub fn tool_key(name: &str) -> HandlerKey {
379 HandlerKey::from(format!("tool:{name}"))
380}
381
382pub fn embed_key(label: &str) -> HandlerKey {
384 HandlerKey::from(format!("embed:{label}"))
385}
386
387pub fn rerank_key(label: &str) -> HandlerKey {
389 HandlerKey::from(format!("rerank:{label}"))
390}
391
392pub fn memory_key(label: &str) -> HandlerKey {
395 HandlerKey::from(format!("memory:{label}"))
396}
397
398pub fn retrieve_key(label: &str) -> HandlerKey {
401 HandlerKey::from(format!("retrieve:{label}"))
402}
403
404pub mod family {
411 use std::marker::PhantomData;
412
413 use super::{
414 CustomEffect, EffectFamily, EffectKind, EmbedInputs, EmbedOutputs, Family, MemoryOp,
415 MemoryOutcome, Outcome, RerankRequest, RetrieveQuery, RetrievedDocuments, ToolCallRequest,
416 sealed::Sealed,
417 };
418 use crate::{
419 completion::{CompletionRequest, CompletionResponse},
420 error::{ErrorKind, ErrorReport},
421 rerank::RerankResponse,
422 tool::ToolResult,
423 };
424
425 macro_rules! marker {
426 ($($(#[$doc:meta])* $name:ident => $family:ident, $request:ty, $answer:ty, $wrap:expr, $unwrap:expr;)+) => {$(
427 $(#[$doc])*
428 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
429 pub struct $name;
430
431 impl Sealed for $name {}
432
433 impl Family for $name {
434 const FAMILY: EffectFamily = EffectFamily::$family;
435 type Request = $request;
436 type Answer = $answer;
437
438 fn wrap(request: Self::Request) -> Result<EffectKind, ErrorReport> {
439 let wrap: fn(Self::Request) -> EffectKind = $wrap;
440 Ok(wrap(request))
441 }
442
443 fn unwrap(outcome: Outcome) -> Result<Self::Answer, ErrorReport> {
444 let unwrap: fn(Outcome) -> Result<Self::Answer, ErrorReport> = $unwrap;
445 unwrap(outcome)
446 }
447 }
448 )+};
449 }
450
451 marker! {
452 Completion => Completion, CompletionRequest, CompletionResponse,
455 |request| EffectKind::Completion { request, stream: false },
456 |outcome| match outcome {
457 Outcome::Completion(response) => Ok(response),
458 other => Err(Completion::mismatch(&other)),
459 };
460 Tool => Tool, ToolCallRequest, ToolResult,
462 |request| EffectKind::ToolCall { name: request.name, args: request.args },
463 |outcome| match outcome {
464 Outcome::ToolResult { result } => Ok(result),
465 other => Err(Tool::mismatch(&other)),
466 };
467 Embed => Embed, EmbedInputs, EmbedOutputs,
469 |inputs| EffectKind::Embed { inputs },
470 |outcome| match outcome {
471 Outcome::Embeddings(outputs) => Ok(outputs),
472 other => Err(Embed::mismatch(&other)),
473 };
474 Rerank => Rerank, RerankRequest, RerankResponse,
476 |request| EffectKind::Rerank { request },
477 |outcome| match outcome {
478 Outcome::Reranked(response) => Ok(response),
479 other => Err(Rerank::mismatch(&other)),
480 };
481 Memory => Memory, MemoryOp, MemoryOutcome,
483 |op| EffectKind::Memory { op },
484 |outcome| match outcome {
485 Outcome::Memory(answer) => Ok(answer),
486 other => Err(Memory::mismatch(&other)),
487 };
488 Retrieve => Retrieve, RetrieveQuery, RetrievedDocuments,
490 |query| EffectKind::Retrieve { query },
491 |outcome| match outcome {
492 Outcome::Documents(documents) => Ok(documents),
493 other => Err(Retrieve::mismatch(&other)),
494 };
495 }
496
497 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
500 pub struct Dynamic;
501
502 impl Sealed for Dynamic {}
503
504 impl super::Served for Dynamic {
505 const SERVED: Option<EffectFamily> = None;
506 }
507
508 pub struct Custom<E: CustomEffect>(PhantomData<fn() -> E>);
511
512 impl<E: CustomEffect> Custom<E> {
513 pub const fn new() -> Self {
515 Self(PhantomData)
516 }
517 }
518
519 impl<E: CustomEffect> Clone for Custom<E> {
522 fn clone(&self) -> Self {
523 *self
524 }
525 }
526 impl<E: CustomEffect> Copy for Custom<E> {}
527 impl<E: CustomEffect> Default for Custom<E> {
528 fn default() -> Self {
529 Self::new()
530 }
531 }
532 impl<E: CustomEffect> std::fmt::Debug for Custom<E> {
533 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
534 write!(f, "Custom<{}>", E::KIND)
535 }
536 }
537 impl<E: CustomEffect> PartialEq for Custom<E> {
538 fn eq(&self, _: &Self) -> bool {
539 true
540 }
541 }
542 impl<E: CustomEffect> Eq for Custom<E> {}
543 impl<E: CustomEffect> std::hash::Hash for Custom<E> {
544 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
545 E::KIND.hash(state);
546 }
547 }
548
549 impl<E: CustomEffect> Sealed for Custom<E> {}
550
551 impl<E: CustomEffect> Family for Custom<E> {
552 const FAMILY: EffectFamily = EffectFamily::Custom;
553 type Request = E;
554 type Answer = E::Answer;
555
556 fn wrap(request: E) -> Result<EffectKind, ErrorReport> {
557 serde_json::to_value(&request)
560 .map(|payload| EffectKind::Custom {
561 kind: std::sync::Arc::from(E::KIND),
562 payload,
563 })
564 .map_err(|error| {
565 ErrorReport::new(
566 ErrorKind::Request,
567 format!("the `{}` effect did not serialize: {error}", E::KIND),
568 )
569 })
570 }
571
572 fn unwrap(outcome: Outcome) -> Result<E::Answer, ErrorReport> {
573 match outcome {
574 Outcome::Custom { payload: value } => {
575 serde_json::from_value(value).map_err(|error| {
576 ErrorReport::new(
577 ErrorKind::Internal,
578 format!(
579 "the answer to the `{}` effect did not deserialize: {error}",
580 E::KIND
581 ),
582 )
583 })
584 }
585 other => Err(Self::mismatch(&other)),
586 }
587 }
588 }
589}
590
591#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
593pub struct HandlerDescriptor {
594 pub key: HandlerKey,
596 pub family: FamilyDescriptor,
598 #[serde(default, skip_serializing_if = "Vec::is_empty")]
601 pub layers: Vec<String>,
602}
603
604#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
608#[serde(tag = "family", rename_all = "snake_case")]
609pub enum FamilyDescriptor {
610 Completion {
612 model: ModelRef,
614 capabilities: ProviderCapabilities,
616 },
617 Tool {
619 name: String,
621 description: String,
623 parameters: serde_json::Value,
625 #[serde(default, skip_serializing_if = "Option::is_none")]
627 embedding: Option<ToolEmbeddingDescriptor>,
628 },
629 Embed {
631 model: String,
633 #[serde(default, skip_serializing_if = "Option::is_none")]
635 dims: Option<usize>,
636 max_documents: usize,
638 modality: EmbedModality,
640 },
641 Rerank {
643 model: String,
645 max_documents: usize,
647 },
648 Memory {},
650 Retrieve {},
652 Custom {
654 kind: String,
656 },
657}
658
659impl FamilyDescriptor {
660 pub const fn family(&self) -> EffectFamily {
662 match self {
663 Self::Completion { .. } => EffectFamily::Completion,
664 Self::Tool { .. } => EffectFamily::Tool,
665 Self::Embed { .. } => EffectFamily::Embed,
666 Self::Rerank { .. } => EffectFamily::Rerank,
667 Self::Memory {} => EffectFamily::Memory,
668 Self::Retrieve {} => EffectFamily::Retrieve,
669 Self::Custom { .. } => EffectFamily::Custom,
670 }
671 }
672}
673
674#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
677pub struct ToolEmbeddingDescriptor {
678 pub context: serde_json::Value,
680 pub embedding_docs: Vec<String>,
682}
683
684#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
686#[serde(rename_all = "snake_case")]
687pub enum EmbedModality {
688 Text,
690 Image,
692}
693
694#[derive(Debug, Clone, Serialize, Deserialize)]
696#[serde(tag = "effect", rename_all = "snake_case")]
697pub enum EffectKind {
698 Completion {
700 request: CompletionRequest,
702 stream: bool,
704 },
705 ToolCall {
708 name: String,
710 args: String,
712 },
713 Embed {
715 inputs: EmbedInputs,
717 },
718 Rerank {
720 request: RerankRequest,
722 },
723 Memory {
725 op: MemoryOp,
727 },
728 Retrieve {
730 query: RetrieveQuery,
732 },
733 Custom {
735 #[serde(with = "arc_str")]
737 kind: Arc<str>,
738 payload: serde_json::Value,
740 },
741}
742
743impl EffectKind {
744 pub const fn family(&self) -> EffectFamily {
746 match self {
747 Self::Completion { .. } => EffectFamily::Completion,
748 Self::ToolCall { .. } => EffectFamily::Tool,
749 Self::Embed { .. } => EffectFamily::Embed,
750 Self::Rerank { .. } => EffectFamily::Rerank,
751 Self::Memory { .. } => EffectFamily::Memory,
752 Self::Retrieve { .. } => EffectFamily::Retrieve,
753 Self::Custom { .. } => EffectFamily::Custom,
754 }
755 }
756
757 pub fn name(&self) -> &str {
760 match self {
761 Self::Custom { kind, .. } => kind,
762 Self::Completion { .. }
763 | Self::ToolCall { .. }
764 | Self::Embed { .. }
765 | Self::Rerank { .. }
766 | Self::Memory { .. }
767 | Self::Retrieve { .. } => self.family().name(),
768 }
769 }
770
771 pub const fn streams(&self) -> bool {
774 match self {
775 Self::Completion { stream, .. } => *stream,
776 Self::ToolCall { .. }
777 | Self::Embed { .. }
778 | Self::Rerank { .. }
779 | Self::Memory { .. }
780 | Self::Retrieve { .. }
781 | Self::Custom { .. } => false,
782 }
783 }
784}
785
786#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
789#[serde(tag = "modality", content = "inputs", rename_all = "snake_case")]
790pub enum EmbedInputs {
791 Texts(Vec<String>),
793 Images(Vec<Vec<u8>>),
795}
796
797#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
800pub struct RerankRequest {
801 pub query: String,
803 pub documents: Vec<String>,
805}
806
807#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
810#[serde(tag = "op", rename_all = "snake_case")]
811pub enum MemoryOp {
812 Load {
814 conversation: ConversationId,
816 },
817 Append {
819 conversation: ConversationId,
821 messages: Vec<Message>,
823 },
824 Clear {
826 conversation: ConversationId,
828 },
829}
830
831#[derive(Debug, Clone, Serialize, Deserialize)]
836#[serde(tag = "query", rename_all = "snake_case")]
837pub enum RetrieveQuery {
838 TopN {
840 req: VectorSearchRequest<Filter<serde_json::Value>>,
842 },
843 TopNIds {
845 req: VectorSearchRequest<Filter<serde_json::Value>>,
847 },
848}
849
850#[derive(Debug, Clone, Serialize, Deserialize)]
852#[serde(tag = "outcome", rename_all = "snake_case")]
853pub enum Outcome {
854 Completion(CompletionResponse),
856 ToolResult {
860 result: ToolResult,
862 },
863 Embeddings(EmbedOutputs),
865 Reranked(RerankResponse),
867 Memory(MemoryOutcome),
869 Documents(RetrievedDocuments),
871 Custom {
873 payload: serde_json::Value,
875 },
876}
877
878impl Outcome {
879 pub const fn family(&self) -> EffectFamily {
881 match self {
882 Self::Completion(_) => EffectFamily::Completion,
883 Self::ToolResult { .. } => EffectFamily::Tool,
884 Self::Embeddings(_) => EffectFamily::Embed,
885 Self::Reranked(_) => EffectFamily::Rerank,
886 Self::Memory(_) => EffectFamily::Memory,
887 Self::Documents(_) => EffectFamily::Retrieve,
888 Self::Custom { .. } => EffectFamily::Custom,
889 }
890 }
891}
892
893#[derive(Debug, Clone, Serialize, Deserialize)]
895#[serde(tag = "modality", content = "response", rename_all = "snake_case")]
896pub enum EmbedOutputs {
897 Texts(EmbeddingResponse),
899 Images(ImageEmbeddingResponse),
901}
902
903#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
905#[serde(tag = "memory", rename_all = "snake_case")]
906pub enum MemoryOutcome {
907 Loaded {
909 messages: Vec<Message>,
911 },
912 Appended,
914 Cleared,
916}
917
918#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
920#[serde(tag = "retrieved", content = "results", rename_all = "snake_case")]
921pub enum RetrievedDocuments {
922 Scored(Vec<(f64, String, serde_json::Value)>),
924 Ids(Vec<(f64, String)>),
926}
927
928#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
931pub struct Delivery {
932 pub batch: u64,
935 pub id: EffectId,
937 pub kind: DeliveryKind,
939}
940
941#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
943#[serde(tag = "delivery", rename_all = "snake_case")]
944pub enum DeliveryKind {
945 Outcome,
947 Cancelled,
950 Stream {
953 items: usize,
955 },
956}
957
958#[derive(Debug, Clone, Serialize, Deserialize)]
960pub struct EffectRecord {
961 #[serde(deserialize_with = "Option::deserialize")]
967 pub tool_output: Option<crate::tool::ToolResultContext>,
968 pub id: EffectId,
970 pub key: HandlerKey,
972 pub kind: EffectKind,
974 pub outcome: Result<Outcome, ErrorReport>,
976 #[serde(default, skip_serializing_if = "Option::is_none")]
982 pub events: Option<Transcript>,
983 #[serde(default, skip_serializing_if = "Option::is_none")]
985 pub parent: Option<EffectId>,
986 #[serde(default, skip_serializing_if = "Option::is_none", with = "opt_arc_str")]
989 pub scope: Option<std::sync::Arc<str>>,
990}
991
992#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
995#[serde(transparent)]
996pub struct EffectRow(std::collections::BTreeMap<HandlerKey, EffectFamily>);
997
998#[derive(Debug, Clone, PartialEq, Eq)]
1001pub struct RowGap {
1002 pub key: HandlerKey,
1004 pub needed: EffectFamily,
1006 pub served: Option<EffectFamily>,
1008}
1009
1010impl std::fmt::Display for RowGap {
1011 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1012 match self.served {
1013 Some(served) => write!(
1014 f,
1015 "`{}` is needed as {} but served as {served}",
1016 self.key, self.needed
1017 ),
1018 None => write!(f, "`{}` ({}) is not served", self.key, self.needed),
1019 }
1020 }
1021}
1022
1023#[derive(Debug, Clone, PartialEq, Eq)]
1025pub enum RowDiff {
1026 Missing {
1028 key: HandlerKey,
1030 family: EffectFamily,
1032 },
1033 Extra {
1035 key: HandlerKey,
1037 family: EffectFamily,
1039 },
1040 Family {
1042 key: HandlerKey,
1044 this: EffectFamily,
1046 other: EffectFamily,
1047 },
1048}
1049
1050impl std::fmt::Display for RowDiff {
1051 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1052 match self {
1053 Self::Missing { key, family } => write!(f, "`{key}` ({family}) is missing"),
1054 Self::Extra { key, family } => write!(f, "`{key}` ({family}) is extra"),
1055 Self::Family { key, this, other } => {
1056 write!(f, "`{key}` is {this} here and {other} there")
1057 }
1058 }
1059 }
1060}
1061
1062impl EffectRow {
1063 pub fn new() -> Self {
1065 Self::default()
1066 }
1067
1068 pub fn insert(&mut self, key: HandlerKey, family: EffectFamily) -> Option<EffectFamily> {
1070 self.0.insert(key, family)
1071 }
1072
1073 pub fn insert_if_absent(&mut self, key: HandlerKey, family: EffectFamily) {
1075 self.0.entry(key).or_insert(family);
1076 }
1077
1078 pub fn remove(&mut self, key: &HandlerKey) -> Option<EffectFamily> {
1080 self.0.remove(key)
1081 }
1082
1083 pub fn get(&self, key: &HandlerKey) -> Option<&EffectFamily> {
1085 self.0.get(key)
1086 }
1087
1088 pub fn contains_key(&self, key: &HandlerKey) -> bool {
1090 self.0.contains_key(key)
1091 }
1092
1093 pub fn keys(&self) -> impl Iterator<Item = &HandlerKey> {
1095 self.0.keys()
1096 }
1097
1098 pub fn iter(&self) -> impl Iterator<Item = (&HandlerKey, &EffectFamily)> {
1100 self.0.iter()
1101 }
1102
1103 pub fn len(&self) -> usize {
1105 self.0.len()
1106 }
1107
1108 pub fn is_empty(&self) -> bool {
1110 self.0.is_empty()
1111 }
1112
1113 pub fn is_subset_of(&self, handlers: &[HandlerDescriptor]) -> Result<(), RowGap> {
1116 for (key, needed) in &self.0 {
1117 let served = handlers
1118 .iter()
1119 .find(|descriptor| &descriptor.key == key)
1120 .map(|descriptor| descriptor.family.family());
1121 match served {
1122 Some(served) if served == *needed => {}
1123 served => {
1124 return Err(RowGap {
1125 key: key.clone(),
1126 needed: *needed,
1127 served,
1128 });
1129 }
1130 }
1131 }
1132 Ok(())
1133 }
1134
1135 pub fn diff(&self, other: &EffectRow) -> Vec<RowDiff> {
1138 let mut diffs = Vec::new();
1139 for (key, family) in &self.0 {
1140 match other.0.get(key) {
1141 None => diffs.push(RowDiff::Missing {
1142 key: key.clone(),
1143 family: *family,
1144 }),
1145 Some(theirs) if theirs != family => diffs.push(RowDiff::Family {
1146 key: key.clone(),
1147 this: *family,
1148 other: *theirs,
1149 }),
1150 Some(_) => {}
1151 }
1152 }
1153 for (key, family) in &other.0 {
1154 if !self.0.contains_key(key) {
1155 diffs.push(RowDiff::Extra {
1156 key: key.clone(),
1157 family: *family,
1158 });
1159 }
1160 }
1161 diffs
1162 }
1163}
1164
1165impl FromIterator<(HandlerKey, EffectFamily)> for EffectRow {
1166 fn from_iter<I: IntoIterator<Item = (HandlerKey, EffectFamily)>>(iter: I) -> Self {
1167 Self(iter.into_iter().collect())
1168 }
1169}
1170
1171impl<'a> IntoIterator for &'a EffectRow {
1172 type Item = (&'a HandlerKey, &'a EffectFamily);
1173 type IntoIter = std::collections::btree_map::Iter<'a, HandlerKey, EffectFamily>;
1174
1175 fn into_iter(self) -> Self::IntoIter {
1176 self.0.iter()
1177 }
1178}
1179
1180impl IntoIterator for EffectRow {
1181 type Item = (HandlerKey, EffectFamily);
1182 type IntoIter = std::collections::btree_map::IntoIter<HandlerKey, EffectFamily>;
1183
1184 fn into_iter(self) -> Self::IntoIter {
1185 self.0.into_iter()
1186 }
1187}
1188
1189const _: fn() = || {
1191 fn assert_wire<T: Clone + Send + Sync + 'static + Serialize + serde::de::DeserializeOwned>() {}
1192 assert_wire::<EffectId>();
1193 assert_wire::<HandlerKey>();
1194 assert_wire::<EffectFamily>();
1195 assert_wire::<HandlerDescriptor>();
1196 assert_wire::<FamilyDescriptor>();
1197 assert_wire::<ToolEmbeddingDescriptor>();
1198 assert_wire::<EmbedModality>();
1199 assert_wire::<EffectKind>();
1200 assert_wire::<EmbedInputs>();
1201 assert_wire::<RerankRequest>();
1202 assert_wire::<MemoryOp>();
1203 assert_wire::<RetrieveQuery>();
1204 assert_wire::<Outcome>();
1205 assert_wire::<EmbedOutputs>();
1206 assert_wire::<MemoryOutcome>();
1207 assert_wire::<RetrievedDocuments>();
1208 assert_wire::<EffectRecord>();
1209 assert_wire::<EffectRow>();
1210 assert_wire::<Transcript>();
1211};
1212
1213#[cfg(test)]
1214mod tests;