Skip to main content

starweaver_runtime/capability/
bundle.rs

1//! Capability bundle contracts and static bundle implementation.
2
3use std::sync::Arc;
4
5use starweaver_model::{ModelRequestParameters, ModelSettings};
6use starweaver_tools::{DynTool, DynToolset, ToolRegistry};
7
8use starweaver_usage::UsageLimits;
9
10use crate::{
11    instructions::DynDynamicInstruction,
12    output::{DynOutputFunction, OutputValidator},
13};
14
15use super::{AgentCapability, CapabilitySpec};
16
17/// Composable agent extension that contributes hooks, tools, instructions, and settings.
18pub trait CapabilityBundle: Send + Sync {
19    /// Bundle name for diagnostics and registry surfaces.
20    fn name(&self) -> &str;
21
22    /// Stable capability spec for bundle-level reconstruction evidence.
23    fn spec(&self) -> CapabilitySpec {
24        CapabilitySpec::new(self.name())
25    }
26
27    /// Capability hooks contributed by this bundle.
28    fn hooks(&self) -> Vec<Arc<dyn AgentCapability>> {
29        Vec::new()
30    }
31
32    /// Stream observer hooks contributed by this bundle.
33    fn stream_observers(&self) -> Vec<Arc<dyn AgentCapability>> {
34        Vec::new()
35    }
36
37    /// Static instructions contributed by this bundle.
38    fn get_instructions(&self) -> Vec<String> {
39        Vec::new()
40    }
41
42    /// Dynamic instructions contributed by this bundle.
43    fn dynamic_instructions(&self) -> Vec<DynDynamicInstruction> {
44        Vec::new()
45    }
46
47    /// Runtime tools and tool instructions contributed by this bundle.
48    fn get_tools(&self) -> Option<ToolRegistry> {
49        None
50    }
51
52    /// Model settings overlay contributed by this bundle.
53    fn model_settings(&self) -> Option<ModelSettings> {
54        None
55    }
56
57    /// Request parameter overlay contributed by this bundle.
58    fn request_params(&self) -> Option<ModelRequestParameters> {
59        None
60    }
61
62    /// Output functions contributed by this bundle.
63    fn output_functions(&self) -> Vec<DynOutputFunction> {
64        Vec::new()
65    }
66
67    /// Output validators contributed by this bundle.
68    fn output_validators(&self) -> Vec<Arc<dyn OutputValidator>> {
69        Vec::new()
70    }
71
72    /// Usage limits contributed by this bundle.
73    fn usage_limits(&self) -> Option<UsageLimits> {
74        None
75    }
76}
77
78/// Static capability bundle for reusable runtime composition.
79#[derive(Clone, Default)]
80pub struct StaticCapabilityBundle {
81    name: String,
82    spec: Option<CapabilitySpec>,
83    hooks: Vec<Arc<dyn AgentCapability>>,
84    stream_observers: Vec<Arc<dyn AgentCapability>>,
85    instructions: Vec<String>,
86    dynamic_instructions: Vec<DynDynamicInstruction>,
87    tools: ToolRegistry,
88    model_settings: Option<ModelSettings>,
89    request_params: Option<ModelRequestParameters>,
90    output_functions: Vec<DynOutputFunction>,
91    output_validators: Vec<Arc<dyn OutputValidator>>,
92    usage_limits: Option<UsageLimits>,
93}
94
95impl StaticCapabilityBundle {
96    /// Create an empty static capability bundle.
97    #[must_use]
98    pub fn new(name: impl Into<String>) -> Self {
99        Self {
100            name: name.into(),
101            spec: None,
102            hooks: Vec::new(),
103            stream_observers: Vec::new(),
104            instructions: Vec::new(),
105            dynamic_instructions: Vec::new(),
106            tools: ToolRegistry::new(),
107            model_settings: None,
108            request_params: None,
109            output_functions: Vec::new(),
110            output_validators: Vec::new(),
111            usage_limits: None,
112        }
113    }
114
115    /// Set the bundle capability spec.
116    #[must_use]
117    pub fn with_spec(mut self, spec: CapabilitySpec) -> Self {
118        self.spec = Some(spec);
119        self
120    }
121
122    /// Add a capability hook.
123    #[must_use]
124    pub fn with_hook(mut self, hook: Arc<dyn AgentCapability>) -> Self {
125        self.hooks.push(hook);
126        self
127    }
128
129    /// Add a stream observer hook.
130    #[must_use]
131    pub fn with_stream_observer(mut self, observer: Arc<dyn AgentCapability>) -> Self {
132        self.stream_observers.push(observer);
133        self
134    }
135
136    /// Add a static instruction.
137    #[must_use]
138    pub fn with_instruction(mut self, instruction: impl Into<String>) -> Self {
139        self.instructions.push(instruction.into());
140        self
141    }
142
143    /// Add a dynamic instruction.
144    #[must_use]
145    pub fn with_dynamic_instruction(mut self, instruction: DynDynamicInstruction) -> Self {
146        self.dynamic_instructions.push(instruction);
147        self
148    }
149
150    /// Add one tool.
151    #[must_use]
152    pub fn with_tool(mut self, tool: DynTool) -> Self {
153        self.tools.insert(tool);
154        self
155    }
156
157    /// Add one toolset.
158    #[must_use]
159    pub fn with_toolset(mut self, toolset: &DynToolset) -> Self {
160        self.tools.insert_toolset(toolset);
161        self
162    }
163
164    /// Set model settings overlay.
165    #[must_use]
166    pub fn with_model_settings(mut self, settings: ModelSettings) -> Self {
167        self.model_settings = Some(settings);
168        self
169    }
170
171    /// Set request parameter overlay.
172    #[must_use]
173    pub fn with_request_params(mut self, params: ModelRequestParameters) -> Self {
174        self.request_params = Some(params);
175        self
176    }
177
178    /// Add an output function.
179    #[must_use]
180    pub fn with_output_function(mut self, function: DynOutputFunction) -> Self {
181        self.output_functions.push(function);
182        self
183    }
184
185    /// Add an output validator.
186    #[must_use]
187    pub fn with_output_validator(mut self, validator: Arc<dyn OutputValidator>) -> Self {
188        self.output_validators.push(validator);
189        self
190    }
191
192    /// Set usage limits overlay.
193    #[must_use]
194    pub const fn with_usage_limits(mut self, limits: UsageLimits) -> Self {
195        self.usage_limits = Some(limits);
196        self
197    }
198}
199
200impl CapabilityBundle for StaticCapabilityBundle {
201    fn name(&self) -> &str {
202        &self.name
203    }
204
205    fn spec(&self) -> CapabilitySpec {
206        self.spec
207            .clone()
208            .unwrap_or_else(|| CapabilitySpec::new(self.name.clone()))
209    }
210
211    fn hooks(&self) -> Vec<Arc<dyn AgentCapability>> {
212        self.hooks.clone()
213    }
214
215    fn stream_observers(&self) -> Vec<Arc<dyn AgentCapability>> {
216        self.stream_observers.clone()
217    }
218
219    fn get_instructions(&self) -> Vec<String> {
220        self.instructions.clone()
221    }
222
223    fn dynamic_instructions(&self) -> Vec<DynDynamicInstruction> {
224        self.dynamic_instructions.clone()
225    }
226
227    fn get_tools(&self) -> Option<ToolRegistry> {
228        if self.tools.is_empty() && self.tools.get_instructions().is_empty() {
229            None
230        } else {
231            Some(self.tools.clone())
232        }
233    }
234
235    fn model_settings(&self) -> Option<ModelSettings> {
236        self.model_settings.clone()
237    }
238
239    fn request_params(&self) -> Option<ModelRequestParameters> {
240        self.request_params.clone()
241    }
242
243    fn output_functions(&self) -> Vec<DynOutputFunction> {
244        self.output_functions.clone()
245    }
246
247    fn output_validators(&self) -> Vec<Arc<dyn OutputValidator>> {
248        self.output_validators.clone()
249    }
250
251    fn usage_limits(&self) -> Option<UsageLimits> {
252        self.usage_limits.clone()
253    }
254}