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 serde::{Deserialize, Serialize};
11use schemars::JsonSchema;
12
13/// An entry in weights with an explicit weight and optional invert flag.
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, arbitrary::Arbitrary)]
15#[schemars(rename = "WeightsEntry")]
16pub struct WeightsEntry {
17    /// The weight for this agent in the swarm. Must be in [0, 1].
18    #[serde(deserialize_with = "crate::serde_util::decimal")]
19    #[schemars(with = "f64")]
20    #[arbitrary(with = crate::arbitrary_util::arbitrary_rust_decimal)]
21    pub weight: Decimal,
22    /// If true, invert this agent's vote distribution before combining.
23    ///
24    /// When omitted or false, the vote distribution is used as-is.
25    #[serde(skip_serializing_if = "Option::is_none")]
26    #[schemars(extend("omitempty" = true))]
27    pub invert: Option<bool>,
28}
29
30/// Weights for a swarm's agents.
31///
32/// - `Weights(Vec<Decimal>)` - simple representation (no inversion)
33/// - `Entries(Vec<WeightsEntry>)` - weights with optional per-agent `invert`
34#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, arbitrary::Arbitrary)]
35#[serde(untagged)]
36#[schemars(rename = "Weights")]
37pub enum Weights {
38    /// Simple vector of decimal weights.
39    #[schemars(title = "Weights")]
40    Weights(#[serde(deserialize_with = "crate::serde_util::vec_decimal")] #[schemars(with = "Vec<f64>")] #[arbitrary(with = crate::arbitrary_util::arbitrary_vec_rust_decimal)] Vec<Decimal>),
41    /// Vector of entries with optional invert flags.
42    #[schemars(title = "Entries")]
43    Entries(Vec<WeightsEntry>),
44}
45
46impl Weights {
47    /// Returns the length of the underlying weights vector.
48    pub fn len(&self) -> usize {
49        match self {
50            Weights::Weights(weights) => weights.len(),
51            Weights::Entries(entries) => entries.len(),
52        }
53    }
54
55    /// Returns true if the weights contain no entries.
56    pub fn is_empty(&self) -> bool {
57        self.len() == 0
58    }
59
60    /// Normalizes into `(weight, invert)` pairs.
61    ///
62    /// For the `Weights` variant, all `invert` flags are `false`.
63    pub fn to_weights_and_invert(&self) -> Vec<(Decimal, bool)> {
64        match self {
65            Weights::Weights(weights) => {
66                weights.iter().map(|w| (*w, false)).collect()
67            }
68            Weights::Entries(entries) => entries
69                .iter()
70                .map(|entry| (entry.weight, entry.invert.unwrap_or(false)))
71                .collect(),
72        }
73    }
74}