Skip to main content

runifold_model/
runtime_profile.rs

1use std::collections::BTreeMap;
2
3use serde_json::Value;
4
5use crate::{CircuitBreakerConfig, FeaturePolicy, ModelRetryPolicy, ResponseMode};
6
7/// A reviewed workload preset layered over a Provider's protocol-safe policy.
8pub trait RuntimeProfilePreset: Copy + Send + Sync + 'static {
9    /// Applies this workload's choices without discarding Provider-owned
10    /// request options, retry exceptions, or circuit policy.
11    fn apply(self, provider: ProviderRuntimeProfile) -> ProviderRuntimeProfile;
12}
13
14/// Long-lived service defaults prioritizing correctness and Provider policy.
15#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
16pub struct ProductionProfile;
17
18impl RuntimeProfilePreset for ProductionProfile {
19    fn apply(self, provider: ProviderRuntimeProfile) -> ProviderRuntimeProfile {
20        provider
21    }
22}
23
24/// User-facing latency defaults that commit canonical stream events promptly.
25#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
26pub struct InteractiveProfile;
27
28impl RuntimeProfilePreset for InteractiveProfile {
29    fn apply(self, provider: ProviderRuntimeProfile) -> ProviderRuntimeProfile {
30        provider.response_mode(ResponseMode::Streaming)
31    }
32}
33
34/// Offline execution defaults that validate a complete response before commit.
35#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
36pub struct BatchProfile;
37
38impl RuntimeProfilePreset for BatchProfile {
39    fn apply(self, provider: ProviderRuntimeProfile) -> ProviderRuntimeProfile {
40        provider.response_mode(ResponseMode::Complete)
41    }
42}
43
44/// Provider-recommended defaults for one fully composed model runtime.
45///
46/// The profile contains execution policy rather than transport credentials or
47/// model identity. Concrete providers may override [`Default`] when their wire
48/// protocol requires a different delivery mode, capability policy, retry
49/// authority, circuit policy, or namespaced request option.
50#[derive(Clone, Debug, Eq, PartialEq)]
51pub struct ProviderRuntimeProfile {
52    response_mode: ResponseMode,
53    feature_policy: FeaturePolicy,
54    retry_policy: ModelRetryPolicy,
55    circuit_breaker: CircuitBreakerConfig,
56    provider_options: BTreeMap<String, Value>,
57}
58
59impl Default for ProviderRuntimeProfile {
60    fn default() -> Self {
61        Self {
62            response_mode: ResponseMode::Streaming,
63            feature_policy: FeaturePolicy::Strict,
64            retry_policy: ModelRetryPolicy::default(),
65            circuit_breaker: CircuitBreakerConfig::default(),
66            provider_options: BTreeMap::new(),
67        }
68    }
69}
70
71impl ProviderRuntimeProfile {
72    /// Creates the provider-neutral fail-closed baseline.
73    pub fn conservative() -> Self {
74        Self::default()
75    }
76
77    /// Replaces response delivery behavior.
78    #[must_use]
79    pub const fn response_mode(mut self, response_mode: ResponseMode) -> Self {
80        self.response_mode = response_mode;
81        self
82    }
83
84    /// Replaces handling for unknown or emulated model capabilities.
85    #[must_use]
86    pub const fn feature_policy(mut self, feature_policy: FeaturePolicy) -> Self {
87        self.feature_policy = feature_policy;
88        self
89    }
90
91    /// Replaces bounded same-route retry policy.
92    #[must_use]
93    pub fn retry_policy(mut self, retry_policy: ModelRetryPolicy) -> Self {
94        self.retry_policy = retry_policy;
95        self
96    }
97
98    /// Replaces circuit-breaker policy.
99    #[must_use]
100    pub fn circuit_breaker(mut self, circuit_breaker: CircuitBreakerConfig) -> Self {
101        self.circuit_breaker = circuit_breaker;
102        self
103    }
104
105    /// Adds or replaces one adapter-owned namespaced request option.
106    #[must_use]
107    pub fn provider_option(mut self, provider: impl Into<String>, options: Value) -> Self {
108        self.provider_options.insert(provider.into(), options);
109        self
110    }
111
112    /// Returns response delivery behavior.
113    pub const fn selected_response_mode(&self) -> ResponseMode {
114        self.response_mode
115    }
116
117    /// Returns handling for unknown or emulated model capabilities.
118    pub const fn selected_feature_policy(&self) -> FeaturePolicy {
119        self.feature_policy
120    }
121
122    /// Returns bounded same-route retry policy.
123    pub const fn selected_retry_policy(&self) -> &ModelRetryPolicy {
124        &self.retry_policy
125    }
126
127    /// Returns circuit-breaker policy.
128    pub const fn selected_circuit_breaker(&self) -> &CircuitBreakerConfig {
129        &self.circuit_breaker
130    }
131
132    /// Returns adapter-owned namespaced request options.
133    pub const fn provider_options(&self) -> &BTreeMap<String, Value> {
134        &self.provider_options
135    }
136
137    /// Splits the profile into owned runtime components.
138    pub fn into_parts(
139        self,
140    ) -> (
141        ResponseMode,
142        FeaturePolicy,
143        ModelRetryPolicy,
144        CircuitBreakerConfig,
145        BTreeMap<String, Value>,
146    ) {
147        (
148            self.response_mode,
149            self.feature_policy,
150            self.retry_policy,
151            self.circuit_breaker,
152            self.provider_options,
153        )
154    }
155}
156
157#[cfg(test)]
158mod tests {
159    use crate::{
160        BatchProfile, FeaturePolicy, InteractiveProfile, ProductionProfile, ProviderRuntimeProfile,
161        ResponseMode, RuntimeProfilePreset,
162    };
163
164    #[test]
165    fn conservative_profile_is_fail_closed_and_provider_neutral() {
166        let profile = ProviderRuntimeProfile::conservative();
167
168        assert_eq!(profile.selected_response_mode(), ResponseMode::Streaming);
169        assert_eq!(profile.selected_feature_policy(), FeaturePolicy::Strict);
170        assert!(profile.provider_options().is_empty());
171        assert_eq!(profile.selected_retry_policy().max_attempts(), 3);
172    }
173
174    #[test]
175    fn workload_presets_preserve_provider_owned_options() {
176        let provider = ProviderRuntimeProfile::conservative()
177            .provider_option("provider", serde_json::json!({"safe": true}));
178
179        let production = ProductionProfile.apply(provider.clone());
180        let interactive = InteractiveProfile.apply(provider.clone());
181        let batch = BatchProfile.apply(provider);
182
183        assert_eq!(production.selected_response_mode(), ResponseMode::Streaming);
184        assert_eq!(
185            interactive.selected_response_mode(),
186            ResponseMode::Streaming
187        );
188        assert_eq!(batch.selected_response_mode(), ResponseMode::Complete);
189        assert_eq!(batch.provider_options()["provider"]["safe"], true);
190    }
191}