semantic_operators/
filter.rs1use crate::each::{BatchOptions, Item, ItemResult, ask_each, validate_ids};
3use crate::{Answer, AnswerValue, Boolean, Error, Result, State};
4use serde_json::{Map, Value};
5use std::collections::BTreeSet;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub enum FilterOutcome {
10 Match,
12 No,
14 Unsure,
16 Failed,
18}
19#[derive(Clone, Debug)]
21pub struct Verdict {
22 id: String,
23 position: usize,
24 outcome: FilterOutcome,
25 result: ItemResult,
26}
27impl Verdict {
28 pub fn id(&self) -> &str {
30 &self.id
31 }
32 pub fn position(&self) -> usize {
34 self.position
35 }
36 pub fn outcome(&self) -> FilterOutcome {
38 self.outcome
39 }
40 pub fn answer(&self) -> Option<&Answer<AnswerValue>> {
42 self.result.answer()
43 }
44 pub fn error(&self) -> Option<&Error> {
46 self.result.error()
47 }
48}
49#[derive(Clone, Debug)]
51pub struct Filtered {
52 question: Boolean,
53 verdicts: Vec<Verdict>,
54}
55impl Filtered {
56 pub fn question(&self) -> &Boolean {
58 &self.question
59 }
60 pub fn verdicts(&self) -> &[Verdict] {
62 &self.verdicts
63 }
64 pub fn matched(&self) -> Vec<&Verdict> {
66 self.with_outcome(FilterOutcome::Match)
67 }
68 pub fn rejected(&self) -> Vec<&Verdict> {
70 self.with_outcome(FilterOutcome::No)
71 }
72 pub fn unsure(&self) -> Vec<&Verdict> {
74 self.with_outcome(FilterOutcome::Unsure)
75 }
76 pub fn failed(&self) -> Vec<&Verdict> {
78 self.with_outcome(FilterOutcome::Failed)
79 }
80 pub fn models(&self) -> BTreeSet<&str> {
82 self.verdicts
83 .iter()
84 .filter_map(|v| v.answer())
85 .filter_map(|a| a.call())
86 .filter_map(|c| c.model.as_deref())
87 .filter(|m| !m.is_empty())
88 .collect()
89 }
90 fn with_outcome(&self, outcome: FilterOutcome) -> Vec<&Verdict> {
91 self.verdicts
92 .iter()
93 .filter(|v| v.outcome == outcome)
94 .collect()
95 }
96}
97pub async fn filter_items(
109 provider: &dyn crate::Provider,
110 question: &Boolean,
111 items: &[Item],
112 options: &BatchOptions,
113) -> Result<Filtered> {
114 validate_ids(items.iter().map(Item::id))?;
115 let mut states = Vec::with_capacity(items.len());
116 for item in items {
117 let state = if let Some(context) = options.context.as_ref().filter(|v| !v.is_null()) {
118 let mut value = Map::new();
119 value.insert("context".into(), context.clone());
120 value.insert("item".into(), item.state().as_value().clone());
121 State::try_from(Value::Object(value))?
122 } else {
123 item.state().clone()
124 };
125 states.push(state);
126 }
127 let results = ask_each(provider, &question.clone().into(), &states, options).await?;
128 let verdicts = items
129 .iter()
130 .zip(results)
131 .enumerate()
132 .map(|(position, (item, result))| {
133 let outcome = match &result {
134 ItemResult::Error(_) => FilterOutcome::Failed,
135 ItemResult::Answer(answer) => match answer.value() {
136 Some(AnswerValue::Boolean(true)) => FilterOutcome::Match,
137 Some(AnswerValue::Boolean(false)) => FilterOutcome::No,
138 _ => FilterOutcome::Unsure,
139 },
140 };
141 Verdict {
142 id: item.id().to_owned(),
143 position,
144 outcome,
145 result,
146 }
147 })
148 .collect();
149 Ok(Filtered {
150 question: question.clone(),
151 verdicts,
152 })
153}