Skip to main content

devboy_fireflies/
enricher.rs

1//! Schema enricher for Fireflies meeting notes tools.
2
3use devboy_core::{
4    CostModel, FollowUpLink, SideEffectClass, ToolCategory, ToolEnricher, ToolSchema,
5    ToolValueModel, ValueClass,
6};
7use serde_json::Value;
8
9/// Enricher for Fireflies.ai — declares support for MeetingNotes category.
10pub struct FirefliesSchemaEnricher;
11
12impl ToolEnricher for FirefliesSchemaEnricher {
13    fn supported_categories(&self) -> &[ToolCategory] {
14        &[ToolCategory::MeetingNotes]
15    }
16
17    fn enrich_schema(&self, _tool_name: &str, _schema: &mut ToolSchema) {
18        // Fireflies has no dynamic enrichment (no custom fields or metadata-driven enums).
19        // All parameters are statically defined in the base tool definitions.
20    }
21
22    fn transform_args(&self, _tool_name: &str, _args: &mut Value) {
23        // No argument transformation needed.
24    }
25
26    /// Paper 3 — Fireflies meetings → transcript chain. Transcripts
27    /// are large (typical 8 kB, max ~50 kB) but content is highly
28    /// reusable across the session, so prefetch hit rate is high
29    /// when the user asks "what was discussed".
30    fn value_model(&self, tool_name: &str) -> Option<ToolValueModel> {
31        let model = match tool_name {
32            "list_meetings" | "get_meeting_notes" => ToolValueModel {
33                value_class: ValueClass::Supporting,
34                cost_model: CostModel {
35                    typical_kb: 3.0,
36                    latency_ms_p50: Some(450),
37                    freshness_ttl_s: Some(300),
38                    ..CostModel::default()
39                },
40                follow_up: vec![FollowUpLink {
41                    tool: "get_meeting_transcript".into(),
42                    probability: 0.55,
43                    projection: Some("id".into()),
44                    projection_arg: Some("meeting_id".into()),
45                }],
46                side_effect_class: SideEffectClass::ReadOnly,
47                ..ToolValueModel::default()
48            },
49            "get_meeting_transcript" => ToolValueModel {
50                value_class: ValueClass::Critical,
51                cost_model: CostModel {
52                    typical_kb: 8.0,
53                    max_kb: Some(50.0),
54                    latency_ms_p50: Some(900),
55                    freshness_ttl_s: Some(600),
56                    ..CostModel::default()
57                },
58                side_effect_class: SideEffectClass::ReadOnly,
59                ..ToolValueModel::default()
60            },
61            _ => return None,
62        };
63        Some(model)
64    }
65
66    /// Fireflies API host.
67    fn rate_limit_host(&self, _tool_name: &str, _args: &Value) -> Option<String> {
68        Some("api.fireflies.ai".into())
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn test_supported_categories() {
78        let enricher = FirefliesSchemaEnricher;
79        assert_eq!(
80            enricher.supported_categories(),
81            &[ToolCategory::MeetingNotes]
82        );
83    }
84
85    #[test]
86    fn test_enrich_schema_is_noop() {
87        let enricher = FirefliesSchemaEnricher;
88        let mut schema = ToolSchema::new();
89        schema.add_property("test", devboy_core::PropertySchema::string("test field"));
90        let original_len = schema.properties.len();
91        enricher.enrich_schema("get_meeting_notes", &mut schema);
92        assert_eq!(schema.properties.len(), original_len);
93    }
94}