Skip to main content

objectiveai_sdk/agent/openrouter/
stop.rs

1//! Stop sequence configuration for Agents.
2
3use serde::{Deserialize, Serialize};
4use schemars::JsonSchema;
5
6/// Stop sequences that terminate model generation.
7///
8/// When the model generates any of these sequences, it immediately
9/// stops producing further tokens.
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, arbitrary::Arbitrary)]
11#[serde(untagged)]
12#[schemars(rename = "agent.openrouter.Stop")]
13pub enum Stop {
14    /// A single stop sequence.
15    #[schemars(title = "String")]
16    String(String),
17    /// Multiple stop sequences (up to 4 typically supported).
18    #[schemars(title = "Strings")]
19    Strings(Vec<String>),
20}
21
22impl Stop {
23    /// Normalizes the stop configuration for deterministic hashing.
24    ///
25    /// - Empty arrays become `None`
26    /// - Single-element arrays become `String` variant
27    /// - Multi-element arrays are sorted and deduplicated
28    pub fn prepare(self) -> Option<Self> {
29        if let Stop::Strings(mut strings) = self {
30            if strings.is_empty() {
31                None
32            } else if strings.len() == 1 {
33                Some(Stop::String(strings.into_iter().next().unwrap()))
34            } else {
35                strings.sort();
36                strings.dedup();
37                Some(Stop::Strings(strings))
38            }
39        } else {
40            Some(self)
41        }
42    }
43
44    /// Validates that stop sequences are non-empty strings.
45    pub fn validate(&self) -> Result<(), String> {
46        match self {
47            Stop::String(string) if string.is_empty() => {
48                Err("`stop` string cannot be empty".to_string())
49            }
50            Stop::Strings(strings) if strings.iter().any(|s| s.is_empty()) => {
51                Err("`stop` strings cannot be empty".to_string())
52            }
53            _ => Ok(()),
54        }
55    }
56}