1use std::{borrow::Cow, fmt, sync::Arc};
14
15use serde::{Deserialize, Serialize};
16
17use crate::{
18 completion::{CompletionRequest, CompletionResponse, ModelRef, ProviderCapabilities},
19 embeddings::EmbeddingResponse,
20 error::ErrorReport,
21 operation::RerankRequest,
22 rerank::RerankResponse,
23 streaming::StreamRecord,
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)]
34#[serde(transparent)]
35pub struct EffectId(u64);
36
37impl EffectId {
38 pub const fn from_raw(raw: u64) -> Self {
40 Self(raw)
41 }
42
43 pub const fn as_u64(self) -> u64 {
45 self.0
46 }
47}
48
49impl fmt::Display for EffectId {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 write!(f, "effect:{}", self.0)
52 }
53}
54
55#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
57#[serde(transparent)]
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
144impl fmt::Display for KeyParts {
145 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146 if let Some(owner) = &self.owner {
147 write!(f, "{owner}/")?;
148 }
149 if let Some(kind) = &self.kind {
150 write!(f, "{kind}:")?;
151 }
152 f.write_str(&self.label)?;
153 if let Some(generation) = self.generation {
154 write!(f, "#{generation}")?;
155 }
156 Ok(())
157 }
158}
159
160impl fmt::Debug for HandlerKey {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 write!(f, "HandlerKey({:?})", &*self.0)
163 }
164}
165
166impl fmt::Display for HandlerKey {
167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168 f.write_str(&self.0)
169 }
170}
171
172impl From<&str> for HandlerKey {
173 fn from(key: &str) -> Self {
174 Self(Arc::from(key))
175 }
176}
177
178impl From<String> for HandlerKey {
179 fn from(key: String) -> Self {
180 Self(Arc::from(key))
181 }
182}
183
184impl AsRef<str> for HandlerKey {
185 fn as_ref(&self) -> &str {
186 &self.0
187 }
188}
189
190#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
192#[serde(rename_all = "snake_case")]
193pub enum EffectFamily {
194 Completion,
196 Tool,
198 Embed,
200 Rerank,
202 Retrieve,
204 Custom,
206}
207
208impl EffectFamily {
209 pub const fn name(self) -> &'static str {
211 match self {
212 Self::Completion => "completion",
213 Self::Tool => "tool_call",
214 Self::Embed => "embed",
215 Self::Rerank => "rerank",
216 Self::Retrieve => "retrieve",
217 Self::Custom => "custom",
218 }
219 }
220}
221
222impl fmt::Display for EffectFamily {
223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224 f.write_str(self.name())
225 }
226}
227
228pub trait Family: sealed::Sealed + Clone + Copy + Send + Sync + 'static {
232 const FAMILY: EffectFamily;
234 type Request: WasmCompatSend + 'static;
236 type Answer: WasmCompatSend + 'static;
238 fn wrap(request: Self::Request) -> Result<EffectKind, ErrorReport>;
243 fn unwrap(outcome: Answered) -> Result<Self::Answer, ErrorReport>;
245 fn mismatch(outcome: &Answered) -> ErrorReport {
247 ErrorReport::new(
248 crate::error::ErrorKind::Internal,
249 format!(
250 "expected a {} outcome, the handler answered {}",
251 Self::FAMILY,
252 outcome.family()
253 ),
254 )
255 }
256}
257
258mod sealed {
259 pub trait Sealed {}
260}
261
262pub trait Served: sealed::Sealed + 'static {
265 const SERVED: Option<EffectFamily>;
267}
268
269#[diagnostic::do_not_recommend]
272impl<F: Family> Served for F {
273 const SERVED: Option<EffectFamily> = Some(F::FAMILY);
274}
275
276#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
282pub struct ToolCallRequest {
283 pub name: String,
285 pub args: String,
287}
288
289pub trait CustomEffect: Serialize + serde::de::DeserializeOwned + WasmCompatSend + 'static {
294 const KIND: &'static str;
297 type Answer: Serialize + serde::de::DeserializeOwned + WasmCompatSend + 'static;
299}
300
301mod key;
302
303pub use key::Key;
304
305pub fn model_key(label: &str) -> HandlerKey {
307 HandlerKey::from(format!("model:{label}"))
308}
309
310pub fn tool_key(name: &str) -> HandlerKey {
312 HandlerKey::from(format!("tool:{name}"))
313}
314
315pub fn embed_key(label: &str) -> HandlerKey {
317 HandlerKey::from(format!("embed:{label}"))
318}
319
320pub fn rerank_key(label: &str) -> HandlerKey {
322 HandlerKey::from(format!("rerank:{label}"))
323}
324
325pub fn retrieve_key(label: &str) -> HandlerKey {
328 HandlerKey::from(format!("retrieve:{label}"))
329}
330
331pub mod family {
338 use std::marker::PhantomData;
339
340 use super::{
341 Answered, CustomEffect, EffectFamily, EffectKind, EmbedInputs, EmbedOutputs, Family,
342 RetrieveQuery, RetrievedDocuments, ToolCallRequest, sealed::Sealed,
343 };
344 use crate::{
345 completion::{CompletionRequest, CompletionResponse},
346 error::{ErrorKind, ErrorReport},
347 operation::RerankRequest,
348 rerank::RerankResponse,
349 tool::ToolResult,
350 };
351
352 macro_rules! marker {
353 ($($(#[$doc:meta])* $name:ident => $family:ident, $request:ty, $answer:ty, $wrap:expr, $unwrap:expr;)+) => {$(
354 $(#[$doc])*
355 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
356 pub struct $name;
357
358 impl Sealed for $name {}
359
360 impl Family for $name {
361 const FAMILY: EffectFamily = EffectFamily::$family;
362 type Request = $request;
363 type Answer = $answer;
364
365 fn wrap(request: Self::Request) -> Result<EffectKind, ErrorReport> {
366 let wrap: fn(Self::Request) -> EffectKind = $wrap;
367 Ok(wrap(request))
368 }
369
370 fn unwrap(outcome: Answered) -> Result<Self::Answer, ErrorReport> {
371 let unwrap: fn(Answered) -> Result<Self::Answer, ErrorReport> = $unwrap;
372 unwrap(outcome)
373 }
374 }
375 )+};
376 }
377
378 marker! {
379 Completion => Completion, CompletionRequest, CompletionResponse,
382 |request| EffectKind::Completion { request: std::sync::Arc::new(request), stream: false },
383 |outcome| match outcome {
384 Answered::Completion(response) => Ok(std::sync::Arc::unwrap_or_clone(response)),
385 other => Err(Completion::mismatch(&other)),
386 };
387 Tool => Tool, ToolCallRequest, ToolResult,
389 |request| EffectKind::ToolCall { name: request.name, args: request.args },
390 |outcome| match outcome {
391 Answered::ToolResult { result } => Ok(result),
392 other => Err(Tool::mismatch(&other)),
393 };
394 Embed => Embed, EmbedInputs, EmbedOutputs,
396 |inputs| EffectKind::Embed { inputs },
397 |outcome| match outcome {
398 Answered::Embeddings(outputs) => Ok(outputs),
399 other => Err(Embed::mismatch(&other)),
400 };
401 Rerank => Rerank, RerankRequest, RerankResponse,
403 |request| EffectKind::Rerank { request },
404 |outcome| match outcome {
405 Answered::Reranked(response) => Ok(response),
406 other => Err(Rerank::mismatch(&other)),
407 };
408 Retrieve => Retrieve, RetrieveQuery, RetrievedDocuments,
410 |query| EffectKind::Retrieve { query },
411 |outcome| match outcome {
412 Answered::Documents(documents) => Ok(documents),
413 other => Err(Retrieve::mismatch(&other)),
414 };
415 }
416
417 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
420 pub struct Dynamic;
421
422 impl Sealed for Dynamic {}
423
424 impl super::Served for Dynamic {
425 const SERVED: Option<EffectFamily> = None;
426 }
427
428 pub struct Custom<E: CustomEffect>(PhantomData<fn() -> E>);
431
432 impl<E: CustomEffect> Custom<E> {
433 pub const fn new() -> Self {
435 Self(PhantomData)
436 }
437 }
438
439 impl<E: CustomEffect> Clone for Custom<E> {
442 fn clone(&self) -> Self {
443 *self
444 }
445 }
446 impl<E: CustomEffect> Copy for Custom<E> {}
447 impl<E: CustomEffect> Default for Custom<E> {
448 fn default() -> Self {
449 Self::new()
450 }
451 }
452 impl<E: CustomEffect> std::fmt::Debug for Custom<E> {
453 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
454 write!(f, "Custom<{}>", E::KIND)
455 }
456 }
457 impl<E: CustomEffect> PartialEq for Custom<E> {
458 fn eq(&self, _: &Self) -> bool {
459 true
460 }
461 }
462 impl<E: CustomEffect> Eq for Custom<E> {}
463 impl<E: CustomEffect> std::hash::Hash for Custom<E> {
464 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
465 E::KIND.hash(state);
466 }
467 }
468
469 impl<E: CustomEffect> Sealed for Custom<E> {}
470
471 impl<E: CustomEffect> Family for Custom<E> {
472 const FAMILY: EffectFamily = EffectFamily::Custom;
473 type Request = E;
474 type Answer = E::Answer;
475
476 fn wrap(request: E) -> Result<EffectKind, ErrorReport> {
477 serde_json::to_value(&request)
480 .map(|payload| EffectKind::Custom {
481 kind: std::sync::Arc::from(E::KIND),
482 payload,
483 })
484 .map_err(|error| {
485 ErrorReport::new(
486 ErrorKind::Request,
487 format!("the `{}` effect did not serialize: {error}", E::KIND),
488 )
489 })
490 }
491
492 fn unwrap(outcome: Answered) -> Result<E::Answer, ErrorReport> {
493 match outcome {
494 Answered::Custom { payload: value } => {
495 serde_json::from_value(value).map_err(|error| {
496 ErrorReport::new(
497 ErrorKind::Internal,
498 format!(
499 "the answer to the `{}` effect did not deserialize: {error}",
500 E::KIND
501 ),
502 )
503 })
504 }
505 other => Err(Self::mismatch(&other)),
506 }
507 }
508 }
509}
510
511#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
513pub struct HandlerDescriptor {
514 pub key: HandlerKey,
516 pub family: FamilyDescriptor,
518 #[serde(default, skip_serializing_if = "Vec::is_empty")]
521 pub layers: Vec<String>,
522}
523
524impl HandlerDescriptor {
525 pub fn tool(name: &str, description: impl Into<String>, parameters: serde_json::Value) -> Self {
528 Self {
529 key: tool_key(name),
530 family: FamilyDescriptor::Tool {
531 name: name.to_owned(),
532 description: description.into(),
533 parameters,
534 embedding: None,
535 },
536 layers: Vec::new(),
537 }
538 }
539}
540
541#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
545#[serde(tag = "family", rename_all = "snake_case")]
546pub enum FamilyDescriptor {
547 Completion {
549 model: ModelRef,
551 capabilities: ProviderCapabilities,
553 },
554 Tool {
556 name: String,
558 description: String,
560 parameters: serde_json::Value,
562 #[serde(default, skip_serializing_if = "Option::is_none")]
564 embedding: Option<ToolEmbeddingDescriptor>,
565 },
566 Embed {
568 model: String,
570 #[serde(default, skip_serializing_if = "Option::is_none")]
572 dims: Option<usize>,
573 max_documents: usize,
575 modality: EmbedModality,
577 },
578 Rerank {
580 model: String,
582 max_documents: usize,
584 },
585 Retrieve {},
587 Custom {
589 kind: String,
591 },
592}
593
594impl FamilyDescriptor {
595 pub const fn family(&self) -> EffectFamily {
597 match self {
598 Self::Completion { .. } => EffectFamily::Completion,
599 Self::Tool { .. } => EffectFamily::Tool,
600 Self::Embed { .. } => EffectFamily::Embed,
601 Self::Rerank { .. } => EffectFamily::Rerank,
602 Self::Retrieve {} => EffectFamily::Retrieve,
603 Self::Custom { .. } => EffectFamily::Custom,
604 }
605 }
606}
607
608#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
611pub struct ToolEmbeddingDescriptor {
612 pub context: serde_json::Value,
614 pub embedding_docs: Vec<String>,
616}
617
618#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
620#[serde(rename_all = "snake_case")]
621pub enum EmbedModality {
622 Text,
624 Image,
626}
627
628#[derive(Debug, Clone, Serialize, Deserialize)]
630#[serde(tag = "effect", rename_all = "snake_case")]
631pub enum EffectKind {
632 Completion {
634 request: Arc<CompletionRequest>,
636 stream: bool,
638 },
639 ToolCall {
642 name: String,
644 args: String,
646 },
647 Embed {
649 inputs: EmbedInputs,
651 },
652 Rerank {
654 request: RerankRequest,
656 },
657 Retrieve {
659 query: RetrieveQuery,
661 },
662 Custom {
664 kind: Arc<str>,
666 payload: serde_json::Value,
668 },
669}
670
671impl From<&crate::message::ToolCall> for EffectKind {
684 fn from(call: &crate::message::ToolCall) -> Self {
685 Self::ToolCall {
686 name: call.function.name.as_str().to_owned(),
687 args: call.function.raw_arguments(),
688 }
689 }
690}
691
692impl EffectKind {
693 pub const fn family(&self) -> EffectFamily {
695 match self {
696 Self::Completion { .. } => EffectFamily::Completion,
697 Self::ToolCall { .. } => EffectFamily::Tool,
698 Self::Embed { .. } => EffectFamily::Embed,
699 Self::Rerank { .. } => EffectFamily::Rerank,
700 Self::Retrieve { .. } => EffectFamily::Retrieve,
701 Self::Custom { .. } => EffectFamily::Custom,
702 }
703 }
704
705 pub fn name(&self) -> &str {
708 match self {
709 Self::Custom { kind, .. } => kind,
710 Self::Completion { .. }
711 | Self::ToolCall { .. }
712 | Self::Embed { .. }
713 | Self::Rerank { .. }
714 | Self::Retrieve { .. } => self.family().name(),
715 }
716 }
717
718 pub const fn streams(&self) -> bool {
721 match self {
722 Self::Completion { stream, .. } => *stream,
723 Self::ToolCall { .. }
724 | Self::Embed { .. }
725 | Self::Rerank { .. }
726 | Self::Retrieve { .. }
727 | Self::Custom { .. } => false,
728 }
729 }
730}
731
732#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
735#[serde(tag = "modality", content = "inputs", rename_all = "snake_case")]
736pub enum EmbedInputs {
737 Texts(Vec<String>),
739 Images(Vec<Vec<u8>>),
741}
742
743#[derive(Debug, Clone, Serialize, Deserialize)]
748#[serde(tag = "query", rename_all = "snake_case")]
749pub enum RetrieveQuery {
750 TopN {
752 req: VectorSearchRequest<Filter<serde_json::Value>>,
754 },
755 TopNIds {
757 req: VectorSearchRequest<Filter<serde_json::Value>>,
759 },
760}
761
762#[derive(Debug, Clone, Serialize, Deserialize)]
764#[serde(tag = "outcome", rename_all = "snake_case")]
765pub enum Answered {
766 Completion(Arc<CompletionResponse>),
768 ToolResult {
772 result: ToolResult,
774 },
775 Embeddings(EmbedOutputs),
777 Reranked(RerankResponse),
779 Documents(RetrievedDocuments),
781 Custom {
783 payload: serde_json::Value,
785 },
786}
787
788impl Answered {
789 pub const fn family(&self) -> EffectFamily {
791 match self {
792 Self::Completion(_) => EffectFamily::Completion,
793 Self::ToolResult { .. } => EffectFamily::Tool,
794 Self::Embeddings(_) => EffectFamily::Embed,
795 Self::Reranked(_) => EffectFamily::Rerank,
796 Self::Documents(_) => EffectFamily::Retrieve,
797 Self::Custom { .. } => EffectFamily::Custom,
798 }
799 }
800}
801
802#[derive(Debug, Clone, Serialize, Deserialize)]
804#[serde(tag = "modality", content = "response", rename_all = "snake_case")]
805pub enum EmbedOutputs {
806 Texts(EmbeddingResponse),
808 Images(EmbeddingResponse),
810}
811
812#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
814#[serde(tag = "retrieved", content = "results", rename_all = "snake_case")]
815pub enum RetrievedDocuments {
816 Scored(Vec<(f64, String, serde_json::Value)>),
818 Ids(Vec<(f64, String)>),
820}
821
822#[derive(Debug, Clone, Serialize, Deserialize)]
824pub struct EffectRecord {
825 #[serde(rename = "tool_output", deserialize_with = "Option::deserialize")]
832 pub published: Option<crate::tool::ToolResultContext>,
833 pub id: EffectId,
835 pub key: HandlerKey,
837 pub kind: EffectKind,
839 pub outcome: Result<Answered, ErrorReport>,
841 #[serde(default, skip_serializing_if = "Option::is_none")]
847 pub events: Option<StreamRecord>,
848 #[serde(default, skip_serializing_if = "Option::is_none")]
852 pub stream_origin: Option<crate::message::Origin>,
853 #[serde(default, skip_serializing_if = "Option::is_none")]
855 pub parent: Option<EffectId>,
856 #[serde(default, skip_serializing_if = "Option::is_none")]
859 pub scope: Option<std::sync::Arc<str>>,
860 #[serde(default, skip_serializing_if = "Option::is_none")]
864 pub purpose: Option<std::borrow::Cow<'static, str>>,
865 #[serde(default, skip_serializing_if = "Option::is_none")]
869 pub turn: Option<u64>,
870 #[serde(default, skip_serializing_if = "Option::is_none")]
874 pub started_ms: Option<u64>,
875 #[serde(default, skip_serializing_if = "Option::is_none")]
878 pub finished_ms: Option<u64>,
879 #[serde(default, skip_serializing_if = "Option::is_none")]
884 pub reviewed: Option<Result<Answered, ErrorReport>>,
885 #[serde(default, skip_serializing_if = "Disposition::is_ran")]
887 pub disposition: Disposition,
888 #[serde(default, skip_serializing_if = "Option::is_none")]
892 pub decided_by: Option<Decider>,
893}
894
895#[non_exhaustive]
897#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
898#[serde(rename_all = "snake_case")]
899pub enum Decider {
900 Person,
902 Plugin(Cow<'static, str>),
904}
905
906impl Decider {
907 pub fn plugin(name: impl Into<Cow<'static, str>>) -> Self {
909 Self::Plugin(name.into())
910 }
911}
912
913#[non_exhaustive]
917#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
918#[serde(rename_all = "snake_case")]
919pub enum Disposition {
920 #[default]
922 Ran,
923 AnsweredBeforeStart,
926 AnsweredWhileRunning,
929 Superseded,
931 Cancelled,
933}
934
935impl EffectRecord {
936 pub fn new(
942 id: EffectId,
943 key: HandlerKey,
944 kind: EffectKind,
945 outcome: Result<Answered, ErrorReport>,
946 ) -> Self {
947 Self {
948 published: None,
949 id,
950 key,
951 kind,
952 outcome,
953 events: None,
954 stream_origin: None,
955 parent: None,
956 scope: None,
957 purpose: None,
958 turn: None,
959 started_ms: None,
960 finished_ms: None,
961 reviewed: None,
962 disposition: Disposition::Ran,
963 decided_by: None,
964 }
965 }
966}
967
968impl Disposition {
969 fn is_ran(&self) -> bool {
971 *self == Self::Ran
972 }
973}
974
975#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
978#[serde(transparent)]
979pub struct EffectRow(std::collections::BTreeMap<HandlerKey, EffectFamily>);
980
981#[derive(Debug, Clone, PartialEq, Eq)]
984pub struct RowGap {
985 pub key: HandlerKey,
987 pub needed: EffectFamily,
989 pub served: Option<EffectFamily>,
991}
992
993impl std::fmt::Display for RowGap {
994 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
995 match self.served {
996 Some(served) => write!(
997 f,
998 "`{}` is needed as {} but served as {served}",
999 self.key, self.needed
1000 ),
1001 None => write!(f, "`{}` ({}) is not served", self.key, self.needed),
1002 }
1003 }
1004}
1005
1006#[derive(Debug, Clone, PartialEq, Eq)]
1008pub enum RowDiff {
1009 Missing {
1011 key: HandlerKey,
1013 family: EffectFamily,
1015 },
1016 Extra {
1018 key: HandlerKey,
1020 family: EffectFamily,
1022 },
1023 Family {
1025 key: HandlerKey,
1027 this: EffectFamily,
1029 other: EffectFamily,
1030 },
1031}
1032
1033impl std::fmt::Display for RowDiff {
1034 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1035 match self {
1036 Self::Missing { key, family } => write!(f, "`{key}` ({family}) is missing"),
1037 Self::Extra { key, family } => write!(f, "`{key}` ({family}) is extra"),
1038 Self::Family { key, this, other } => {
1039 write!(f, "`{key}` is {this} here and {other} there")
1040 }
1041 }
1042 }
1043}
1044
1045impl EffectRow {
1046 pub fn new() -> Self {
1048 Self::default()
1049 }
1050
1051 pub fn insert(&mut self, key: HandlerKey, family: EffectFamily) -> Option<EffectFamily> {
1053 self.0.insert(key, family)
1054 }
1055
1056 pub fn insert_if_absent(&mut self, key: HandlerKey, family: EffectFamily) {
1058 self.0.entry(key).or_insert(family);
1059 }
1060
1061 pub fn remove(&mut self, key: &HandlerKey) -> Option<EffectFamily> {
1063 self.0.remove(key)
1064 }
1065
1066 pub fn get(&self, key: &HandlerKey) -> Option<&EffectFamily> {
1068 self.0.get(key)
1069 }
1070
1071 pub fn contains_key(&self, key: &HandlerKey) -> bool {
1073 self.0.contains_key(key)
1074 }
1075
1076 pub fn keys(&self) -> impl Iterator<Item = &HandlerKey> {
1078 self.0.keys()
1079 }
1080
1081 pub fn iter(&self) -> impl Iterator<Item = (&HandlerKey, &EffectFamily)> {
1083 self.0.iter()
1084 }
1085
1086 pub fn len(&self) -> usize {
1088 self.0.len()
1089 }
1090
1091 pub fn is_empty(&self) -> bool {
1093 self.0.is_empty()
1094 }
1095
1096 pub fn is_subset_of(&self, handlers: &[HandlerDescriptor]) -> Result<(), RowGap> {
1099 for (key, needed) in &self.0 {
1100 let served = handlers
1101 .iter()
1102 .find(|descriptor| &descriptor.key == key)
1103 .map(|descriptor| descriptor.family.family());
1104 match served {
1105 Some(served) if served == *needed => {}
1106 served => {
1107 return Err(RowGap {
1108 key: key.clone(),
1109 needed: *needed,
1110 served,
1111 });
1112 }
1113 }
1114 }
1115 Ok(())
1116 }
1117
1118 pub fn diff(&self, other: &EffectRow) -> Vec<RowDiff> {
1121 let mut diffs = Vec::new();
1122 for (key, family) in &self.0 {
1123 match other.0.get(key) {
1124 None => diffs.push(RowDiff::Missing {
1125 key: key.clone(),
1126 family: *family,
1127 }),
1128 Some(theirs) if theirs != family => diffs.push(RowDiff::Family {
1129 key: key.clone(),
1130 this: *family,
1131 other: *theirs,
1132 }),
1133 Some(_) => {}
1134 }
1135 }
1136 for (key, family) in &other.0 {
1137 if !self.0.contains_key(key) {
1138 diffs.push(RowDiff::Extra {
1139 key: key.clone(),
1140 family: *family,
1141 });
1142 }
1143 }
1144 diffs
1145 }
1146}
1147
1148impl FromIterator<(HandlerKey, EffectFamily)> for EffectRow {
1149 fn from_iter<I: IntoIterator<Item = (HandlerKey, EffectFamily)>>(iter: I) -> Self {
1150 Self(iter.into_iter().collect())
1151 }
1152}
1153
1154impl<'a> IntoIterator for &'a EffectRow {
1155 type Item = (&'a HandlerKey, &'a EffectFamily);
1156 type IntoIter = std::collections::btree_map::Iter<'a, HandlerKey, EffectFamily>;
1157
1158 fn into_iter(self) -> Self::IntoIter {
1159 self.0.iter()
1160 }
1161}
1162
1163impl IntoIterator for EffectRow {
1164 type Item = (HandlerKey, EffectFamily);
1165 type IntoIter = std::collections::btree_map::IntoIter<HandlerKey, EffectFamily>;
1166
1167 fn into_iter(self) -> Self::IntoIter {
1168 self.0.into_iter()
1169 }
1170}
1171
1172const _: fn() = || {
1174 fn assert_wire<T: Clone + Send + Sync + 'static + Serialize + serde::de::DeserializeOwned>() {}
1175 assert_wire::<EffectId>();
1176 assert_wire::<HandlerKey>();
1177 assert_wire::<EffectFamily>();
1178 assert_wire::<HandlerDescriptor>();
1179 assert_wire::<FamilyDescriptor>();
1180 assert_wire::<ToolEmbeddingDescriptor>();
1181 assert_wire::<EmbedModality>();
1182 assert_wire::<EffectKind>();
1183 assert_wire::<EmbedInputs>();
1184 assert_wire::<RerankRequest>();
1185 assert_wire::<RetrieveQuery>();
1186 assert_wire::<Answered>();
1187 assert_wire::<EmbedOutputs>();
1188 assert_wire::<RetrievedDocuments>();
1189 assert_wire::<EffectRecord>();
1190 assert_wire::<Decider>();
1191 assert_wire::<EffectRow>();
1192 assert_wire::<StreamRecord>();
1193};
1194
1195#[cfg(test)]
1196mod tests;