Skip to content

Commit 5c8918f

Browse files
enhance: stabilize Gmail integration PR #522 (#819)
2 parents db99d19 + b819a7c commit 5c8918f

10 files changed

Lines changed: 164 additions & 427 deletions

File tree

backend/app/controller/tool_controller.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,43 @@ async def uninstall_tool(tool: str):
329329
oauth_state_manager._states.pop("google_calendar", None)
330330
logger.info("Cleared Google Calendar OAuth state cache")
331331

332+
return {
333+
"success": True,
334+
"message": f"Successfully uninstalled {tool} and cleaned up authentication tokens"
335+
}
336+
except Exception as e:
337+
logger.error(f"Failed to uninstall {tool}: {e}")
338+
raise HTTPException(
339+
status_code=500,
340+
detail=f"Failed to uninstall {tool}: {str(e)}"
341+
)
342+
elif tool == "google_gmail":
343+
try:
344+
# Clean up Google Gmail token directories (user-scoped + legacy)
345+
token_dirs = set()
346+
try:
347+
token_dirs.add(os.path.dirname(GoogleGmailNativeToolkit._build_canonical_token_path()))
348+
except Exception as e:
349+
logger.warning(f"Failed to resolve canonical Google Gmail token path: {e}")
350+
351+
token_dirs.add(os.path.join(os.path.expanduser("~"), ".eigent", "tokens", "google_gmail"))
352+
353+
for token_dir in token_dirs:
354+
if os.path.exists(token_dir):
355+
shutil.rmtree(token_dir)
356+
logger.info(f"Removed Google Gmail token directory: {token_dir}")
357+
358+
# Clear OAuth state manager cache (this is the key fix!)
359+
# This removes the cached credentials from memory
360+
state = oauth_state_manager.get_state("google_gmail")
361+
if state:
362+
if state.status in ["pending", "authorizing"]:
363+
state.cancel()
364+
logger.info("Cancelled ongoing Google Gmail authorization")
365+
# Clear the state completely to remove cached credentials
366+
oauth_state_manager._states.pop("google_gmail", None)
367+
logger.info("Cleared Google Gmail OAuth state cache")
368+
332369
return {
333370
"success": True,
334371
"message": f"Successfully uninstalled {tool} and cleaned up authentication tokens"
@@ -342,7 +379,7 @@ async def uninstall_tool(tool: str):
342379
else:
343380
raise HTTPException(
344381
status_code=404,
345-
detail=f"Tool '{tool}' not found. Available tools: ['notion', 'google_calendar']"
382+
detail=f"Tool '{tool}' not found. Available tools: ['notion', 'google_calendar', 'google_gmail']"
346383
)
347384

348385

backend/app/utils/toolkit/google_gmail_native_toolkit.py

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44

55
from camel.toolkits import GmailToolkit as BaseGmailToolkit
66
from camel.toolkits.function_tool import FunctionTool
7-
from loguru import logger
87

98
from app.component.environment import env
109
from app.service.task import Agents
1110
from app.utils.listen.toolkit_listen import listen_toolkit
1211
from app.utils.toolkit.abstract_toolkit import AbstractToolkit
1312
from app.utils.oauth_state_manager import oauth_state_manager
13+
from utils import traceroot_wrapper as traceroot
14+
15+
logger = traceroot.get_logger("main")
1416

1517
SCOPES = [
1618
'https://www.googleapis.com/auth/gmail.readonly',
@@ -19,7 +21,6 @@
1921
'https://www.googleapis.com/auth/gmail.compose',
2022
'https://www.googleapis.com/auth/gmail.labels',
2123
'https://www.googleapis.com/auth/contacts.readonly',
22-
'https://www.googleapis.com/auth/people.readonly'
2324
]
2425

2526

@@ -41,16 +42,26 @@ def __init__(
4142
"""
4243
self.api_task_id = api_task_id
4344
self._token_path = (
44-
os.environ.get("GOOGLE_GMAIL_TOKEN_PATH")
45+
env("GOOGLE_GMAIL_TOKEN_PATH")
4546
or os.path.join(
4647
os.path.expanduser("~"),
4748
".eigent",
4849
"tokens",
4950
"google_gmail",
50-
f"google_gmail_token_{api_task_id}.json",
51+
"google_gmail_token.json",
5152
)
5253
)
5354
super().__init__(timeout=timeout)
55+
56+
@classmethod
57+
def _build_canonical_token_path(cls) -> str:
58+
return env("GOOGLE_GMAIL_TOKEN_PATH") or os.path.join(
59+
os.path.expanduser("~"),
60+
".eigent",
61+
"tokens",
62+
"google_gmail",
63+
"google_gmail_token.json",
64+
)
5465

5566
# Email Sending Operations
5667
@listen_toolkit(
@@ -159,8 +170,9 @@ def list_threads(
159170
max_results: int = 10,
160171
include_spam_trash: bool = False,
161172
label_ids: Optional[List[str]] = None,
173+
page_token: Optional[str] = None,
162174
) -> Dict[str, Any]:
163-
return super().list_threads(query, max_results, include_spam_trash, label_ids)
175+
return super().list_threads(query, max_results, include_spam_trash, label_ids, page_token)
164176

165177
# Label Management
166178
@listen_toolkit(
@@ -303,10 +315,10 @@ def _authenticate(self):
303315

304316
# If no token file, try environment variables
305317
if not creds:
306-
client_id = os.environ.get("GOOGLE_CLIENT_ID")
307-
client_secret = os.environ.get("GOOGLE_CLIENT_SECRET")
308-
refresh_token = os.environ.get("GOOGLE_REFRESH_TOKEN")
309-
token_uri = os.environ.get("GOOGLE_TOKEN_URI", "https://oauth2.googleapis.com/token")
318+
client_id = env("GOOGLE_CLIENT_ID")
319+
client_secret = env("GOOGLE_CLIENT_SECRET")
320+
refresh_token = env("GOOGLE_REFRESH_TOKEN")
321+
token_uri = env("GOOGLE_TOKEN_URI", "https://oauth2.googleapis.com/token")
310322

311323
if refresh_token and client_id and client_secret:
312324
logger.info("Creating credentials from environment variables")
@@ -378,9 +390,9 @@ def auth_flow():
378390
state.status = "authorizing"
379391
oauth_state_manager.update_status("google_gmail", "authorizing")
380392

381-
client_id = os.environ.get("GOOGLE_CLIENT_ID")
382-
client_secret = os.environ.get("GOOGLE_CLIENT_SECRET")
383-
token_uri = os.environ.get("GOOGLE_TOKEN_URI", "https://oauth2.googleapis.com/token")
393+
client_id = env("GOOGLE_CLIENT_ID")
394+
client_secret = env("GOOGLE_CLIENT_SECRET")
395+
token_uri = env("GOOGLE_TOKEN_URI", "https://oauth2.googleapis.com/token")
384396

385397
logger.info(f"Google Gmail auth - client_id present: {bool(client_id)}, client_secret present: {bool(client_secret)}")
386398

@@ -437,7 +449,7 @@ def auth_flow():
437449
".eigent",
438450
"tokens",
439451
"google_gmail",
440-
f"google_gmail_token_{api_task_id}.json",
452+
f"google_gmail_token.json",
441453
)
442454

443455
try:

0 commit comments

Comments
 (0)