Skip to main content

st/context_gatherer/
partnership.rs

1//! AI-Human Partnership Analysis
2//!
3//! This module analyzes the quality and patterns of AI-human collaborations,
4//! helping both parties understand and improve their working relationship.
5
6use chrono::{DateTime, Duration, Timelike, Utc};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10use super::{ContextContent, ContextType, GatheredContext};
11
12/// Partnership analyzer for AI-human collaboration
13pub struct PartnershipAnalyzer {
14    contexts: Vec<GatheredContext>,
15}
16
17impl PartnershipAnalyzer {
18    pub fn new(contexts: Vec<GatheredContext>) -> Self {
19        Self { contexts }
20    }
21
22    /// Analyze the overall partnership health
23    pub fn analyze_partnership(&self) -> PartnershipAnalysis {
24        let sessions = self.detect_collaborative_sessions();
25        let conversation_flows = self.analyze_conversation_flows();
26        let interaction_patterns = self.analyze_interaction_patterns();
27        let collaboration_metrics = self.calculate_collaboration_metrics(&sessions);
28        let relationship_evolution = self.analyze_relationship_evolution();
29        let shared_understanding = self.measure_shared_understanding();
30
31        PartnershipAnalysis {
32            total_interactions: self.contexts.len(),
33            collaborative_sessions: sessions,
34            conversation_flows,
35            interaction_patterns,
36            collaboration_metrics,
37            relationship_evolution,
38            shared_understanding,
39            partnership_health: self.calculate_partnership_health(&collaboration_metrics),
40            recommendations: self.generate_recommendations(&collaboration_metrics),
41        }
42    }
43
44    /// Detect collaborative sessions where human and AI work together
45    fn detect_collaborative_sessions(&self) -> Vec<CollaborativeSession> {
46        let mut sessions = Vec::new();
47        let mut current_session: Option<CollaborativeSession> = None;
48
49        // Group contexts by time proximity (within 30 minutes)
50        let session_gap = Duration::minutes(30);
51
52        let mut sorted_contexts = self.contexts.clone();
53        sorted_contexts.sort_by_key(|c| c.timestamp);
54
55        for context in sorted_contexts {
56            if let Some(ref mut session) = current_session {
57                let gap = context.timestamp - session.end_time;
58
59                if gap > session_gap || context.ai_tool != session.primary_tool {
60                    // End current session
61                    sessions.push(session.clone());
62                    current_session = Some(CollaborativeSession::new(context));
63                } else {
64                    // Continue session
65                    session.update(context);
66                }
67            } else {
68                // Start first session
69                current_session = Some(CollaborativeSession::new(context));
70            }
71        }
72
73        if let Some(session) = current_session {
74            sessions.push(session);
75        }
76
77        sessions
78    }
79
80    /// Analyze conversation flow patterns
81    fn analyze_conversation_flows(&self) -> Vec<ConversationFlow> {
82        let mut flows = Vec::new();
83
84        // Extract chat histories
85        let chat_contexts: Vec<_> = self
86            .contexts
87            .iter()
88            .filter(|c| matches!(c.content_type, ContextType::ChatHistory))
89            .collect();
90
91        for context in chat_contexts {
92            if let ContextContent::Json(json) = &context.content {
93                if let Some(messages) = json.get("messages").and_then(|m| m.as_array()) {
94                    let flow = self.analyze_message_flow(messages, &context.ai_tool);
95                    flows.push(flow);
96                }
97            }
98        }
99
100        flows
101    }
102
103    /// Analyze a single message flow
104    fn analyze_message_flow(&self, messages: &[serde_json::Value], tool: &str) -> ConversationFlow {
105        let mut turn_count = 0;
106        let mut question_count = 0;
107        let mut clarification_count = 0;
108        let mut completion_count = 0;
109        let mut context_switches = 0;
110        let mut last_topic = String::new();
111
112        for (i, msg) in messages.iter().enumerate() {
113            if let Some(content) = msg.get("content").and_then(|c| c.as_str()) {
114                turn_count += 1;
115
116                // Detect questions
117                if content.contains('?') {
118                    question_count += 1;
119                }
120
121                // Detect clarifications
122                if content.contains("clarify")
123                    || content.contains("what do you mean")
124                    || content.contains("could you explain")
125                {
126                    clarification_count += 1;
127                }
128
129                // Detect completions
130                if content.contains("done")
131                    || content.contains("complete")
132                    || content.contains("finished")
133                    || content.contains("works")
134                {
135                    completion_count += 1;
136                }
137
138                // Detect topic switches (simplified)
139                let current_topic = self.extract_topic(content);
140                if i > 0 && current_topic != last_topic {
141                    context_switches += 1;
142                }
143                last_topic = current_topic;
144            }
145        }
146
147        ConversationFlow {
148            tool: tool.to_string(),
149            turn_count,
150            question_count,
151            clarification_count,
152            completion_count,
153            context_switches,
154            flow_smoothness: self.calculate_flow_smoothness(
155                turn_count,
156                clarification_count,
157                context_switches,
158            ),
159        }
160    }
161
162    /// Calculate how smooth the conversation flow is
163    fn calculate_flow_smoothness(
164        &self,
165        turns: usize,
166        clarifications: usize,
167        switches: usize,
168    ) -> f32 {
169        if turns == 0 {
170            return 0.0;
171        }
172
173        let disruptions = clarifications + switches;
174
175        1.0 - (disruptions as f32 / turns as f32).min(1.0)
176    }
177
178    /// Extract topic from content (simplified)
179    fn extract_topic(&self, content: &str) -> String {
180        // Simple keyword-based topic extraction
181        let keywords = [
182            "function", "file", "error", "bug", "feature", "test", "deploy", "design",
183        ];
184
185        for keyword in keywords {
186            if content.to_lowercase().contains(keyword) {
187                return keyword.to_string();
188            }
189        }
190
191        "general".to_string()
192    }
193
194    /// Analyze interaction patterns
195    fn analyze_interaction_patterns(&self) -> InteractionPatterns {
196        let mut tool_preferences = HashMap::new();
197        let mut time_of_day_activity = vec![0; 24]; // 24 hours
198        let mut response_patterns = ResponsePatterns::default();
199
200        for context in &self.contexts {
201            // Track tool preferences
202            *tool_preferences.entry(context.ai_tool.clone()).or_insert(0) += 1;
203
204            // Track time of day
205            let hour = context.timestamp.hour() as usize;
206            time_of_day_activity[hour] += 1;
207
208            // Analyze response patterns (simplified)
209            if let ContextContent::Json(json) = &context.content {
210                if let Some(messages) = json.get("messages").and_then(|m| m.as_array()) {
211                    self.analyze_response_patterns(messages, &mut response_patterns);
212                }
213            }
214        }
215
216        // Find peak hours
217        let peak_hours = self.find_peak_hours(&time_of_day_activity);
218
219        InteractionPatterns {
220            tool_preferences,
221            peak_collaboration_hours: peak_hours,
222            average_session_length: self.calculate_average_session_length(),
223            response_patterns,
224        }
225    }
226
227    /// Analyze response patterns in conversations
228    fn analyze_response_patterns(
229        &self,
230        messages: &[serde_json::Value],
231        patterns: &mut ResponsePatterns,
232    ) {
233        let mut last_was_human = false;
234
235        for msg in messages.iter() {
236            if let Some(role) = msg.get("role").and_then(|r| r.as_str()) {
237                if role == "user" {
238                    last_was_human = true;
239                } else if role == "assistant" && last_was_human {
240                    // AI response to human
241                    if let Some(content) = msg.get("content").and_then(|c| c.as_str()) {
242                        // Measure response characteristics
243                        if content.len() > 500 {
244                            patterns.detailed_responses += 1;
245                        } else if content.len() < 100 {
246                            patterns.brief_responses += 1;
247                        }
248
249                        if content.contains("```") {
250                            patterns.code_heavy_responses += 1;
251                        }
252
253                        if content.contains("Let me") || content.contains("I'll") {
254                            patterns.proactive_responses += 1;
255                        }
256                    }
257                    last_was_human = false;
258                }
259            }
260        }
261
262        patterns.total_responses += messages.len() / 2; // Rough estimate
263    }
264
265    /// Find peak collaboration hours
266    fn find_peak_hours(&self, activity: &[usize]) -> Vec<usize> {
267        let mut hours_with_activity: Vec<(usize, usize)> = activity
268            .iter()
269            .enumerate()
270            .map(|(hour, &count)| (hour, count))
271            .filter(|(_, count)| *count > 0)
272            .collect();
273
274        hours_with_activity.sort_by_key(|(_, count)| std::cmp::Reverse(*count));
275
276        hours_with_activity
277            .into_iter()
278            .take(3)
279            .map(|(hour, _)| hour)
280            .collect()
281    }
282
283    /// Calculate average session length
284    fn calculate_average_session_length(&self) -> Duration {
285        let sessions = self.detect_collaborative_sessions();
286        if sessions.is_empty() {
287            return Duration::zero();
288        }
289
290        let total_duration: Duration = sessions.iter().map(|s| s.end_time - s.start_time).sum();
291
292        let avg_seconds = total_duration.num_seconds() / sessions.len() as i64;
293        Duration::seconds(avg_seconds)
294    }
295
296    /// Calculate collaboration metrics
297    fn calculate_collaboration_metrics(
298        &self,
299        sessions: &[CollaborativeSession],
300    ) -> CollaborationMetrics {
301        let total_sessions = sessions.len();
302        let mut productive_sessions = 0;
303        let mut stuck_sessions = 0;
304        let mut learning_sessions = 0;
305
306        for session in sessions {
307            if session.outcomes_achieved > 0 {
308                productive_sessions += 1;
309            }
310            if session.clarifications_needed > 2 {
311                stuck_sessions += 1;
312            }
313            if session.new_concepts_introduced > 0 {
314                learning_sessions += 1;
315            }
316        }
317
318        CollaborationMetrics {
319            productivity_rate: if total_sessions > 0 {
320                productive_sessions as f32 / total_sessions as f32
321            } else {
322                0.0
323            },
324            learning_rate: if total_sessions > 0 {
325                learning_sessions as f32 / total_sessions as f32
326            } else {
327                0.0
328            },
329            stuck_rate: if total_sessions > 0 {
330                stuck_sessions as f32 / total_sessions as f32
331            } else {
332                0.0
333            },
334            collaboration_depth: self.calculate_collaboration_depth(sessions),
335            mutual_understanding: self.calculate_mutual_understanding(sessions),
336        }
337    }
338
339    /// Calculate depth of collaboration
340    fn calculate_collaboration_depth(&self, sessions: &[CollaborativeSession]) -> f32 {
341        if sessions.is_empty() {
342            return 0.0;
343        }
344
345        let avg_interactions = sessions.iter().map(|s| s.interaction_count).sum::<usize>() as f32
346            / sessions.len() as f32;
347
348        // Normalize to 0-1 scale (assuming 20+ interactions is deep)
349        (avg_interactions / 20.0).min(1.0)
350    }
351
352    /// Calculate mutual understanding score
353    fn calculate_mutual_understanding(&self, sessions: &[CollaborativeSession]) -> f32 {
354        if sessions.is_empty() {
355            return 0.0;
356        }
357
358        let understanding_factors = sessions
359            .iter()
360            .map(|s| {
361                let clarity =
362                    1.0 - (s.clarifications_needed as f32 / s.interaction_count.max(1) as f32);
363                let efficiency = s.outcomes_achieved as f32 / s.interaction_count.max(1) as f32;
364                (clarity + efficiency) / 2.0
365            })
366            .sum::<f32>();
367
368        understanding_factors / sessions.len() as f32
369    }
370
371    /// Analyze how the relationship has evolved
372    fn analyze_relationship_evolution(&self) -> RelationshipEvolution {
373        let sessions = self.detect_collaborative_sessions();
374        if sessions.len() < 2 {
375            return RelationshipEvolution::default();
376        }
377
378        // Split sessions into early and recent
379        let mid_point = sessions.len() / 2;
380        let early_sessions = &sessions[..mid_point];
381        let recent_sessions = &sessions[mid_point..];
382
383        let early_metrics = self.calculate_collaboration_metrics(early_sessions);
384        let recent_metrics = self.calculate_collaboration_metrics(recent_sessions);
385
386        RelationshipEvolution {
387            productivity_trend: recent_metrics.productivity_rate - early_metrics.productivity_rate,
388            understanding_trend: recent_metrics.mutual_understanding
389                - early_metrics.mutual_understanding,
390            complexity_trend: recent_sessions
391                .iter()
392                .map(|s| s.new_concepts_introduced)
393                .sum::<usize>() as f32
394                / early_sessions
395                    .iter()
396                    .map(|s| s.new_concepts_introduced)
397                    .sum::<usize>()
398                    .max(1) as f32,
399            trust_indicators: self.calculate_trust_indicators(&sessions),
400        }
401    }
402
403    /// Calculate trust indicators
404    fn calculate_trust_indicators(&self, sessions: &[CollaborativeSession]) -> TrustIndicators {
405        let mut autonomy_given = 0;
406        let mut suggestions_followed = 0;
407
408        // Simplified trust calculation based on session patterns
409        for session in sessions {
410            if session.interaction_count > 5 && session.clarifications_needed < 2 {
411                autonomy_given += 1;
412            }
413            if session.outcomes_achieved > 0 {
414                suggestions_followed += 1;
415            }
416        }
417
418        TrustIndicators {
419            autonomy_level: autonomy_given as f32 / sessions.len().max(1) as f32,
420            correction_acceptance: 0.8, // Placeholder - would need more detailed analysis
421            suggestion_adoption_rate: suggestions_followed as f32 / sessions.len().max(1) as f32,
422        }
423    }
424
425    /// Measure shared understanding between AI and human
426    fn measure_shared_understanding(&self) -> SharedUnderstanding {
427        let mut shared_vocabulary = HashMap::new();
428        let mut communication_efficiency = 0.0;
429
430        // Analyze vocabulary usage
431        for context in &self.contexts {
432            if let ContextContent::Text(text) = &context.content {
433                // Extract technical terms (simplified)
434                for word in text.split_whitespace() {
435                    if word.len() > 5 && !word.chars().all(|c| c.is_lowercase()) {
436                        *shared_vocabulary.entry(word.to_string()).or_insert(0) += 1;
437                    }
438                }
439            }
440        }
441
442        // Calculate efficiency based on decreasing clarifications over time
443        let sessions = self.detect_collaborative_sessions();
444        if sessions.len() > 1 {
445            let early_clarifications = sessions[..sessions.len() / 2]
446                .iter()
447                .map(|s| s.clarifications_needed)
448                .sum::<usize>() as f32;
449            let recent_clarifications = sessions[sessions.len() / 2..]
450                .iter()
451                .map(|s| s.clarifications_needed)
452                .sum::<usize>() as f32;
453
454            communication_efficiency =
455                1.0 - (recent_clarifications / early_clarifications.max(1.0)).min(1.0);
456        }
457
458        SharedUnderstanding {
459            vocabulary_size: shared_vocabulary.len(),
460            concept_alignment: 0.75, // Placeholder
461            communication_efficiency,
462            domain_expertise_areas: self.identify_expertise_areas(&shared_vocabulary),
463        }
464    }
465
466    /// Identify areas of shared expertise
467    fn identify_expertise_areas(&self, vocabulary: &HashMap<String, usize>) -> Vec<String> {
468        // Simple domain identification based on vocabulary
469        let domains = vec![
470            ("rust", vec!["impl", "trait", "async", "tokio", "cargo"]),
471            (
472                "javascript",
473                vec!["const", "async", "await", "npm", "react"],
474            ),
475            ("python", vec!["def", "import", "pip", "django", "flask"]),
476            (
477                "ai",
478                vec!["model", "training", "neural", "embedding", "transformer"],
479            ),
480            (
481                "database",
482                vec!["query", "table", "index", "postgres", "sqlite"],
483            ),
484        ];
485
486        let mut identified_domains = Vec::new();
487
488        for (domain, keywords) in domains {
489            let matches = keywords
490                .iter()
491                .filter(|k| vocabulary.contains_key(&k.to_string()))
492                .count();
493
494            if matches >= 2 {
495                identified_domains.push(domain.to_string());
496            }
497        }
498
499        identified_domains
500    }
501
502    /// Calculate overall partnership health
503    fn calculate_partnership_health(&self, metrics: &CollaborationMetrics) -> PartnershipHealth {
504        let score = (metrics.productivity_rate * 0.3
505            + metrics.learning_rate * 0.2
506            + (1.0 - metrics.stuck_rate) * 0.2
507            + metrics.collaboration_depth * 0.15
508            + metrics.mutual_understanding * 0.15)
509            .min(1.0);
510
511        let status = match score {
512            s if s >= 0.8 => "Thriving",
513            s if s >= 0.6 => "Healthy",
514            s if s >= 0.4 => "Developing",
515            _ => "Needs Attention",
516        };
517
518        PartnershipHealth {
519            overall_score: score,
520            status: status.to_string(),
521            strengths: self.identify_strengths(metrics),
522            areas_for_improvement: self.identify_improvements(metrics),
523        }
524    }
525
526    /// Identify partnership strengths
527    fn identify_strengths(&self, metrics: &CollaborationMetrics) -> Vec<String> {
528        let mut strengths = Vec::new();
529
530        if metrics.productivity_rate > 0.7 {
531            strengths.push("High productivity in achieving goals".to_string());
532        }
533        if metrics.learning_rate > 0.5 {
534            strengths.push("Continuous learning and growth".to_string());
535        }
536        if metrics.mutual_understanding > 0.7 {
537            strengths.push("Strong mutual understanding".to_string());
538        }
539        if metrics.collaboration_depth > 0.6 {
540            strengths.push("Deep, meaningful collaborations".to_string());
541        }
542
543        strengths
544    }
545
546    /// Identify areas for improvement
547    fn identify_improvements(&self, metrics: &CollaborationMetrics) -> Vec<String> {
548        let mut improvements = Vec::new();
549
550        if metrics.stuck_rate > 0.3 {
551            improvements.push("Reduce getting stuck - try breaking down complex tasks".to_string());
552        }
553        if metrics.productivity_rate < 0.5 {
554            improvements.push("Focus on completing more outcomes per session".to_string());
555        }
556        if metrics.mutual_understanding < 0.5 {
557            improvements.push("Improve clarity in communication".to_string());
558        }
559
560        improvements
561    }
562
563    /// Generate recommendations for improving the partnership
564    fn generate_recommendations(&self, metrics: &CollaborationMetrics) -> Vec<String> {
565        let mut recommendations = Vec::new();
566
567        // Base recommendations on metrics
568        if metrics.stuck_rate > 0.2 {
569            recommendations.push(
570                "💡 When stuck, try: 1) Break down the problem, 2) Provide more context, 3) Ask for alternative approaches".to_string()
571            );
572        }
573
574        if metrics.learning_rate < 0.3 {
575            recommendations.push(
576                "📚 Explore new areas together - try asking about unfamiliar technologies or concepts".to_string()
577            );
578        }
579
580        if metrics.collaboration_depth < 0.5 {
581            recommendations.push(
582                "🤝 Deepen collaboration by working on longer-term projects together".to_string(),
583            );
584        }
585
586        if metrics.mutual_understanding < 0.6 {
587            recommendations.push(
588                "🗣️ Improve understanding by being more specific about requirements and constraints".to_string()
589            );
590        }
591
592        // Always add one positive reinforcement
593        recommendations.push("🌟 Keep building on your collaborative strengths!".to_string());
594
595        recommendations
596    }
597}
598
599/// A collaborative session between human and AI
600#[derive(Debug, Clone, Serialize, Deserialize)]
601pub struct CollaborativeSession {
602    pub start_time: DateTime<Utc>,
603    pub end_time: DateTime<Utc>,
604    pub primary_tool: String,
605    pub interaction_count: usize,
606    pub clarifications_needed: usize,
607    pub outcomes_achieved: usize,
608    pub new_concepts_introduced: usize,
609    pub session_mood: SessionMood,
610}
611
612impl CollaborativeSession {
613    fn new(context: GatheredContext) -> Self {
614        let mut session = Self {
615            start_time: context.timestamp,
616            end_time: context.timestamp,
617            primary_tool: context.ai_tool.clone(),
618            interaction_count: 0,
619            clarifications_needed: 0,
620            outcomes_achieved: 0,
621            new_concepts_introduced: 0,
622            session_mood: SessionMood::Neutral,
623        };
624
625        // Analyze the first context
626        session.update(context);
627        session
628    }
629
630    fn update(&mut self, context: GatheredContext) {
631        self.end_time = context.timestamp;
632        self.interaction_count += 1;
633
634        // Analyze content to update metrics
635        if let ContextContent::Json(json) = &context.content {
636            if let Some(messages) = json.get("messages").and_then(|m| m.as_array()) {
637                for msg in messages {
638                    if let Some(content) = msg.get("content").and_then(|c| c.as_str()) {
639                        // Check for completion indicators
640                        if content.contains("done")
641                            || content.contains("complete")
642                            || content.contains("finished")
643                            || content.contains("works")
644                            || content.contains("successfully")
645                        {
646                            self.outcomes_achieved += 1;
647                        }
648
649                        // Check for clarifications
650                        if content.contains("?")
651                            || content.contains("clarify")
652                            || content.contains("what do you mean")
653                        {
654                            self.clarifications_needed += 1;
655                        }
656
657                        // Check for new concepts
658                        if content.contains("What's")
659                            || content.contains("How does")
660                            || content.contains("explain")
661                            || content.contains("is a ")
662                        {
663                            self.new_concepts_introduced += 1;
664                        }
665                    }
666                }
667            }
668        }
669
670        // Update mood based on metrics
671        if self.outcomes_achieved > self.clarifications_needed {
672            self.session_mood = SessionMood::Productive;
673        } else if self.clarifications_needed > 3 {
674            self.session_mood = SessionMood::Stuck;
675        }
676    }
677}
678
679#[derive(Debug, Clone, Serialize, Deserialize)]
680pub enum SessionMood {
681    Frustrated,
682    Stuck,
683    Neutral,
684    Productive,
685    Excited,
686}
687
688/// Analysis of conversation flow
689#[derive(Debug, Clone, Serialize, Deserialize)]
690pub struct ConversationFlow {
691    pub tool: String,
692    pub turn_count: usize,
693    pub question_count: usize,
694    pub clarification_count: usize,
695    pub completion_count: usize,
696    pub context_switches: usize,
697    pub flow_smoothness: f32,
698}
699
700/// Interaction patterns between human and AI
701#[derive(Debug, Clone, Serialize, Deserialize)]
702pub struct InteractionPatterns {
703    pub tool_preferences: HashMap<String, usize>,
704    pub peak_collaboration_hours: Vec<usize>,
705    pub average_session_length: Duration,
706    pub response_patterns: ResponsePatterns,
707}
708
709#[derive(Debug, Clone, Default, Serialize, Deserialize)]
710pub struct ResponsePatterns {
711    pub total_responses: usize,
712    pub detailed_responses: usize,
713    pub brief_responses: usize,
714    pub code_heavy_responses: usize,
715    pub proactive_responses: usize,
716}
717
718/// Metrics for collaboration quality
719#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
720pub struct CollaborationMetrics {
721    pub productivity_rate: f32,
722    pub learning_rate: f32,
723    pub stuck_rate: f32,
724    pub collaboration_depth: f32,
725    pub mutual_understanding: f32,
726}
727
728/// How the relationship has evolved over time
729#[derive(Debug, Clone, Default, Serialize, Deserialize)]
730pub struct RelationshipEvolution {
731    pub productivity_trend: f32, // Positive = improving
732    pub understanding_trend: f32,
733    pub complexity_trend: f32, // Handling more complex tasks
734    pub trust_indicators: TrustIndicators,
735}
736
737#[derive(Debug, Clone, Default, Serialize, Deserialize)]
738pub struct TrustIndicators {
739    pub autonomy_level: f32,
740    pub correction_acceptance: f32,
741    pub suggestion_adoption_rate: f32,
742}
743
744/// Shared understanding metrics
745#[derive(Debug, Clone, Serialize, Deserialize)]
746pub struct SharedUnderstanding {
747    pub vocabulary_size: usize,
748    pub concept_alignment: f32,
749    pub communication_efficiency: f32,
750    pub domain_expertise_areas: Vec<String>,
751}
752
753/// Overall partnership health
754#[derive(Debug, Clone, Serialize, Deserialize)]
755pub struct PartnershipHealth {
756    pub overall_score: f32,
757    pub status: String,
758    pub strengths: Vec<String>,
759    pub areas_for_improvement: Vec<String>,
760}
761
762/// Complete partnership analysis
763#[derive(Debug, Clone, Serialize, Deserialize)]
764pub struct PartnershipAnalysis {
765    pub total_interactions: usize,
766    pub collaborative_sessions: Vec<CollaborativeSession>,
767    pub conversation_flows: Vec<ConversationFlow>,
768    pub interaction_patterns: InteractionPatterns,
769    pub collaboration_metrics: CollaborationMetrics,
770    pub relationship_evolution: RelationshipEvolution,
771    pub shared_understanding: SharedUnderstanding,
772    pub partnership_health: PartnershipHealth,
773    pub recommendations: Vec<String>,
774}