1use serde::{Deserialize, Serialize};
14use serde_json::Value;
15
16use crate::actionability::{Actionability, Address, RemediationUse};
17use crate::decimal::Decimal;
18use crate::grade::{Grade, PresentWhen};
19use crate::ids::{ArtifactId, CalibrationId, DefinitionId};
20use crate::limits::DEFINITION_V1;
21use crate::schema;
22use crate::time::Timestamp;
23
24#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
26#[serde(deny_unknown_fields)]
27pub struct Definition {
28 pub schema: String,
29 pub name: String,
30 pub version: String,
32 pub package: String,
33 pub inputs: Vec<InputDecl>,
34 #[serde(deserialize_with = "crate::generation::steps")]
35 pub steps: Vec<StepDecl>,
36 pub policy: PolicyDecl,
37 pub limits: LimitsDecl,
38 #[serde(default, skip_serializing_if = "Option::is_none")]
41 pub actionability: Option<Actionability>,
42 #[serde(
44 default,
45 skip_serializing_if = "Option::is_none",
46 deserialize_with = "crate::screening::present"
47 )]
48 #[schemars(with = "crate::screening::Screening")]
49 pub screening: Option<crate::screening::Screening>,
50}
51
52crate::document::versioned_document!(
53 Definition,
54 [schema::DEFINITION, schema::DEFINITION_V2],
55 "rustev.definition/1 or rustev.definition/2",
56 DEFINITION_V1,
57 DefinitionId
58);
59
60#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
62#[serde(deny_unknown_fields)]
63pub struct InputDecl {
64 pub name: String,
65 #[serde(rename = "type")]
66 pub ty: TypeDecl,
67 pub provenance: Vec<ProvenanceClass>,
69 pub freshness: Freshness,
70 #[serde(default, skip_serializing_if = "Option::is_none")]
73 pub evidence: Option<EvidenceDecl>,
74 #[serde(default, skip_serializing_if = "Option::is_none")]
77 pub on_disagreement: Option<Disagreement>,
78}
79
80#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
82#[serde(rename_all = "snake_case", deny_unknown_fields)]
83pub enum EvidenceDecl {
84 Graded {
85 sources: Vec<String>,
87 caps: Vec<Cap>,
89 },
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
93#[serde(deny_unknown_fields)]
94pub struct Cap {
95 pub path: String,
96 pub max_grade: Grade,
97}
98
99#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
102#[serde(rename_all = "snake_case", deny_unknown_fields)]
103pub enum Disagreement {
104 RequireAgreement,
105 Prefer { sources: Vec<String> },
106 TreatAsHint { sources: Vec<String> },
107 Abstain,
108}
109
110#[derive(
112 Debug,
113 Clone,
114 Copy,
115 PartialEq,
116 Eq,
117 PartialOrd,
118 Ord,
119 Hash,
120 Serialize,
121 Deserialize,
122 schemars::JsonSchema,
123)]
124#[serde(rename_all = "kebab-case")]
125pub enum ProvenanceClass {
126 AuthenticatedAppField,
127 SystemOfRecord,
128 UserSupplied,
129 ThirdParty,
130 AttributedClaim,
131 ModelDerived,
132}
133
134#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
136#[serde(rename_all = "snake_case", deny_unknown_fields)]
137pub enum Freshness {
138 MaxAgeMs(u64),
139 NotRequired,
140}
141
142#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
144#[serde(rename_all = "snake_case", deny_unknown_fields)]
145pub enum TypeDecl {
146 Bool,
147 Integer,
148 Decimal,
149 Text {
150 max_bytes: u64,
151 },
152 Timestamp,
153 Enum {
154 options: Vec<String>,
155 },
156 List {
157 item: Box<TypeDecl>,
158 max_items: u64,
159 },
160 Record {
161 fields: Vec<FieldDecl>,
162 #[serde(default, skip_serializing_if = "Option::is_none")]
164 present_when: Option<PresentWhen>,
165 },
166 Nullable {
169 #[serde(rename = "type")]
170 ty: Box<TypeDecl>,
171 },
172 Map {
174 key_max_bytes: u64,
175 value: Box<TypeDecl>,
176 max_entries: u64,
177 },
178}
179
180#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
181#[serde(deny_unknown_fields)]
182pub struct FieldDecl {
183 pub name: String,
184 #[serde(rename = "type")]
185 pub ty: TypeDecl,
186}
187
188#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
190#[serde(deny_unknown_fields)]
191pub struct StepDecl {
192 pub id: String,
193 pub body: StepBody,
194}
195
196#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
197#[serde(rename_all = "snake_case", deny_unknown_fields)]
198pub enum StepBody {
199 Acquire(crate::acquisition_step::Acquire),
200 Widen(crate::acquisition_step::Widen),
201 Exact(ExactDecl),
202 Semantic(SemanticDecl),
203 Corroborate(CorroborateDecl),
205 Generate(Box<crate::generation::Generate>),
206 Extract(Box<crate::extraction::Extract>),
207 AcceptModelDerived(Box<crate::extraction::AcceptModelDerived>),
208 Rank(Box<crate::ranking::Rank>),
209 Select(Box<crate::ranking::Select>),
210}
211
212#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
214#[serde(deny_unknown_fields)]
215pub struct CorroborateDecl {
216 pub claim: String,
218 pub upgrade_to: Grade,
220 pub by: Vec<Check>,
222 pub mode: CorroborationMode,
223 pub on_contradiction: OnContradiction,
224}
225
226#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
227#[serde(deny_unknown_fields)]
228pub struct Check {
229 pub predicate: Predicate,
230 pub on_false: OnFalse,
231}
232
233#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
234#[serde(rename_all = "snake_case")]
235pub enum OnFalse {
236 Contradicts,
237 NoEvidence,
238}
239
240#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
241#[serde(rename_all = "snake_case")]
242pub enum CorroborationMode {
243 All,
244 Any,
245}
246
247#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
248#[serde(rename_all = "snake_case")]
249pub enum OnContradiction {
250 Conflict,
251 TreatAsHint,
252 Abstain,
253}
254
255#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
260#[serde(deny_unknown_fields)]
261pub struct ExactDecl {
262 pub op: String,
263 pub version: u32,
264 pub args: Value,
265}
266
267#[derive(
269 Debug,
270 Clone,
271 Copy,
272 PartialEq,
273 Eq,
274 PartialOrd,
275 Ord,
276 Hash,
277 Serialize,
278 Deserialize,
279 schemars::JsonSchema,
280)]
281#[serde(rename_all = "snake_case")]
282pub enum Operation {
283 Classify,
284 Proposition,
285 Rubric,
286 Rank,
287 Generate,
288 Extract,
289}
290
291#[derive(
293 Debug,
294 Clone,
295 Copy,
296 PartialEq,
297 Eq,
298 PartialOrd,
299 Ord,
300 Hash,
301 Serialize,
302 Deserialize,
303 schemars::JsonSchema,
304)]
305#[serde(rename_all = "snake_case")]
306pub enum RequiredKind {
307 Ranking,
308 Label,
309 Distribution,
310 CalibratedProbability,
311 OrdinalDistribution,
312 Scores,
313 ScreenVerdict,
314 TextRecord,
315 TypedRecord,
316}
317
318#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
320#[serde(deny_unknown_fields)]
321pub struct SemanticDecl {
322 pub operation: Operation,
323 pub task: String,
325 pub question: String,
327 pub options: Vec<String>,
330 pub candidates: Candidates,
332 pub requires: RequiredKind,
333 pub project: Vec<String>,
335 pub for_each: Vec<ForEach>,
337 pub backend: BackendReq,
338 pub calibration: CalibrationRef,
339 pub fallback: Fallback,
340}
341
342#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
343#[serde(rename_all = "snake_case", deny_unknown_fields)]
344pub enum Candidates {
345 None,
346 From(String),
347}
348
349#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
351#[serde(deny_unknown_fields)]
352pub struct ForEach {
353 pub over: String,
355 pub bind: String,
357 pub items: ItemSource,
359}
360
361#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
362#[serde(rename_all = "snake_case", deny_unknown_fields)]
363pub enum ItemSource {
364 None,
365 List {
367 list: String,
368 id_field: String,
369 },
370}
371
372#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
374#[serde(deny_unknown_fields)]
375pub struct BackendReq {
376 pub min_determinism: Determinism,
377 pub artifact: ArtifactPin,
378 pub binding: BindingChoice,
379}
380
381#[derive(
383 Debug,
384 Clone,
385 Copy,
386 PartialEq,
387 Eq,
388 PartialOrd,
389 Ord,
390 Hash,
391 Serialize,
392 Deserialize,
393 schemars::JsonSchema,
394)]
395#[serde(rename_all = "snake_case")]
396pub enum Determinism {
397 Unspecified,
398 Tolerance,
399 Bitwise,
400}
401
402#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
403#[serde(rename_all = "snake_case", deny_unknown_fields)]
404pub enum ArtifactPin {
405 Any,
406 Pinned(ArtifactId),
407}
408
409#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
411#[serde(rename_all = "snake_case", deny_unknown_fields)]
412pub enum BindingChoice {
413 Auto,
414 Backend(String),
415}
416
417#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
418#[serde(rename_all = "snake_case", deny_unknown_fields)]
419pub enum CalibrationRef {
420 None,
421 Id(CalibrationId),
422}
423
424#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
426#[serde(rename_all = "snake_case", deny_unknown_fields)]
427pub enum Fallback {
428 None,
429 Unsupported,
431 Kind {
433 requires: RequiredKind,
434 reason: String,
435 },
436}
437
438#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
440#[serde(deny_unknown_fields)]
441pub struct LimitsDecl {
442 #[serde(
444 default,
445 skip_serializing_if = "Option::is_none",
446 deserialize_with = "crate::screening::present"
447 )]
448 #[schemars(with = "crate::acquisition_step::AcquisitionBudget")]
449 pub acquisition: Option<crate::acquisition_step::AcquisitionBudget>,
450 pub max_semantic_requests: u64,
451 pub on_excess: ExcessPolicy,
452 pub max_projection_bytes: u64,
453 pub distribution_tolerance: Decimal,
455 pub deadline_ms: u64,
457}
458
459#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
460#[serde(rename_all = "snake_case")]
461pub enum ExcessPolicy {
462 Refuse,
463 TruncateVisible,
464}
465
466#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
468#[serde(rename_all = "snake_case", deny_unknown_fields)]
469pub enum Literal {
470 Bool(bool),
471 Integer(i64),
472 Decimal(Decimal),
473 Text(String),
474 Timestamp(Timestamp),
475 DurationMs(u64),
476 Enum(String),
477}
478
479#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
484#[serde(rename_all = "snake_case", deny_unknown_fields)]
485pub enum Expr {
486 Ref(String),
487 Lit(Literal),
488 Add {
489 left: Box<Expr>,
490 right: Box<Expr>,
491 },
492 Sub {
493 left: Box<Expr>,
494 right: Box<Expr>,
495 },
496 Mul {
498 left: Box<Expr>,
499 right: Box<Expr>,
500 },
501 Div {
503 left: Box<Expr>,
504 right: Box<Expr>,
505 },
506 Round {
508 value: Box<Expr>,
509 scale: u32,
510 },
511 Fx {
513 amount: Box<Expr>,
514 from: Box<Expr>,
515 to: Box<Expr>,
516 rates: String,
517 scale: u32,
518 },
519 Len(Box<Expr>),
521 ToDecimal(Box<Expr>),
523 Graded {
526 #[serde(rename = "ref")]
527 reference: String,
528 require: GradeRequirement,
529 },
530}
531
532#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
534#[serde(deny_unknown_fields)]
535pub struct GradeRequirement {
536 pub min: Grade,
537 pub max_age_ms: AgeLimit,
538}
539
540#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
541#[serde(rename_all = "snake_case", deny_unknown_fields)]
542pub enum AgeLimit {
543 Ms(u64),
544 NotRequired,
545}
546
547#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
548#[serde(rename_all = "snake_case")]
549pub enum CmpOp {
550 Eq,
551 Ne,
552 Lt,
553 Le,
554 Gt,
555 Ge,
556}
557
558#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
560#[serde(rename_all = "snake_case", deny_unknown_fields)]
561pub enum Predicate {
562 Cmp {
563 left: Expr,
564 op: CmpOp,
565 right: Expr,
566 },
567 All(Vec<Predicate>),
568 Any(Vec<Predicate>),
569 Not(Box<Predicate>),
570 Member {
571 value: Expr,
572 set: Vec<Literal>,
573 },
574 Present(String),
576 IsConstructed {
578 #[serde(rename = "ref")]
579 reference: String,
580 },
581 MemberRef {
584 value: Expr,
585 set: String,
586 },
587}
588
589#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
591#[serde(deny_unknown_fields)]
592pub struct PolicyDecl {
593 pub on_unresolved: Vec<Handler>,
594 pub rules: Vec<Rule>,
595 pub adjustments: Vec<Adjustment>,
596}
597
598#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
600#[serde(deny_unknown_fields)]
601pub struct Handler {
602 pub target: String,
604 pub reasons: ReasonSet,
605 pub action: HandlerAction,
606}
607
608#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
609#[serde(rename_all = "snake_case", deny_unknown_fields)]
610pub enum ReasonSet {
611 Any,
612 Only(Vec<ReasonKind>),
613}
614
615#[derive(
617 Debug,
618 Clone,
619 Copy,
620 PartialEq,
621 Eq,
622 PartialOrd,
623 Ord,
624 Hash,
625 Serialize,
626 Deserialize,
627 schemars::JsonSchema,
628)]
629#[serde(rename_all = "snake_case")]
630pub enum ReasonKind {
631 MissingEvidence,
632 StaleEvidence,
633 InvalidInput,
634 Abstained,
635 BackendUnavailable,
636 InvalidBackendOutput,
637 BudgetExhausted,
638 DeadlineExceeded,
639 Conflict,
640 Unsupported,
641 InsufficientGrade,
643 ScreenUnavailable,
644 Skipped,
646}
647
648impl ReasonKind {
649 pub const ALL: [ReasonKind; 13] = [
650 ReasonKind::MissingEvidence,
651 ReasonKind::StaleEvidence,
652 ReasonKind::InvalidInput,
653 ReasonKind::Abstained,
654 ReasonKind::BackendUnavailable,
655 ReasonKind::InvalidBackendOutput,
656 ReasonKind::BudgetExhausted,
657 ReasonKind::DeadlineExceeded,
658 ReasonKind::Conflict,
659 ReasonKind::Unsupported,
660 ReasonKind::InsufficientGrade,
661 ReasonKind::ScreenUnavailable,
662 ReasonKind::Skipped,
663 ];
664}
665
666#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
667#[serde(rename_all = "snake_case", deny_unknown_fields)]
668pub enum HandlerAction {
669 Propagate,
671 Escalate {
672 reason: String,
673 },
674 AsUnmet,
677 PropagateWith {
680 remediation: Option<RemediationUse>,
681 #[serde(default, skip_serializing_if = "Address::is_computed")]
682 address: Address,
683 },
684 NeedsInput {
686 fields: Vec<String>,
687 questions: QuestionSelection,
688 },
689}
690
691#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
692#[serde(deny_unknown_fields)]
693pub struct Rule {
694 pub id: String,
695 pub when: Cond,
696 pub then: OutcomeDecl,
697}
698
699#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
701#[serde(rename_all = "snake_case", deny_unknown_fields)]
702pub enum UncalibratedThreshold {
703 NotDeclared,
704 Declared { reason: String },
705}
706
707#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
709#[serde(rename_all = "snake_case", deny_unknown_fields)]
710pub enum Cond {
711 Always,
712 TopLabel {
713 step: String,
714 label: String,
715 },
716 MassAtLeast {
717 step: String,
718 option: String,
719 threshold: Decimal,
720 uncalibrated_threshold: UncalibratedThreshold,
721 },
722 TopMassBelow {
723 step: String,
724 threshold: Decimal,
725 uncalibrated_threshold: UncalibratedThreshold,
726 },
727 ExpectationAtLeast {
728 step: String,
729 value: Decimal,
730 },
731 Exact(Predicate),
732 Empty(String),
733 Nonempty(String),
734 All(Vec<Cond>),
735 Any(Vec<Cond>),
736 Not(Box<Cond>),
737 ProvenanceWithin {
740 #[serde(rename = "ref")]
741 reference: String,
742 classes: Vec<ProvenanceClass>,
743 },
744}
745
746#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
749#[serde(untagged, deny_unknown_fields)]
750pub enum QuestionSelection {
751 Declared(Vec<String>),
752 Proposed {
753 proposed_by: String,
754 allowed: Vec<String>,
755 },
756}
757
758impl QuestionSelection {
759 pub fn allowed(&self) -> &[String] {
760 match self {
761 Self::Declared(ids) => ids,
762 Self::Proposed { allowed, .. } => allowed,
763 }
764 }
765}
766
767#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
769#[serde(rename_all = "snake_case", deny_unknown_fields)]
770pub enum OutcomeDecl {
771 Propose {
772 action: String,
773 params: Vec<ParamDecl>,
774 },
775 Escalate {
776 reason: String,
777 },
778 MissingEvidenceFrom {
780 step: String,
781 },
782 NeedsInput {
784 fields: Vec<String>,
785 questions: QuestionSelection,
786 },
787}
788
789#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
790#[serde(deny_unknown_fields)]
791pub struct ParamDecl {
792 pub name: String,
793 pub value: ParamSource,
794}
795
796#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
797#[serde(rename_all = "snake_case", deny_unknown_fields)]
798pub enum ParamSource {
799 Ref(String),
800 Lit(Literal),
801}
802
803#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
805#[serde(deny_unknown_fields)]
806pub struct Adjustment {
807 pub id: String,
808 pub when: Cond,
809 pub param: String,
810 pub scale: Vec<String>,
811}