hermes-gsuite-cli 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. gsuite_cli/__init__.py +7 -0
  2. gsuite_cli/ai/__init__.py +38 -0
  3. gsuite_cli/ai/analytics.py +460 -0
  4. gsuite_cli/ai/chatbot.py +48 -0
  5. gsuite_cli/ai/commands.py +667 -0
  6. gsuite_cli/ai/date_parser.py +234 -0
  7. gsuite_cli/ai/gemini_client.py +62 -0
  8. gsuite_cli/ai/intent_schema.py +482 -0
  9. gsuite_cli/ai/nlp.py +677 -0
  10. gsuite_cli/ai/query_engine.py +1584 -0
  11. gsuite_cli/ai/summarizer.py +385 -0
  12. gsuite_cli/ai/tool_registry.py +1230 -0
  13. gsuite_cli/auth/__init__.py +7 -0
  14. gsuite_cli/auth/oauth.py +391 -0
  15. gsuite_cli/cli.py +6086 -0
  16. gsuite_cli/config/__init__.py +5 -0
  17. gsuite_cli/config/hermes_config.py +125 -0
  18. gsuite_cli/config/manager.py +349 -0
  19. gsuite_cli/profile/__init__.py +35 -0
  20. gsuite_cli/profile/commands.py +188 -0
  21. gsuite_cli/profile/models.py +190 -0
  22. gsuite_cli/profile/resolver.py +440 -0
  23. gsuite_cli/profile/service.py +357 -0
  24. gsuite_cli/profile/storage.py +125 -0
  25. gsuite_cli/profile/validators.py +86 -0
  26. gsuite_cli/services/__init__.py +3 -0
  27. gsuite_cli/services/admin.py +268 -0
  28. gsuite_cli/services/apps_script.py +206 -0
  29. gsuite_cli/services/booking.py +735 -0
  30. gsuite_cli/services/calendar.py +846 -0
  31. gsuite_cli/services/calendar_advanced.py +534 -0
  32. gsuite_cli/services/calendar_resolver.py +510 -0
  33. gsuite_cli/services/calendar_validators.py +207 -0
  34. gsuite_cli/services/chat.py +457 -0
  35. gsuite_cli/services/chat_commands.py +423 -0
  36. gsuite_cli/services/cloud_identity.py +187 -0
  37. gsuite_cli/services/cloud_search.py +75 -0
  38. gsuite_cli/services/diagnostics.py +250 -0
  39. gsuite_cli/services/docs.py +472 -0
  40. gsuite_cli/services/docs_advanced.py +601 -0
  41. gsuite_cli/services/drive.py +377 -0
  42. gsuite_cli/services/drive_activity.py +76 -0
  43. gsuite_cli/services/forms.py +364 -0
  44. gsuite_cli/services/gmail.py +1252 -0
  45. gsuite_cli/services/gmail_advanced.py +513 -0
  46. gsuite_cli/services/gmail_commands.py +737 -0
  47. gsuite_cli/services/meet.py +800 -0
  48. gsuite_cli/services/people.py +122 -0
  49. gsuite_cli/services/profile_resolver.py +15 -0
  50. gsuite_cli/services/reminders.py +243 -0
  51. gsuite_cli/services/resource_resolver.py +706 -0
  52. gsuite_cli/services/sheets.py +517 -0
  53. gsuite_cli/services/sheets_advanced.py +638 -0
  54. gsuite_cli/services/tasks.py +258 -0
  55. gsuite_cli/services/workspace_commands.py +98 -0
  56. gsuite_cli/services/workspace_events.py +122 -0
  57. gsuite_cli/ui/__init__.py +7 -0
  58. gsuite_cli/ui/human_flows.py +1366 -0
  59. gsuite_cli/ui/interactive.py +2825 -0
  60. gsuite_cli/ui/interactive_calendar.py +708 -0
  61. gsuite_cli/ui/interactive_labels.py +1016 -0
  62. gsuite_cli/ui/interactive_profile.py +1243 -0
  63. gsuite_cli/utils/__init__.py +3 -0
  64. gsuite_cli/utils/cache.py +258 -0
  65. gsuite_cli/utils/errors.py +147 -0
  66. gsuite_cli/utils/formatters.py +412 -0
  67. gsuite_cli/utils/validator.py +266 -0
  68. hermes_gsuite_cli-0.2.0.dist-info/METADATA +333 -0
  69. hermes_gsuite_cli-0.2.0.dist-info/RECORD +73 -0
  70. hermes_gsuite_cli-0.2.0.dist-info/WHEEL +5 -0
  71. hermes_gsuite_cli-0.2.0.dist-info/entry_points.txt +2 -0
  72. hermes_gsuite_cli-0.2.0.dist-info/licenses/LICENSE +22 -0
  73. hermes_gsuite_cli-0.2.0.dist-info/top_level.txt +1 -0
