walkerhsu commited on
Commit
c74033b
·
1 Parent(s): 132b0f4
agents/__pycache__/gmail_agent.cpython-313.pyc ADDED
Binary file (5.61 kB). View file
 
agents/__pycache__/google_calendar_agent.cpython-313.pyc ADDED
Binary file (12 kB). View file
 
agents/gmail_agent.py ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+ import json
3
+ import base64
4
+ from googleapiclient.discovery import build
5
+ from utils.auth import authenticate_google_services
6
+
7
+ class Gmail_Agent:
8
+ def __init__(self, creds=None):
9
+ if not creds:
10
+ creds = authenticate_google_services()
11
+ self.service = build('gmail', 'v1', credentials=creds)
12
+
13
+ def read_gmail_messages(self, queries, max_results=5):
14
+ query = " ".join(queries)
15
+ results = self.service.users().messages().list(userId='me', maxResults=max_results, q=query).execute()
16
+ messages = results.get('messages', [])
17
+ return messages
18
+
19
+ def format_messages(self, messages):
20
+ formatted_content = "\n\n==========\n\n"
21
+ for message in messages:
22
+ msg = self.service.users().messages().get(userId='me', id=message['id']).execute()
23
+ import json
24
+ with open('results.json', 'w', encoding='utf-8') as f:
25
+ json.dump(msg, f)
26
+ internal_Date = int(msg['internalDate'])
27
+ formatted_Date = datetime.fromtimestamp(internal_Date / 1000).strftime('%Y-%m-%d %H:%M:%S')
28
+ formatted_content += f"信件日期: {formatted_Date}\n"
29
+ content = self._parse_gmail_response(msg['payload']['parts'])
30
+ formatted_content+=f"信件內容: {content}\n\n==========\n\n"
31
+ return formatted_content
32
+
33
+ def send_email(self, to, subject, message_content):
34
+ try:
35
+ from email.message import EmailMessage
36
+ message = EmailMessage()
37
+ message.set_content(message_content)
38
+ message["To"] = ", ".join(to)
39
+ message["Subject"] = subject
40
+ encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
41
+ create_message = {"raw": encoded_message}
42
+ send_message = (
43
+ self.service.users()
44
+ .messages()
45
+ .send(userId="me", body=create_message)
46
+ .execute()
47
+ )
48
+ print(f'Message Id: {send_message["id"]}')
49
+ formatted_content = "✅ Gmail 已傳送給所有使用者,請至您的 Gmail 寄件備份確認!"
50
+ except Exception as error:
51
+ print(f"An error occurred: {error}")
52
+ send_message = None
53
+ formatted_content = f"❌ 信件未能成功寄出,以下是錯誤訊息(英文):{error}"
54
+ return formatted_content
55
+
56
+ def get_all_tools(self):
57
+ LLM_tools = []
58
+ LLM_tools.append({
59
+ "name": "query_gmail_tool",
60
+ "description": "query the gmails with specified queries",
61
+ "parameters": {
62
+ "type": "object",
63
+ "properties": {
64
+ "queries": {
65
+ "type": "array",
66
+ "items": {"type": "string"},
67
+ "description": "Queries that can be used to filter the gmails. (e.g., ['before:2025/06/01', 'after:2025/05/01', 'from:[email protected]'])",
68
+ },
69
+ },
70
+ "required": ["queries"]
71
+ },
72
+ })
73
+ LLM_tools.append({
74
+ "name": "send_email_tool",
75
+ "description": "Send an email to a specified recipient with a specified subject and message",
76
+ "parameters": {
77
+ "type": "object",
78
+ "properties": {
79
+ "to": {
80
+ "type": "array",
81
+ "items": {"type": "string"},
82
+ "description": "An array of email addresses of all the recipients"
83
+ },
84
+ "subject": {
85
+ "type": "string",
86
+ "description": "The subject of the email"
87
+ },
88
+ "message_content": {
89
+ "type": "string",
90
+ "description": "The content of the email"
91
+ }
92
+ },
93
+ "required": ["to", "subject", "message_content"]
94
+ }
95
+ })
96
+ return LLM_tools
97
+
98
+ def _parse_gmail_response(self, parts):
99
+ for part in parts:
100
+ if "parts" in part:
101
+ content = self._parse_gmail_response(part["parts"])
102
+ if content:
103
+ return content
104
+ if part['mimeType'] == 'text/plain' and part['body']['size'] != 0:
105
+ decoded_data = base64.urlsafe_b64decode(part['body']['data']).decode('utf-8')
106
+ return decoded_data
107
+ return None
agents/google_calendar_agent.py ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from googleapiclient.discovery import build
2
+ from datetime import datetime, timedelta
3
+ from utils.auth import authenticate_google_services
4
+
5
+ class GoogleCalendar_Agent:
6
+ def __init__(self, creds=None, timezone="Asia/Taipei"):
7
+ if not creds:
8
+ creds = authenticate_google_services()
9
+ self.service = build("calendar", "v3", credentials=creds)
10
+ self.timezone = timezone
11
+
12
+ def list_events(self, n: int=5, queries=None) -> str:
13
+ now = datetime.utcnow().isoformat() + 'Z'
14
+ events_result = self.service.events().list(
15
+ calendarId='primary',
16
+ timeMin=now,
17
+ maxResults=n,
18
+ singleEvents=True,
19
+ orderBy='startTime'
20
+ ).execute()
21
+ events = events_result.get('items', [])
22
+ if not events:
23
+ return "🔕 找不到即將到來的行事曆事件。"
24
+ output = "📅 您接下來的行事曆事件如下:\n"
25
+ for event in events:
26
+ start = event['start'].get('dateTime', event['start'].get('date'))
27
+ summary = event.get('summary', 'No title')
28
+ output += f"• {summary} at {start}\n"
29
+ return output
30
+
31
+ def create_event(self,
32
+ summary: str,
33
+ start_time: str,
34
+ end_time: str,
35
+ location: str = None,
36
+ description: str = None,
37
+ attendees: list = None,
38
+ reminder_minutes: int = None) -> str:
39
+ event = {
40
+ 'summary': summary,
41
+ 'start': {'dateTime': start_time, 'timeZone': self.timezone},
42
+ 'end': {'dateTime': end_time, 'timeZone': self.timezone}
43
+ }
44
+ if location:
45
+ event['location'] = location
46
+ if description:
47
+ event['description'] = description
48
+ if attendees:
49
+ event['attendees'] = [{'email': email} for email in attendees]
50
+ if reminder_minutes:
51
+ event['reminders'] = {
52
+ 'useDefault': False,
53
+ 'overrides': [{'method': 'popup', 'minutes': reminder_minutes}]
54
+ }
55
+ try:
56
+ created_event = self.service.events().insert(
57
+ calendarId='primary',
58
+ sendUpdates='all',
59
+ body=event
60
+ ).execute()
61
+ return f"""✅ 已成功建立事件:\n標題 : {summary}\n開始時間 : {start_time}\n結束時間 : {end_time}\n地點 : {location or '無'}\n說明 : {description or '無'}\n參與者 : {', '.join(attendees) if attendees else '無'}\n提醒時間 : {reminder_minutes if reminder_minutes is not None else '使用預設提醒'}\n時區 : {self.timezone}\n事件連結 : {created_event.get('htmlLink')}"""
62
+ except Exception as e:
63
+ return f"❌ 建立事件失敗,以下是錯誤訊息(英文): {e}"
64
+
65
+ def find_event(self, query: str,) -> list:
66
+ now = datetime.utcnow().isoformat() + 'Z'
67
+ events_result = self.service.events().list(
68
+ calendarId='primary',
69
+ timeMin=now,
70
+ singleEvents=True,
71
+ orderBy='startTime',
72
+ q=query
73
+ ).execute()
74
+ return events_result.get('items', [])
75
+
76
+ def update_event(self, query: str,
77
+ new_summary=None,
78
+ new_location=None,
79
+ new_description=None,
80
+ new_start=None,
81
+ new_end=None,
82
+ new_attendees=None,
83
+ reminder_minutes=None):
84
+ events = self.find_event(query)
85
+ if not events:
86
+ return f"❌ 找不到包含「{query}」的事件"
87
+ if all(arg is None for arg in [
88
+ new_summary, new_location, new_description,
89
+ new_start, new_end, new_attendees, reminder_minutes
90
+ ]):
91
+ return f"⚠️ 沒有指定任何要更新的欄位,未執行更新。"
92
+ for event in events:
93
+ event_id = event['id']
94
+ try:
95
+ if new_summary:
96
+ event['summary'] = new_summary
97
+ if new_location:
98
+ event['location'] = new_location
99
+ if new_description:
100
+ event['description'] = new_description
101
+ if new_attendees:
102
+ event['attendees'] = [{'email': email} for email in new_attendees]
103
+ if reminder_minutes:
104
+ event['reminders'] = {
105
+ 'useDefault': False,
106
+ 'overrides': [{'method': 'popup', 'minutes': reminder_minutes}]
107
+ }
108
+ if new_start or new_end:
109
+ event['start']['dateTime'] = new_start
110
+ event['end']['dateTime'] = new_end
111
+ event['start']['timeZone'] = 'Asia/Taipei'
112
+ event['end']['timeZone'] = 'Asia/Taipei'
113
+ updated_event = self.service.events().update(
114
+ calendarId='primary',
115
+ eventId=event_id,
116
+ sendUpdates='all',
117
+ body=event
118
+ ).execute()
119
+ return f"✅ 已成功更新事件:{updated_event['summary']}"
120
+ except Exception as e:
121
+ return f"❌ 更新事件失敗,以下是錯誤訊息(英文): {e}"
122
+
123
+ def delete_event(self, query: str):
124
+ now = datetime.utcnow().isoformat() + 'Z'
125
+ event_results = self.find_event(query)
126
+ if len(event_results) == 0:
127
+ return f"❌ 找不到包含「{query}」的事件"
128
+ for event in event_results:
129
+ target_id = event["id"]
130
+ try:
131
+ self.service.events().delete(
132
+ calendarId='primary',
133
+ eventId=target_id,
134
+ sendUpdates='all'
135
+ ).execute()
136
+ return f"🗑️ 已刪除事件:{event['summary']}(ID: {target_id})"
137
+ except Exception as e:
138
+ return f"❌ 刪除事件失敗(ID: {target_id}),以下是錯誤訊息(英文): {e}"
139
+
140
+ def get_event_details(self, query: str):
141
+ events = self.find_event(query)
142
+ if not events:
143
+ return f"❌ 找不到符合 '{query}' 的事件!"
144
+ event = events[0]
145
+ summary = event.get('summary', 'No Title')
146
+ start_time = event['start'].get('dateTime', event['start'].get('date'))
147
+ end_time = event['end'].get('dateTime', event['end'].get('date'))
148
+ formatted_string = f"事件: {summary}\n開始時間: {start_time}\n結束時間: {end_time}"
149
+ attendees = event.get('attendees', [])
150
+ emails = [attendee.get('email') for attendee in attendees if attendee.get('email')]
151
+ if emails:
152
+ formatted_string += f"\n參與者: {', '.join(emails)}"
153
+ else:
154
+ formatted_string += "\n無參與者"
155
+ return formatted_string
156
+
157
+ def get_all_tools(self):
158
+ LLM_tools = []
159
+ LLM_tools.append({
160
+ "name": "create_Calendar_event",
161
+ "description": "Create a new Google Calendar event by specifying summary, start time, and end time, and optionally location, description, attendees, and reminder.",
162
+ "parameters": {
163
+ "type": "object",
164
+ "properties": {
165
+ "summary": {
166
+ "type": "string",
167
+ "description": "Title or summary of the event (e.g., 'Lunch with Alice')"
168
+ },
169
+ "start_time": {
170
+ "type": "string",
171
+ "format": "date-time",
172
+ "description": "Start time of the event in ISO format (e.g., '2025-07-01T14:00:00')"
173
+ },
174
+ "end_time": {
175
+ "type": "string",
176
+ "format": "date-time",
177
+ "description": "End time of the event in ISO format (e.g., '2025-07-01T15:00:00')"
178
+ },
179
+ "location": {
180
+ "type": "string",
181
+ "description": "Location of the event (e.g., 'Taipei 101')"
182
+ },
183
+ "description": {
184
+ "type": "string",
185
+ "description": "Detailed description of the event"
186
+ },
187
+ "attendees": {
188
+ "type": "array",
189
+ "items": {"type": "string"},
190
+ "description": "List of email addresses to invite to the event"
191
+ },
192
+ "reminder_minutes": {
193
+ "type": "integer",
194
+ "description": "Number of minutes before the event to trigger a popup reminder"
195
+ }
196
+ },
197
+ "required": ["summary", "start_time", "end_time"]
198
+ },
199
+ })
200
+ LLM_tools.append({
201
+ "name": "list_Calendar_events",
202
+ "description": "List your upcoming Google Calendar events.",
203
+ "parameters": {
204
+ "type": "object",
205
+ "properties": {
206
+ "n": {
207
+ "type": "integer",
208
+ "default": 5,
209
+ "description": "Number of upcoming events to list (default: 5)"
210
+ }
211
+ }
212
+ }
213
+ })
214
+ LLM_tools.append({
215
+ "name": "update_Calendar_event",
216
+ "description": "Update a Google Calendar event using a keyword query to find it. You can update summary, location, description, start/end time, attendees, or reminders.",
217
+ "parameters": {
218
+ "type": "object",
219
+ "properties": {
220
+ "query": {
221
+ "type": "string",
222
+ "description": "Keyword to find the event to update (e.g., 'meeting with Bob')"
223
+ },
224
+ "new_summary": {
225
+ "type": "string",
226
+ "description": "New summary/title for the event"
227
+ },
228
+ "new_location": {
229
+ "type": "string",
230
+ "description": "New location for the event"
231
+ },
232
+ "new_description": {
233
+ "type": "string",
234
+ "description": "New description for the event"
235
+ },
236
+ "new_start": {
237
+ "type": "string",
238
+ "format": "date-time",
239
+ "description": "New start time in ISO format"
240
+ },
241
+ "new_end": {
242
+ "type": "string",
243
+ "format": "date-time",
244
+ "description": "New end time in ISO format"
245
+ },
246
+ "new_attendees": {
247
+ "type": "array",
248
+ "items": {"type": "string"},
249
+ "description": "New list of attendees (email addresses)"
250
+ },
251
+ "reminder_minutes": {
252
+ "type": "integer",
253
+ "description": "Updated popup reminder time in minutes"
254
+ }
255
+ },
256
+ "required": ["query"]
257
+ }
258
+ })
259
+ LLM_tools.append({
260
+ "name": "delete_Calendar_event",
261
+ "description": "Delete a Google Calendar event using a keyword query to find it.",
262
+ "parameters": {
263
+ "type": "object",
264
+ "properties": {
265
+ "query": {
266
+ "type": "string",
267
+ "description": "Keyword to find the event to delete (e.g., 'meeting with Bob')"
268
+ }
269
+ },
270
+ "required": ["query"]
271
+ }
272
+ })
273
+ LLM_tools.append({
274
+ "name": "get_event_details",
275
+ "description": "Find the attendee email addresses and the summary of a specific event, based on a keyword.",
276
+ "parameters": {
277
+ "type": "object",
278
+ "properties": {
279
+ "query": {
280
+ "type": "string",
281
+ "description": "Keyword to find the event and extract attendees and summary of that event."
282
+ }
283
+ },
284
+ "required": ["query"]
285
+ }
286
+ })
287
+ return LLM_tools
app.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ from utils.auth import authenticate_google_services
4
+ from utils.llm import Google_Gemini_LLM
5
+ from agents.gmail_agent import Gmail_Agent
6
+ from agents.google_calendar_agent import GoogleCalendar_Agent
7
+
8
+ # Authenticate and initialize agents
9
+ creds = authenticate_google_services()
10
+ GMAIL_AGENT = Gmail_Agent(creds)
11
+ CALENDAR_Agent = GoogleCalendar_Agent(creds)
12
+
13
+ # Initialize LLM
14
+ # Use os.environ.get instead of userdata.get
15
+ GOOGLE_API_KEY = os.environ.get('GOOGLE_API_KEY')
16
+ if not GOOGLE_API_KEY:
17
+ raise ValueError("GOOGLE_API_KEY environment variable not set")
18
+
19
+ MODEL = "gemini-1.5-flash"
20
+ YOUR_AI_AGENT = Google_Gemini_LLM(GOOGLE_API_KEY, MODEL)
21
+ YOUR_AI_AGENT.append_function_tools(GMAIL_AGENT.get_all_tools())
22
+ YOUR_AI_AGENT.append_function_tools(CALENDAR_Agent.get_all_tools())
23
+
24
+ def generate_response(user_prompt, history):
25
+ response = YOUR_AI_AGENT.generate_content(user_prompt)
26
+ while(response.function_call):
27
+ function_name = response.function_call.name
28
+ function_args = response.function_call.args
29
+ if function_name == "query_gmail_tool":
30
+ messages = GMAIL_AGENT.read_gmail_messages(**function_args)
31
+ if not messages:
32
+ formatted_content = "❌ 找不到對應的 Gmail,請確認您的搜尋條件是否合理,或者請重新執行!"
33
+ return formatted_content
34
+ else:
35
+ formatted_content = GMAIL_AGENT.format_messages(messages)
36
+ user_prompt += f"\n根據使用者的搜尋條件,以下是對應的信件內容(可能有多封信件):<信件內容開始>{formatted_content}<信件內容結束>. 請仔細閱讀以上的信件內容,並將資料整理統整給使用者!請以簡潔且新處的方式傳達內容!"
37
+ elif function_name == "send_email_tool":
38
+ formatted_content = GMAIL_AGENT.send_email(**function_args)
39
+ return formatted_content
40
+ elif function_name == "list_Calendar_events":
41
+ formatted_content = CALENDAR_Agent.list_events(**function_args)
42
+ user_prompt += f"\n根據使用者的搜尋條件,以下是從使用者的日曆中找到對應的事件內容(可能有多封信件): <事件內容開始>{formatted_content}<事件內容結束>. 請仔細閱讀以上的事件內容,並將資料統整給使用者!請以簡潔且新處的方式傳達內容!"
43
+ elif function_name == "create_Calendar_event":
44
+ formatted_content = CALENDAR_Agent.create_event(**function_args)
45
+ return formatted_content
46
+ elif function_name == "update_Calendar_event":
47
+ formatted_content = CALENDAR_Agent.update_event(**function_args)
48
+ return formatted_content
49
+ elif function_name == "delete_Calendar_event":
50
+ formatted_content = CALENDAR_Agent.delete_event(**function_args)
51
+ return formatted_content
52
+ elif function_name == "get_event_details":
53
+ formatted_content = CALENDAR_Agent.get_event_details(**function_args)
54
+ user_prompt += f"\n根據使用者的搜尋條件,以下是從使用者的日曆中找到對應的事件內容 <事件內容開始>{formatted_content}<事件內容結束>. 請仔細閱讀以上的事件內容,並且完成使用者的指示(例如:統整事件內容、寄送提醒信件給參與事件的人...等等)!"
55
+ else:
56
+ raise NotImplementedError(f"Function Call {function_name} is not supported yet.")
57
+ response = YOUR_AI_AGENT.generate_content(user_prompt)
58
+ return response.text
59
+
60
+ # It's better to get the email from the user's input
61
+ # than to hardcode it.
62
+ demo = gr.ChatInterface(
63
+ fn=generate_response,
64
+ chatbot=gr.Chatbot(
65
+ label="📬 AI 助理",
66
+ ),
67
+ title="Gmail & Calendar 智慧助理",
68
+ description="輸入自然語言,讓 AI 幫你管理 Gmail 或 Google 行事曆",
69
+ examples=[
70
+ "找出2025/6月標註星號的信件",
71
+ "請幫我列出近期的事件",
72
+ "建立2025/8/10早上10點的網球活動",
73
+ ]
74
+ )
75
+
76
+ if __name__ == "__main__":
77
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ google-api-python-client
2
+ google-auth-httplib2
3
+ google-auth-oauthlib
4
+ gradio
utils/__pycache__/auth.cpython-313.pyc ADDED
Binary file (1.58 kB). View file
 
