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 Label,
308 Distribution,
309 CalibratedProbability,
310 OrdinalDistribution,
311 Scores,
312 ScreenVerdict,
313 TextRecord,
314 TypedRecord,
315}
316
317#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
319#[serde(deny_unknown_fields)]
320pub struct SemanticDecl {
321 pub operation: Operation,
322 pub task: String,
324 pub question: String,
326 pub options: Vec<String>,
329 pub candidates: Candidates,
331 pub requires: RequiredKind,
332 pub project: Vec<String>,
334 pub for_each: Vec<ForEach>,
336 pub backend: BackendReq,
337 pub calibration: CalibrationRef,
338 pub fallback: Fallback,
339}
340
341#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
342#[serde(rename_all = "snake_case", deny_unknown_fields)]
343pub enum Candidates {
344 None,
345 From(String),
346}
347
348#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
350#[serde(deny_unknown_fields)]
351pub struct ForEach {
352 pub over: String,
354 pub bind: String,
356 pub items: ItemSource,
358}
359
360#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
361#[serde(rename_all = "snake_case", deny_unknown_fields)]
362pub enum ItemSource {
363 None,
364 List {
366 list: String,
367 id_field: String,
368 },
369}
370
371#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
373#[serde(deny_unknown_fields)]
374pub struct BackendReq {
375 pub min_determinism: Determinism,
376 pub artifact: ArtifactPin,
377 pub binding: BindingChoice,
378}
379
380#[derive(
382 Debug,
383 Clone,
384 Copy,
385 PartialEq,
386 Eq,
387 PartialOrd,
388 Ord,
389 Hash,
390 Serialize,
391 Deserialize,
392 schemars::JsonSchema,
393)]
394#[serde(rename_all = "snake_case")]
395pub enum Determinism {
396 Unspecified,
397 Tolerance,
398 Bitwise,
399}
400
401#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
402#[serde(rename_all = "snake_case", deny_unknown_fields)]
403pub enum ArtifactPin {
404 Any,
405 Pinned(ArtifactId),
406}
407
408#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
410#[serde(rename_all = "snake_case", deny_unknown_fields)]
411pub enum BindingChoice {
412 Auto,
413 Backend(String),
414}
415
416#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
417#[serde(rename_all = "snake_case", deny_unknown_fields)]
418pub enum CalibrationRef {
419 None,
420 Id(CalibrationId),
421}
422
423#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
425#[serde(rename_all = "snake_case", deny_unknown_fields)]
426pub enum Fallback {
427 None,
428 Unsupported,
430 Kind {
432 requires: RequiredKind,
433 reason: String,
434 },
435}
436
437#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
439#[serde(deny_unknown_fields)]
440pub struct LimitsDecl {
441 #[serde(
443 default,
444 skip_serializing_if = "Option::is_none",
445 deserialize_with = "crate::screening::present"
446 )]
447 #[schemars(with = "crate::acquisition_step::AcquisitionBudget")]
448 pub acquisition: Option<crate::acquisition_step::AcquisitionBudget>,
449 pub max_semantic_requests: u64,
450 pub on_excess: ExcessPolicy,
451 pub max_projection_bytes: u64,
452 pub distribution_tolerance: Decimal,
454 pub deadline_ms: u64,
456}
457
458#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
459#[serde(rename_all = "snake_case")]
460pub enum ExcessPolicy {
461 Refuse,
462 TruncateVisible,
463}
464
465#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
467#[serde(rename_all = "snake_case", deny_unknown_fields)]
468pub enum Literal {
469 Bool(bool),
470 Integer(i64),
471 Decimal(Decimal),
472 Text(String),
473 Timestamp(Timestamp),
474 DurationMs(u64),
475 Enum(String),
476}
477
478#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
483#[serde(rename_all = "snake_case", deny_unknown_fields)]
484pub enum Expr {
485 Ref(String),
486 Lit(Literal),
487 Add {
488 left: Box<Expr>,
489 right: Box<Expr>,
490 },
491 Sub {
492 left: Box<Expr>,
493 right: Box<Expr>,
494 },
495 Mul {
497 left: Box<Expr>,
498 right: Box<Expr>,
499 },
500 Div {
502 left: Box<Expr>,
503 right: Box<Expr>,
504 },
505 Round {
507 value: Box<Expr>,
508 scale: u32,
509 },
510 Fx {
512 amount: Box<Expr>,
513 from: Box<Expr>,
514 to: Box<Expr>,
515 rates: String,
516 scale: u32,
517 },
518 Len(Box<Expr>),
520 ToDecimal(Box<Expr>),
522 Graded {
525 #[serde(rename = "ref")]
526 reference: String,
527 require: GradeRequirement,
528 },
529}
530
531#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
533#[serde(deny_unknown_fields)]
534pub struct GradeRequirement {
535 pub min: Grade,
536 pub max_age_ms: AgeLimit,
537}
538
539#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
540#[serde(rename_all = "snake_case", deny_unknown_fields)]
541pub enum AgeLimit {
542 Ms(u64),
543 NotRequired,
544}
545
546#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
547#[serde(rename_all = "snake_case")]
548pub enum CmpOp {
549 Eq,
550 Ne,
551 Lt,
552 Le,
553 Gt,
554 Ge,
555}
556
557#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
559#[serde(rename_all = "snake_case", deny_unknown_fields)]
560pub enum Predicate {
561 Cmp {
562 left: Expr,
563 op: CmpOp,
564 right: Expr,
565 },
566 All(Vec<Predicate>),
567 Any(Vec<Predicate>),
568 Not(Box<Predicate>),
569 Member {
570 value: Expr,
571 set: Vec<Literal>,
572 },
573 Present(String),
575 IsConstructed {
577 #[serde(rename = "ref")]
578 reference: String,
579 },
580 MemberRef {
583 value: Expr,
584 set: String,
585 },
586}
587
588#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
590#[serde(deny_unknown_fields)]
591pub struct PolicyDecl {
592 pub on_unresolved: Vec<Handler>,
593 pub rules: Vec<Rule>,
594 pub adjustments: Vec<Adjustment>,
595}
596
597#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
599#[serde(deny_unknown_fields)]
600pub struct Handler {
601 pub target: String,
603 pub reasons: ReasonSet,
604 pub action: HandlerAction,
605}
606
607#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
608#[serde(rename_all = "snake_case", deny_unknown_fields)]
609pub enum ReasonSet {
610 Any,
611 Only(Vec<ReasonKind>),
612}
613
614#[derive(
616 Debug,
617 Clone,
618 Copy,
619 PartialEq,
620 Eq,
621 PartialOrd,
622 Ord,
623 Hash,
624 Serialize,
625 Deserialize,
626 schemars::JsonSchema,
627)]
628#[serde(rename_all = "snake_case")]
629pub enum ReasonKind {
630 MissingEvidence,
631 StaleEvidence,
632 InvalidInput,
633 Abstained,
634 BackendUnavailable,
635 InvalidBackendOutput,
636 BudgetExhausted,
637 DeadlineExceeded,
638 Conflict,
639 Unsupported,
640 InsufficientGrade,
642 ScreenUnavailable,
643 Skipped,
645}
646
647impl ReasonKind {
648 pub const ALL: [ReasonKind; 13] = [
649 ReasonKind::MissingEvidence,
650 ReasonKind::StaleEvidence,
651 ReasonKind::InvalidInput,
652 ReasonKind::Abstained,
653 ReasonKind::BackendUnavailable,
654 ReasonKind::InvalidBackendOutput,
655 ReasonKind::BudgetExhausted,
656 ReasonKind::DeadlineExceeded,
657 ReasonKind::Conflict,
658 ReasonKind::Unsupported,
659 ReasonKind::InsufficientGrade,
660 ReasonKind::ScreenUnavailable,
661 ReasonKind::Skipped,
662 ];
663}
664
665#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
666#[serde(rename_all = "snake_case", deny_unknown_fields)]
667pub enum HandlerAction {
668 Propagate,
670 Escalate {
671 reason: String,
672 },
673 AsUnmet,
676 PropagateWith {
679 remediation: Option<RemediationUse>,
680 #[serde(default, skip_serializing_if = "Address::is_computed")]
681 address: Address,
682 },
683 NeedsInput {
685 fields: Vec<String>,
686 questions: QuestionSelection,
687 },
688}
689
690#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
691#[serde(deny_unknown_fields)]
692pub struct Rule {
693 pub id: String,
694 pub when: Cond,
695 pub then: OutcomeDecl,
696}
697
698#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
700#[serde(rename_all = "snake_case", deny_unknown_fields)]
701pub enum UncalibratedThreshold {
702 NotDeclared,
703 Declared { reason: String },
704}
705
706#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
708#[serde(rename_all = "snake_case", deny_unknown_fields)]
709pub enum Cond {
710 Always,
711 TopLabel {
712 step: String,
713 label: String,
714 },
715 MassAtLeast {
716 step: String,
717 option: String,
718 threshold: Decimal,
719 uncalibrated_threshold: UncalibratedThreshold,
720 },
721 TopMassBelow {
722 step: String,
723 threshold: Decimal,
724 uncalibrated_threshold: UncalibratedThreshold,
725 },
726 ExpectationAtLeast {
727 step: String,
728 value: Decimal,
729 },
730 Exact(Predicate),
731 Empty(String),
732 Nonempty(String),
733 All(Vec<Cond>),
734 Any(Vec<Cond>),
735 Not(Box<Cond>),
736 ProvenanceWithin {
739 #[serde(rename = "ref")]
740 reference: String,
741 classes: Vec<ProvenanceClass>,
742 },
743}
744
745#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
748#[serde(untagged, deny_unknown_fields)]
749pub enum QuestionSelection {
750 Declared(Vec<String>),
751 Proposed {
752 proposed_by: String,
753 allowed: Vec<String>,
754 },
755}
756
757impl QuestionSelection {
758 pub fn allowed(&self) -> &[String] {
759 match self {
760 Self::Declared(ids) => ids,
761 Self::Proposed { allowed, .. } => allowed,
762 }
763 }
764}
765
766#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
768#[serde(rename_all = "snake_case", deny_unknown_fields)]
769pub enum OutcomeDecl {
770 Propose {
771 action: String,
772 params: Vec<ParamDecl>,
773 },
774 Escalate {
775 reason: String,
776 },
777 MissingEvidenceFrom {
779 step: String,
780 },
781 NeedsInput {
783 fields: Vec<String>,
784 questions: QuestionSelection,
785 },
786}
787
788#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
789#[serde(deny_unknown_fields)]
790pub struct ParamDecl {
791 pub name: String,
792 pub value: ParamSource,
793}
794
795#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
796#[serde(rename_all = "snake_case", deny_unknown_fields)]
797pub enum ParamSource {
798 Ref(String),
799 Lit(Literal),
800}
801
802#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
804#[serde(deny_unknown_fields)]
805pub struct Adjustment {
806 pub id: String,
807 pub when: Cond,
808 pub param: String,
809 pub scale: Vec<String>,
810}