gsuite_cli/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ """
2
+ HERMES CLI - Advanced CLI tool for Google Workspace services and Gemini AI
3
+ """
4
+
5
+ __version__ = "0.1.0"
6
+ __author__ = "NSakthivel"
7
+ __email__ = "nsakthiveldev@gmail.com"
@@ -0,0 +1,38 @@
1
+ """
2
+ AI-powered features for GSuite CLI
3
+ """
4
+
5
+ from .commands import ai
6
+ from .nlp import NaturalLanguageProcessor
7
+ from .analytics import AIAnalytics
8
+ from .summarizer import EmailSummarizer
9
+ from .query_engine import NaturalQueryEngine, ConversationContext
10
+ from .intent_schema import StructuredIntent, validate_structured_intent
11
+ from .date_parser import NaturalDateParser
12
+ from .tool_registry import (
13
+ ToolDefinition,
14
+ ToolRegistry,
15
+ ToolValidator,
16
+ ToolExecutor,
17
+ ToolExecutionResult,
18
+ get_default_registry,
19
+ )
20
+
21
+ __all__ = [
22
+ 'ai',
23
+ 'NaturalLanguageProcessor',
24
+ 'AIAnalytics',
25
+ 'EmailSummarizer',
26
+ 'NaturalQueryEngine',
27
+ 'ConversationContext',
28
+ 'StructuredIntent',
29
+ 'validate_structured_intent',
30
+ 'NaturalDateParser',
31
+ 'ToolDefinition',
32
+ 'ToolRegistry',
33
+ 'ToolValidator',
34
+ 'ToolExecutor',
35
+ 'ToolExecutionResult',
36
+ 'get_default_registry',
37
+ ]
38
+
@@ -0,0 +1,460 @@
1
+ """
2
+ AI-powered productivity analytics
3
+ """
4
+
5
+ import logging
6
+ from typing import Dict, List, Any, Optional
7
+ from datetime import datetime, timedelta
8
+ from collections import Counter, defaultdict
9
+ import json
10
+
11
+ from .gemini_client import GeminiClient
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class AIAnalytics:
17
+ """AI-powered productivity analytics and insights"""
18
+
19
+ def __init__(self, config_manager=None):
20
+ self.config_manager = config_manager
21
+ self.gemini_client = None
22
+
23
+ if config_manager:
24
+ ai_config = config_manager.get('ai')
25
+ if ai_config and ai_config.gemini_api_key:
26
+ self.gemini_client = GeminiClient(
27
+ api_key=ai_config.gemini_api_key,
28
+ model_name=ai_config.model_name
29
+ )
30
+
31
+ self.productivity_metrics = {
32
+ 'email_response_rate': 0.0,
33
+ 'meeting_frequency': 0.0,
34
+ 'peak_productivity_hours': [],
35
+ 'communication_patterns': {},
36
+ 'workload_trends': {}
37
+ }
38
+
39
+ def analyze_productivity(self,
40
+ emails: List[Dict[str, Any]],
41
+ events: List[Dict[str, Any]],
42
+ period: str = 'week') -> Dict[str, Any]:
43
+ """
44
+ Analyze productivity patterns from emails and calendar events
45
+
46
+ Args:
47
+ emails: List of email dictionaries
48
+ events: List of calendar event dictionaries
49
+ period: Analysis period ('day', 'week', 'month')
50
+
51
+ Returns:
52
+ Comprehensive productivity analysis
53
+ """
54
+ if not emails and not events:
55
+ return {'message': 'No data available for analysis'}
56
+
57
+ # Time-based analysis
58
+ time_analysis = self._analyze_time_patterns(emails, events, period)
59
+
60
+ # Email analysis
61
+ email_analysis = self._analyze_email_patterns(emails, period)
62
+
63
+ # Calendar analysis
64
+ calendar_analysis = self._analyze_calendar_patterns(events, period)
65
+
66
+ # Generate insights
67
+ insights = self._generate_productivity_insights(
68
+ time_analysis, email_analysis, calendar_analysis
69
+ )
70
+
71
+ # Calculate productivity score
72
+ productivity_score = self._calculate_productivity_score(
73
+ email_analysis, calendar_analysis, time_analysis
74
+ )
75
+
76
+ recommendations = self._generate_recommendations(insights)
77
+
78
+ # Enhance with Gemini if available
79
+ if self.gemini_client and self.gemini_client.is_available:
80
+ try:
81
+ gemini_analysis = self._analyze_with_gemini(
82
+ email_analysis, calendar_analysis, time_analysis, period
83
+ )
84
+ if gemini_analysis:
85
+ # Append Gemini insights and recommendations
86
+ if 'insights' in gemini_analysis:
87
+ insights.extend(gemini_analysis['insights'])
88
+ if 'recommendations' in gemini_analysis:
89
+ # Prioritize Gemini recommendations
90
+ recommendations = gemini_analysis['recommendations'] + recommendations
91
+ except Exception as e:
92
+ logger.error(f"Gemini analytics failed: {e}")
93
+
94
+ return {
95
+ 'period': period,
96
+ 'productivity_score': productivity_score,
97
+ 'time_analysis': time_analysis,
98
+ 'email_analysis': email_analysis,
99
+ 'calendar_analysis': calendar_analysis,
100
+ 'insights': insights,
101
+ 'recommendations': recommendations[:5] # Limit to top 5
102
+ }
103
+
104
+ def _analyze_with_gemini(self,
105
+ email_stats: Dict[str, Any],
106
+ calendar_stats: Dict[str, Any],
107
+ time_stats: Dict[str, Any],
108
+ period: str) -> Optional[Dict[str, Any]]:
109
+ """Analyze productivity stats using Gemini"""
110
+ prompt = f'''
111
+ Analyze the following productivity statistics for the {period}.
112
+
113
+ Email Stats: {json.dumps(email_stats, default=str)}
114
+ Calendar Stats: {json.dumps(calendar_stats, default=str)}
115
+ Time Stats: {json.dumps(time_stats, default=str)}
116
+
117
+ Return ONLY a JSON object with:
118
+ - insights: list of deep insights about working habits and productivity
119
+ - recommendations: list of specific, actionable recommendations to improve productivity
120
+ '''
121
+
122
+ response = self.gemini_client.generate_content(prompt)
123
+ if not response:
124
+ return None
125
+
126
+ try:
127
+ # Clean up response
128
+ text = response.replace('```json', '').replace('```', '').strip()
129
+ start_idx = text.find('{')
130
+ end_idx = text.rfind('}') + 1
131
+ if start_idx != -1 and end_idx != -1:
132
+ return json.loads(text[start_idx:end_idx])
133
+ except Exception as e:
134
+ logger.error(f"Error parsing Gemini response: {e}")
135
+ return None
136
+ return None
137
+
138
+ def _analyze_time_patterns(self,
139
+ emails: List[Dict[str, Any]],
140
+ events: List[Dict[str, Any]],
141
+ period: str) -> Dict[str, Any]:
142
+ """Analyze time-based patterns"""
143
+ hour_activity = defaultdict(int)
144
+ day_activity = defaultdict(int)
145
+
146
+ # Analyze email timestamps
147
+ for email in emails:
148
+ date_str = email.get('date', '')
149
+ if date_str:
150
+ try:
151
+ # Parse date (simplified)
152
+ if 'T' in date_str:
153
+ datetime_part = date_str.split('T')[1]
154
+ hour = int(datetime_part.split(':')[0])
155
+ else:
156
+ hour = 12 # Default to noon if no time
157
+
158
+ hour_activity[hour] += 1
159
+ day_activity['email'] += 1
160
+ except:
161
+ continue
162
+
163
+ # Analyze event times
164
+ for event in events:
165
+ start_str = event.get('start', '')
166
+ if start_str:
167
+ try:
168
+ if 'T' in start_str:
169
+ datetime_part = start_str.split('T')[1]
170
+ hour = int(datetime_part.split(':')[0])
171
+ else:
172
+ hour = 12 # Default to noon if no time
173
+
174
+ hour_activity[hour] += 1
175
+ day_activity['events'] += 1
176
+ except:
177
+ continue
178
+
179
+ # Find peak hours
180
+ if hour_activity:
181
+ peak_hours = sorted(hour_activity.items(), key=lambda x: x[1], reverse=True)[:3]
182
+ peak_hours = [f"{hour}:00" for hour, count in peak_hours]
183
+ else:
184
+ peak_hours = []
185
+
186
+ return {
187
+ 'peak_hours': peak_hours,
188
+ 'hourly_distribution': dict(hour_activity),
189
+ 'daily_breakdown': dict(day_activity)
190
+ }
191
+
192
+ def _analyze_email_patterns(self, emails: List[Dict[str, Any]], period: str) -> Dict[str, Any]:
193
+ """Analyze email communication patterns"""
194
+ if not emails:
195
+ return {'total': 0}
196
+
197
+ total_emails = len(emails)
198
+
199
+ # Sender analysis
200
+ senders = [email.get('from', '') for email in emails]
201
+ sender_counts = Counter(senders)
202
+ top_senders = sender_counts.most_common(5)
203
+
204
+ # Subject analysis
205
+ subjects = [email.get('subject', '') for email in emails]
206
+ subject_lengths = [len(s) for s in subjects if s]
207
+ avg_subject_length = sum(subject_lengths) / len(subject_lengths) if subject_lengths else 0
208
+
209
+ # Time-based patterns
210
+ recent_emails = 0
211
+ older_emails = 0
212
+
213
+ for email in emails:
214
+ date_str = email.get('date', '')
215
+ if date_str:
216
+ try:
217
+ # Simple date parsing
218
+ if '2026' in date_str: # Current year
219
+ recent_emails += 1
220
+ else:
221
+ older_emails += 1
222
+ except:
223
+ continue
224
+
225
+ return {
226
+ 'total': total_emails,
227
+ 'top_senders': top_senders,
228
+ 'avg_subject_length': round(avg_subject_length, 1),
229
+ 'recent_emails': recent_emails,
230
+ 'older_emails': older_emails,
231
+ 'emails_per_day': round(total_emails / 7, 1) if period == 'week' else total_emails
232
+ }
233
+
234
+ def _analyze_calendar_patterns(self, events: List[Dict[str, Any]], period: str) -> Dict[str, Any]:
235
+ """Analyze calendar event patterns"""
236
+ if not events:
237
+ return {'total': 0}
238
+
239
+ total_events = len(events)
240
+
241
+ # Event types by keywords
242
+ meeting_events = 0
243
+ personal_events = 0
244
+ other_events = 0
245
+
246
+ for event in events:
247
+ title = event.get('summary', '').lower()
248
+ if any(keyword in title for keyword in ['meeting', 'call', 'conference', 'sync']):
249
+ meeting_events += 1
250
+ elif any(keyword in title for keyword in ['birthday', 'personal', 'holiday']):
251
+ personal_events += 1
252
+ else:
253
+ other_events += 1
254
+
255
+ # Duration analysis (simplified)
256
+ avg_duration_hours = 1.0 # Default assumption
257
+
258
+ return {
259
+ 'total': total_events,
260
+ 'meetings': meeting_events,
261
+ 'personal': personal_events,
262
+ 'other': other_events,
263
+ 'avg_duration_hours': avg_duration_hours,
264
+ 'events_per_day': round(total_events / 7, 1) if period == 'week' else total_events
265
+ }
266
+
267
+ def _generate_productivity_insights(self,
268
+ time_analysis: Dict[str, Any],
269
+ email_analysis: Dict[str, Any],
270
+ calendar_analysis: Dict[str, Any]) -> List[str]:
271
+ """Generate AI-powered insights from data"""
272
+ insights = []
273
+
274
+ # Email insights
275
+ if email_analysis.get('total', 0) > 50:
276
+ insights.append("High email volume - consider batching responses")
277
+
278
+ if email_analysis.get('top_senders'):
279
+ top_sender = email_analysis['top_senders'][0]
280
+ insights.append(f"Most communication with {top_sender[0]} ({top_sender[1]} emails)")
281
+
282
+ # Calendar insights
283
+ if calendar_analysis.get('meetings', 0) > 10:
284
+ insights.append("Heavy meeting schedule - ensure focus time")
285
+
286
+ if calendar_analysis.get('personal', 0) > 5:
287
+ insights.append("Good work-life balance with personal events")
288
+
289
+ # Time insights
290
+ if time_analysis.get('peak_hours'):
291
+ peak_hour = time_analysis['peak_hours'][0]
292
+ insights.append(f"Peak activity around {peak_hour}")
293
+
294
+ # Communication patterns
295
+ total_emails = email_analysis.get('total', 0)
296
+ total_events = calendar_analysis.get('total', 0)
297
+
298
+ if total_emails > total_events * 5:
299
+ insights.append("Email-heavy communication pattern")
300
+ elif total_events > total_emails * 2:
301
+ insights.append("Meeting-heavy schedule")
302
+ else:
303
+ insights.append("Balanced communication approach")
304
+
305
+ return insights
306
+
307
+ def _calculate_productivity_score(self,
308
+ email_analysis: Dict[str, Any],
309
+ calendar_analysis: Dict[str, Any],
310
+ time_analysis: Dict[str, Any]) -> float:
311
+ """Calculate overall productivity score (0-100)"""
312
+ score = 50 # Base score
313
+
314
+ # Email productivity factors
315
+ email_total = email_analysis.get('total', 0)
316
+ if 10 <= email_total <= 30: # Optimal range
317
+ score += 10
318
+ elif email_total > 50: # Too many emails
319
+ score -= 10
320
+
321
+ # Meeting productivity factors
322
+ meetings = calendar_analysis.get('meetings', 0)
323
+ if 5 <= meetings <= 15: # Optimal range
324
+ score += 10
325
+ elif meetings > 20: # Too many meetings
326
+ score -= 15
327
+
328
+ # Work-life balance
329
+ personal_events = calendar_analysis.get('personal', 0)
330
+ if personal_events > 0:
331
+ score += 5
332
+
333
+ # Time distribution
334
+ peak_hours = time_analysis.get('peak_hours', [])
335
+ if len(peak_hours) > 0:
336
+ score += 5
337
+
338
+ return max(0, min(100, score))
339
+
340
+ def _generate_recommendations(self, insights: List[str]) -> List[str]:
341
+ """Generate AI-powered recommendations"""
342
+ recommendations = []
343
+
344
+ insight_text = ' '.join(insights).lower()
345
+
346
+ if 'high email volume' in insight_text:
347
+ recommendations.append("Schedule specific email checking times (2-3x per day)")
348
+ recommendations.append("Use email templates for common responses")
349
+
350
+ if 'heavy meeting schedule' in insight_text:
351
+ recommendations.append("Block focus time between meetings")
352
+ recommendations.append("Evaluate if all meetings are necessary")
353
+
354
+ if 'peak activity' in insight_text:
355
+ recommendations.append("Schedule important tasks during peak hours")
356
+
357
+ if 'email-heavy' in insight_text:
358
+ recommendations.append("Consider more direct communication methods")
359
+ recommendations.append("Use chat or calls for quick discussions")
360
+
361
+ if 'meeting-heavy' in insight_text:
362
+ recommendations.append("Send agendas in advance to reduce meeting time")
363
+ recommendations.append("Consider async updates instead of meetings")
364
+
365
+ if not recommendations:
366
+ recommendations.append("Maintain current productivity patterns")
367
+ recommendations.append("Continue balancing email and meeting communication")
368
+
369
+ return recommendations[:5] # Return top 5 recommendations
370
+
371
+ def generate_weekly_report(self,
372
+ emails: List[Dict[str, Any]],
373
+ events: List[Dict[str, Any]]) -> Dict[str, Any]:
374
+ """Generate comprehensive weekly productivity report"""
375
+ analysis = self.analyze_productivity(emails, events, 'week')
376
+
377
+ # Add weekly-specific metrics
378
+ week_start = datetime.now() - timedelta(days=datetime.now().weekday())
379
+ week_end = week_start + timedelta(days=6)
380
+
381
+ return {
382
+ 'report_period': f"{week_start.strftime('%Y-%m-%d')} to {week_end.strftime('%Y-%m-%d')}",
383
+ 'executive_summary': self._generate_executive_summary(analysis),
384
+ 'detailed_analysis': analysis,
385
+ 'achievements': self._extract_achievements(events),
386
+ 'challenges': self._identify_challenges(emails, events),
387
+ 'next_week_priorities': self._suggest_priorities(analysis)
388
+ }
389
+
390
+ def _generate_executive_summary(self, analysis: Dict[str, Any]) -> str:
391
+ """Generate executive summary for weekly report"""
392
+ score = analysis.get('productivity_score', 50)
393
+ insights = analysis.get('insights', [])
394
+
395
+ if score >= 80:
396
+ performance = "excellent"
397
+ elif score >= 60:
398
+ performance = "good"
399
+ elif score >= 40:
400
+ performance = "moderate"
401
+ else:
402
+ performance = "needs improvement"
403
+
404
+ summary = f"Weekly productivity was {performance} (score: {score}/100). "
405
+
406
+ if insights:
407
+ summary += f"Key insights: {insights[0].lower()}"
408
+ if len(insights) > 1:
409
+ summary += f". {insights[1].lower()}"
410
+
411
+ return summary
412
+
413
+ def _extract_achievements(self, events: List[Dict[str, Any]]) -> List[str]:
414
+ """Extract achievements from calendar events"""
415
+ achievements = []
416
+
417
+ for event in events:
418
+ title = event.get('summary', '').lower()
419
+ if any(keyword in title for keyword in ['completed', 'finished', 'done', 'achieved']):
420
+ achievements.append(event.get('summary', ''))
421
+
422
+ return achievements[:3] # Top 3 achievements
423
+
424
+ def _identify_challenges(self, emails: List[Dict[str, Any]], events: List[Dict[str, Any]]) -> List[str]:
425
+ """Identify challenges from emails and events"""
426
+ challenges = []
427
+
428
+ # Look for challenge indicators in emails
429
+ for email in emails:
430
+ subject = email.get('subject', '').lower()
431
+ if any(keyword in subject for keyword in ['problem', 'issue', 'delay', 'urgent']):
432
+ challenges.append(email.get('subject', ''))
433
+
434
+ # Look for schedule conflicts
435
+ if len(events) > 20:
436
+ challenges.append("Heavy schedule with many commitments")
437
+
438
+ return challenges[:3] # Top 3 challenges
439
+
440
+ def _suggest_priorities(self, analysis: Dict[str, Any]) -> List[str]:
441
+ """Suggest priorities for next week"""
442
+ priorities = []
443
+
444
+ recommendations = analysis.get('recommendations', [])
445
+ insights = analysis.get('insights', [])
446
+
447
+ # Convert recommendations to priorities
448
+ for rec in recommendations[:3]:
449
+ if 'schedule' in rec.lower():
450
+ priorities.append(f"Schedule: {rec}")
451
+ elif 'consider' in rec.lower():
452
+ priorities.append(f"Consider: {rec}")
453
+ else:
454
+ priorities.append(f"Action: {rec}")
455
+
456
+ if not priorities:
457
+ priorities.append("Maintain current productivity habits")
458
+ priorities.append("Focus on high-impact activities")
459
+
460
+ return priorities
@@ -0,0 +1,48 @@
1
+ """
2
+ AI Chatbot service for GSuite CLI - Gemini Powered (v3)
3
+ """
4
+
5
+ import logging
6
+ from typing import Optional
7
+ from google import genai
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+ class AIChatBot:
12
+ """Chatbot service powered by Google Gemini (New SDK)"""
13
+
14
+ def __init__(self, gemini_key: str = '', model_name: str = 'gemini-3.6-flash'):
15
+ self.gemini_key = gemini_key
16
+ self.model_name = model_name
17
+ self._client = None
18
+
19
+ @property
20
+ def client(self):
21
+ """Lazy initialization of the Gemini client"""
22
+ if self._client is None and self.gemini_key:
23
+ self._client = genai.Client(api_key=self.gemini_key)
24
+ return self._client
25
+
26
+ def chat(self, message: str) -> str:
27
+ """Send a message to Gemini and get a response using the new SDK"""
28
+ if not self.gemini_key:
29
+ return "❌ Gemini API key not configured. Set it with 'hermes config set ai.gemini_api_key YOUR_KEY'"
30
+
31
+ try:
32
+ if not self.client:
33
+ return "❌ Failed to initialize Gemini client."
34
+
35
+ response = self.client.models.generate_content(
36
+ model=self.model_name,
37
+ contents=message
38
+ )
39
+
40
+ if response and response.text:
41
+ return response.text
42
+ return "❌ No response from Gemini"
43
+
44
+ except Exception as e:
45
+ logger.error(f"Gemini SDK error: {e}")
46
+ # If the specific v3 preview model isn't available, try a fallback if it makes sense,
47
+ # but usually for a preview user, we want to show the specific error.
48
+ return f"❌ Gemini AI Error: {str(e)}"