Skip to main content

objectiveai_sdk/
weights.rs

1//! Weights for swarm agent influence.
2//!
3//! Weights specify how much influence each agent in a Swarm has when
4//! combining votes into final scores. Can either be a simple vector
5//! of decimal weights or a vector of entries that also include an optional
6//! `invert` flag, which inverts that agent's vote distribution before it is
7//! combined.
8
9use rust_decimal::Decimal;
10use schemars::JsonSchema;
11use serde::{Deserialize, Serialize};
12
13/// An entry in weights with an explicit weight and optional invert flag.
14#[derive(
15    Debug,
16    Clone,
17    PartialEq,
18    Serialize,
19    Deserialize,
20    JsonSchema,
21    arbitrary::Arbitrary,
22)]
23#[schemars(rename = "WeightsEntry")]
24pub struct WeightsEntry {
25    /// The weight for this agent in the swarm. Must be in [0, 1].
26    #[serde(deserialize_with = "crate::serde_util::decimal")]
27    #[schemars(with = "f64")]
28    #[arbitrary(with = crate::arbitrary_util::arbitrary_rust_decimal)]
29    pub weight: Decimal,
30    /// If true, invert this agent's vote distribution before combining.
31    ///
32    /// When omitted or false, the vote distribution is used as-is.
33    #[serde(skip_serializing_if = "Option::is_none")]
34    #[schemars(extend("omitempty" = true))]
35    pub invert: Option<bool>,
36}
37
38/// Weights for a swarm's agents.
39///
40/// - `Weights(Vec<Decimal>)` - simple representation (no inversion)
41/// - `Entries(Vec<WeightsEntry>)` - weights with optional per-agent `invert`
42#[derive(
43    Debug,
44    Clone,
45    PartialEq,
46    Serialize,
47    Deserialize,
48    JsonSchema,
49    arbitrary::Arbitrary,
50)]
51#[serde(untagged)]
52#[schemars(rename = "Weights")]
53pub enum Weights {
54    /// Simple vector of decimal weights.
55    #[schemars(title = "Weights")]
56    Weights(
57        #[serde(deserialize_with = "crate::serde_util::vec_decimal")]
58        #[schemars(with = "Vec<f64>")]
59        #[arbitrary(with = crate::arbitrary_util::arbitrary_vec_rust_decimal)]
60        Vec<Decimal>,
61    ),
62    /// Vector of entries with optional invert flags.
63    #[schemars(title = "Entries")]
64    Entries(Vec<WeightsEntry>),
65}
66
67impl Weights {
68    /// Returns the length of the underlying weights vector.
69    pub fn len(&self) -> usize {
70        match self {
71            Weights::Weights(weights) => weights.len(),
72            Weights::Entries(entries) => entries.len(),
73        }
74    }
75
76    /// Returns true if the weights contain no entries.
77    pub fn is_empty(&self) -> bool {
78        self.len() == 0
79    }
80
81    /// Normalizes into `(weight, invert)` pairs.
82    ///
83    /// For the `Weights` variant, all `invert` flags are `false`.
84    pub fn to_weights_and_invert(&self) -> Vec<(Decimal, bool)> {
85        match self {
86            Weights::Weights(weights) => {
87                weights.iter().map(|w| (*w, false)).collect()
88            }
89            Weights::Entries(entries) => entries
90                .iter()
91                .map(|entry| (entry.weight, entry.invert.unwrap_or(false)))
92                .collect(),
93        }
94    }
95}