Skip to main content

composio_sdk/models/
modifiers.rs

1//! Tool modifiers for customizing tool behavior
2//!
3//! This module provides functionality to modify tool schemas, execution parameters,
4//! and execution responses. Modifiers allow you to customize tool behavior without
5//! changing the underlying tool definitions.
6//!
7//! # Modifier Types
8//!
9//! - [`BeforeExecute`] - Modify parameters before tool execution
10//! - [`AfterExecute`] - Modify response after tool execution
11//! - [`SchemaModifier`] - Modify tool schema before agent sees it
12//! - [`BeforeExecuteMeta`] - Modify parameters before meta tool execution (session context)
13//! - [`AfterExecuteMeta`] - Modify response after meta tool execution (session context)
14//!
15//! # Examples
16//!
17//! ```rust
18//! use composio_sdk::models::modifiers::{Modifier, ModifierType};
19//! use std::collections::HashMap;
20//!
21//! // Create a before_execute modifier
22//! let modifier = Modifier::new(
23//!     ModifierType::BeforeExecute,
24//!     vec!["GITHUB_CREATE_ISSUE".to_string()],
25//!     vec![],
26//! );
27//! ```
28
29use serde::{Deserialize, Serialize};
30use std::collections::HashMap;
31
32use super::response::{ToolExecutionResponse, ToolSchema};
33
34/// Parameters for tool execution
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct ToolExecuteParams {
37    /// Tool slug to execute
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub slug: Option<String>,
40    
41    /// Whether to allow tracing
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub allow_tracing: Option<bool>,
44    
45    /// Tool execution arguments
46    pub arguments: HashMap<String, serde_json::Value>,
47    
48    /// Connected account ID to use
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub connected_account_id: Option<String>,
51    
52    /// Custom authentication parameters
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub custom_auth_params: Option<CustomAuthParams>,
55    
56    /// Custom connection data
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub custom_connection_data: Option<CustomConnectionData>,
59    
60    /// Entity ID (deprecated, use user_id)
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub entity_id: Option<String>,
63    
64    /// Natural language text for tool execution
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub text: Option<String>,
67    
68    /// User ID for scoping
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub user_id: Option<String>,
71    
72    /// Tool version to execute
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub version: Option<String>,
75    
76    /// Skip version check (dangerous)
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub dangerously_skip_version_check: Option<bool>,
79}
80
81impl ToolExecuteParams {
82    /// Create new tool execution parameters with required slug
83    pub fn new(slug: impl Into<String>, arguments: HashMap<String, serde_json::Value>) -> Self {
84        Self {
85            slug: Some(slug.into()),
86            allow_tracing: None,
87            arguments,
88            connected_account_id: None,
89            custom_auth_params: None,
90            custom_connection_data: None,
91            entity_id: None,
92            text: None,
93            user_id: None,
94            version: None,
95            dangerously_skip_version_check: None,
96        }
97    }
98    
99    /// Get the tool slug (panics if not set)
100    pub fn slug(&self) -> &str {
101        self.slug.as_ref().expect("Tool slug is required for execution")
102    }
103}
104
105/// Custom authentication parameters
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct CustomAuthParams {
108    /// Base URL for API requests
109    #[serde(skip_serializing_if = "Option::is_none")]
110    pub base_url: Option<String>,
111    
112    /// Custom headers
113    #[serde(skip_serializing_if = "Option::is_none")]
114    pub headers: Option<HashMap<String, String>>,
115    
116    /// Custom query parameters
117    #[serde(skip_serializing_if = "Option::is_none")]
118    pub query_params: Option<HashMap<String, String>>,
119}
120
121/// Custom connection data for various auth schemes
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct CustomConnectionData {
124    /// Authentication scheme
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub auth_scheme: Option<String>,
127    
128    /// Connection parameters
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub parameters: Option<HashMap<String, serde_json::Value>>,
131}
132
133/// Type of modifier
134#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
135#[serde(rename_all = "snake_case")]
136pub enum ModifierType {
137    /// Modifier called before tool execution
138    BeforeExecute,
139    
140    /// Modifier called after tool execution
141    AfterExecute,
142    
143    /// Modifier for tool schema
144    Schema,
145    
146    /// Modifier called before meta tool execution (session context)
147    BeforeExecuteMeta,
148    
149    /// Modifier called after meta tool execution (session context)
150    AfterExecuteMeta,
151}
152
153/// Trait for before execute modifiers
154pub trait BeforeExecute: Send + Sync {
155    /// Modify parameters before tool execution
156    ///
157    /// # Arguments
158    ///
159    /// * `tool` - Tool slug (e.g., "GITHUB_CREATE_ISSUE")
160    /// * `toolkit` - Toolkit slug (e.g., "github")
161    /// * `params` - Execution parameters to modify
162    ///
163    /// # Returns
164    ///
165    /// Modified execution parameters
166    fn modify(&self, tool: &str, toolkit: &str, params: ToolExecuteParams) -> ToolExecuteParams;
167}
168
169/// Trait for after execute modifiers
170pub trait AfterExecute: Send + Sync {
171    /// Modify response after tool execution
172    ///
173    /// # Arguments
174    ///
175    /// * `tool` - Tool slug (e.g., "GITHUB_CREATE_ISSUE")
176    /// * `toolkit` - Toolkit slug (e.g., "github")
177    /// * `response` - Execution response to modify
178    ///
179    /// # Returns
180    ///
181    /// Modified execution response
182    fn modify(
183        &self,
184        tool: &str,
185        toolkit: &str,
186        response: ToolExecutionResponse,
187    ) -> ToolExecutionResponse;
188}
189
190/// Trait for schema modifiers
191pub trait SchemaModifier: Send + Sync {
192    /// Modify tool schema
193    ///
194    /// # Arguments
195    ///
196    /// * `tool` - Tool slug (e.g., "GITHUB_CREATE_ISSUE")
197    /// * `toolkit` - Toolkit slug (e.g., "github")
198    /// * `schema` - Tool schema to modify
199    ///
200    /// # Returns
201    ///
202    /// Modified tool schema
203    fn modify(&self, tool: &str, toolkit: &str, schema: ToolSchema) -> ToolSchema;
204}
205
206/// Trait for before execute meta modifiers (session context)
207pub trait BeforeExecuteMeta: Send + Sync {
208    /// Modify parameters before meta tool execution
209    ///
210    /// # Arguments
211    ///
212    /// * `tool` - Tool slug
213    /// * `toolkit` - Toolkit slug
214    /// * `session_id` - Session ID for context
215    /// * `params` - Parameters to modify
216    ///
217    /// # Returns
218    ///
219    /// Modified parameters
220    fn modify(
221        &self,
222        tool: &str,
223        toolkit: &str,
224        session_id: &str,
225        params: HashMap<String, serde_json::Value>,
226    ) -> HashMap<String, serde_json::Value>;
227}
228
229/// Trait for after execute meta modifiers (session context)
230pub trait AfterExecuteMeta: Send + Sync {
231    /// Modify response after meta tool execution
232    ///
233    /// # Arguments
234    ///
235    /// * `tool` - Tool slug
236    /// * `toolkit` - Toolkit slug
237    /// * `session_id` - Session ID for context
238    /// * `response` - Response to modify
239    ///
240    /// # Returns
241    ///
242    /// Modified response
243    fn modify(
244        &self,
245        tool: &str,
246        toolkit: &str,
247        session_id: &str,
248        response: ToolExecutionResponse,
249    ) -> ToolExecutionResponse;
250}
251
252/// A modifier that can be applied to tools
253pub struct Modifier {
254    /// Type of modifier
255    pub modifier_type: ModifierType,
256    
257    /// List of tool slugs this modifier applies to (empty = all tools)
258    pub tools: Vec<String>,
259    
260    /// List of toolkit slugs this modifier applies to (empty = all toolkits)
261    pub toolkits: Vec<String>,
262    
263    /// The actual modifier function
264    modifier_fn: ModifierFunction,
265}
266
267/// Enum to hold different types of modifier functions
268enum ModifierFunction {
269    BeforeExecute(Box<dyn BeforeExecute>),
270    AfterExecute(Box<dyn AfterExecute>),
271    Schema(Box<dyn SchemaModifier>),
272    BeforeExecuteMeta(Box<dyn BeforeExecuteMeta>),
273    AfterExecuteMeta(Box<dyn AfterExecuteMeta>),
274}
275
276impl Modifier {
277    /// Create a new before_execute modifier
278    pub fn before_execute<F>(tools: Vec<String>, toolkits: Vec<String>, modifier: F) -> Self
279    where
280        F: BeforeExecute + 'static,
281    {
282        Self {
283            modifier_type: ModifierType::BeforeExecute,
284            tools,
285            toolkits,
286            modifier_fn: ModifierFunction::BeforeExecute(Box::new(modifier)),
287        }
288    }
289
290    /// Create a new after_execute modifier
291    pub fn after_execute<F>(tools: Vec<String>, toolkits: Vec<String>, modifier: F) -> Self
292    where
293        F: AfterExecute + 'static,
294    {
295        Self {
296            modifier_type: ModifierType::AfterExecute,
297            tools,
298            toolkits,
299            modifier_fn: ModifierFunction::AfterExecute(Box::new(modifier)),
300        }
301    }
302
303    /// Create a new schema modifier
304    pub fn schema<F>(tools: Vec<String>, toolkits: Vec<String>, modifier: F) -> Self
305    where
306        F: SchemaModifier + 'static,
307    {
308        Self {
309            modifier_type: ModifierType::Schema,
310            tools,
311            toolkits,
312            modifier_fn: ModifierFunction::Schema(Box::new(modifier)),
313        }
314    }
315
316    /// Create a new before_execute_meta modifier
317    pub fn before_execute_meta<F>(tools: Vec<String>, toolkits: Vec<String>, modifier: F) -> Self
318    where
319        F: BeforeExecuteMeta + 'static,
320    {
321        Self {
322            modifier_type: ModifierType::BeforeExecuteMeta,
323            tools,
324            toolkits,
325            modifier_fn: ModifierFunction::BeforeExecuteMeta(Box::new(modifier)),
326        }
327    }
328
329    /// Create a new after_execute_meta modifier
330    pub fn after_execute_meta<F>(tools: Vec<String>, toolkits: Vec<String>, modifier: F) -> Self
331    where
332        F: AfterExecuteMeta + 'static,
333    {
334        Self {
335            modifier_type: ModifierType::AfterExecuteMeta,
336            tools,
337            toolkits,
338            modifier_fn: ModifierFunction::AfterExecuteMeta(Box::new(modifier)),
339        }
340    }
341
342    /// Check if this modifier should be applied to the given tool/toolkit
343    fn should_apply(&self, tool: &str, toolkit: &str) -> bool {
344        // If no tools or toolkits specified, apply to all
345        if self.tools.is_empty() && self.toolkits.is_empty() {
346            return true;
347        }
348
349        // Check if tool or toolkit matches
350        self.tools.contains(&tool.to_string()) || self.toolkits.contains(&toolkit.to_string())
351    }
352
353    /// Apply the modifier to tool execution parameters
354    pub fn apply_to_params(
355        &self,
356        tool: &str,
357        toolkit: &str,
358        params: ToolExecuteParams,
359    ) -> Result<ToolExecuteParams, String> {
360        if !self.should_apply(tool, toolkit) {
361            return Ok(params);
362        }
363
364        match &self.modifier_fn {
365            ModifierFunction::BeforeExecute(modifier) => Ok(modifier.modify(tool, toolkit, params)),
366            _ => Err("Modifier type mismatch: expected BeforeExecute".to_string()),
367        }
368    }
369
370    /// Apply the modifier to tool execution response
371    pub fn apply_to_response(
372        &self,
373        tool: &str,
374        toolkit: &str,
375        response: ToolExecutionResponse,
376    ) -> Result<ToolExecutionResponse, String> {
377        if !self.should_apply(tool, toolkit) {
378            return Ok(response);
379        }
380
381        match &self.modifier_fn {
382            ModifierFunction::AfterExecute(modifier) => {
383                Ok(modifier.modify(tool, toolkit, response))
384            }
385            _ => Err("Modifier type mismatch: expected AfterExecute".to_string()),
386        }
387    }
388
389    /// Apply the modifier to tool schema
390    pub fn apply_to_schema(
391        &self,
392        tool: &str,
393        toolkit: &str,
394        schema: ToolSchema,
395    ) -> Result<ToolSchema, String> {
396        if !self.should_apply(tool, toolkit) {
397            return Ok(schema);
398        }
399
400        match &self.modifier_fn {
401            ModifierFunction::Schema(modifier) => Ok(modifier.modify(tool, toolkit, schema)),
402            _ => Err("Modifier type mismatch: expected Schema".to_string()),
403        }
404    }
405
406    /// Apply the modifier to meta tool parameters
407    pub fn apply_to_meta_params(
408        &self,
409        tool: &str,
410        toolkit: &str,
411        session_id: &str,
412        params: HashMap<String, serde_json::Value>,
413    ) -> Result<HashMap<String, serde_json::Value>, String> {
414        if !self.should_apply(tool, toolkit) {
415            return Ok(params);
416        }
417
418        match &self.modifier_fn {
419            ModifierFunction::BeforeExecuteMeta(modifier) => {
420                Ok(modifier.modify(tool, toolkit, session_id, params))
421            }
422            _ => Err("Modifier type mismatch: expected BeforeExecuteMeta".to_string()),
423        }
424    }
425
426    /// Apply the modifier to meta tool response
427    pub fn apply_to_meta_response(
428        &self,
429        tool: &str,
430        toolkit: &str,
431        session_id: &str,
432        response: ToolExecutionResponse,
433    ) -> Result<ToolExecutionResponse, String> {
434        if !self.should_apply(tool, toolkit) {
435            return Ok(response);
436        }
437
438        match &self.modifier_fn {
439            ModifierFunction::AfterExecuteMeta(modifier) => {
440                Ok(modifier.modify(tool, toolkit, session_id, response))
441            }
442            _ => Err("Modifier type mismatch: expected AfterExecuteMeta".to_string()),
443        }
444    }
445}
446
447/// Collection of modifiers
448pub type Modifiers = Vec<Modifier>;
449
450/// Apply modifiers to tool execution parameters
451pub fn apply_before_execute_modifiers(
452    modifiers: &Modifiers,
453    tool: &str,
454    toolkit: &str,
455    mut params: ToolExecuteParams,
456) -> Result<ToolExecuteParams, String> {
457    for modifier in modifiers {
458        if modifier.modifier_type == ModifierType::BeforeExecute {
459            params = modifier.apply_to_params(tool, toolkit, params)?;
460        }
461    }
462    Ok(params)
463}
464
465/// Apply modifiers to tool execution response
466pub fn apply_after_execute_modifiers(
467    modifiers: &Modifiers,
468    tool: &str,
469    toolkit: &str,
470    mut response: ToolExecutionResponse,
471) -> Result<ToolExecutionResponse, String> {
472    for modifier in modifiers {
473        if modifier.modifier_type == ModifierType::AfterExecute {
474            response = modifier.apply_to_response(tool, toolkit, response)?;
475        }
476    }
477    Ok(response)
478}
479
480/// Apply modifiers to tool schema
481pub fn apply_schema_modifiers(
482    modifiers: &Modifiers,
483    tool: &str,
484    toolkit: &str,
485    mut schema: ToolSchema,
486) -> Result<ToolSchema, String> {
487    for modifier in modifiers {
488        if modifier.modifier_type == ModifierType::Schema {
489            schema = modifier.apply_to_schema(tool, toolkit, schema)?;
490        }
491    }
492    Ok(schema)
493}
494
495/// Apply modifiers to meta tool parameters
496pub fn apply_before_execute_meta_modifiers(
497    modifiers: &Modifiers,
498    tool: &str,
499    toolkit: &str,
500    session_id: &str,
501    mut params: HashMap<String, serde_json::Value>,
502) -> Result<HashMap<String, serde_json::Value>, String> {
503    for modifier in modifiers {
504        if modifier.modifier_type == ModifierType::BeforeExecuteMeta {
505            params = modifier.apply_to_meta_params(tool, toolkit, session_id, params)?;
506        }
507    }
508    Ok(params)
509}
510
511/// Apply modifiers to meta tool response
512pub fn apply_after_execute_meta_modifiers(
513    modifiers: &Modifiers,
514    tool: &str,
515    toolkit: &str,
516    session_id: &str,
517    mut response: ToolExecutionResponse,
518) -> Result<ToolExecutionResponse, String> {
519    for modifier in modifiers {
520        if modifier.modifier_type == ModifierType::AfterExecuteMeta {
521            response = modifier.apply_to_meta_response(tool, toolkit, session_id, response)?;
522        }
523    }
524    Ok(response)
525}