utils/__pycache__/llm.cpython-313.pyc ADDED
Binary file (1.79 kB). View file
 
utils/auth.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import os, pickle
3
+ from google_auth_oauthlib.flow import InstalledAppFlow
4
+ from google.auth.transport.requests import Request
5
+
6
+ SCOPES = [
7
+ 'https://www.googleapis.com/auth/calendar',
8
+ 'https://www.googleapis.com/auth/gmail.readonly',
9
+ 'https://www.googleapis.com/auth/gmail.send'
10
+ ]
11
+
12
+ def authenticate_google_services():
13
+ creds = None
14
+ if os.path.exists('token.pkl'):
15
+ with open('token.pkl', 'rb') as token:
16
+ creds = pickle.load(token)
17
+ if not creds or not creds.valid:
18
+ if creds and creds.expired and creds.refresh_token:
19
+ creds.refresh(Request())
20
+ else:
21
+ flow = InstalledAppFlow.from_client_secrets_file('client_secret.json', SCOPES)
22
+ creds = flow.run_console()
23
+ with open('token.pkl', 'wb') as token:
24
+ pickle.dump(creds, token)
25
+ return creds
utils/llm.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import google.generativeai as genai
3
+ from google.generativeai import types
4
+
5
+ class Google_Gemini_LLM:
6
+ def __init__(self, api_key, model="gemini-1.5-flash"):
7
+ self.api_key = api_key
8
+ self.model = model
9
+ self.client = genai.Client(api_key=api_key)
10
+ self.LLM_tools = []
11
+
12
+ def generate_content(self, contents):
13
+ tools = types.Tool(function_declarations=self.LLM_tools)
14
+ config = types.GenerateContentConfig(tools=[tools])
15
+ response = self.client.models.generate_content(
16
+ model=self.model, contents=contents, config=config
17
+ )
18
+ return response.candidates[0].content.parts[0]
19
+
20
+ def append_function_tools(self, tools):
21
+ for tool in tools:
22
+ self.LLM_tools.append(tool)