Skip to content

Conversation

@waleedalzarooni
Copy link
Collaborator

Description

Integrated Gmail_toolkit with with requested tools

Checklist

Go over all the following points, and put an x in all the boxes that apply.

  • [X ] I have read the CONTRIBUTION guide (required)
  • [X ] I have linked this PR to an issue using the Development section on the right sidebar or by adding Fixes #issue-number in the PR description (required)
  • [ X] I have checked if any dependencies need to be added or updated in pyproject.toml and uv lock
  • [ X] I have updated the tests accordingly (required for a bug fix or a new feature)
  • [ X] I have updated the documentation if needed:
  • [ X] I have added examples if this is a new feature

If you are unsure about any of these, don't hesitate to ask. We are here to help!

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 25, 2025

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch gmail-toolkit

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@waleedalzarooni waleedalzarooni changed the title gmail_toolkit integration gmail_toolkit integration fixes issue #3189 Sep 25, 2025
@waleedalzarooni waleedalzarooni changed the title gmail_toolkit integration fixes issue #3189 feat: gmail_toolkit integration fixes issue #3189 Sep 25, 2025
@waleedalzarooni waleedalzarooni linked an issue Sep 25, 2025 that may be closed by this pull request
2 tasks
@waleedalzarooni
Copy link
Collaborator Author

I still need to include the gmail keys to env variables file

@waleedalzarooni waleedalzarooni marked this pull request as draft September 29, 2025 10:04
@waleedalzarooni
Copy link
Collaborator Author

@Wendong-Fan, should be ready for review only a couple of things to note

  1. still working on a way to maintain authentication status using the refresh token
  2. my first commit worked perfectly, however, I refactored the authentication method and I it's giving me issues, could you let me know if the example file works with you as it may be something to fix in my google console

@waleedalzarooni waleedalzarooni marked this pull request as ready for review October 1, 2025 09:15
@waleedalzarooni
Copy link
Collaborator Author

waleedalzarooni commented Oct 1, 2025

@Wendong-Fan I have integrated a clean OAUTH authentication mechanism and made some fixes according to your IMAP review

Copy link
Member

@Wendong-Fan Wendong-Fan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @waleedalzarooni ! left some comments below

Comment on lines 18 to 21
# Add camel to path - find the camel package directory
current_file = Path(__file__).resolve()
camel_root = current_file.parent.parent.parent
sys.path.insert(0, str(camel_root))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we need this

Comment on lines 24 to 28
from camel.agents import ChatAgent # noqa: E402
from camel.models import ModelFactory # noqa: E402
from camel.toolkits import GmailToolkit # noqa: E402
from camel.types import ModelPlatformType # noqa: E402
from camel.types.enums import ModelType # noqa: E402
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why need # noqa: E402

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested the functionality it works, but seems now we require both GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET and OAuth, do all those necessary?

return header['value']
return ""

def _extract_message_body(self, message: Dict[str, Any]) -> str:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems this function cannot handle nested multipart message structures, only single-level multipart messages?

"googlemaps>=4.10.0,<5",
"requests_oauthlib>=1.3.1,<2",
"google-api-python-client==2.166.0",
"google-auth>=2.0.0,<3.0.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we also need to add this dependency to [all]

Comment on lines 19 to 22
if TYPE_CHECKING:
from googleapiclient.discovery import Resource
else:
Resource = Any
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where did Resource used?

bcc (Optional[Union[str, List[str]]]): BCC recipient email
address(es).
attachments (Optional[List[str]]): List of file paths to attach.
is_html (bool): Whether the body is HTML format.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we add more description for this argument? how and when would this be used, same for other functions

query (str): Gmail search query string.
max_results (int): Maximum number of emails to fetch.
include_spam_trash (bool): Whether to include spam and trash.
label_ids (Optional[List[str]]): List of label IDs to filter by.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what kind of label would be used here? I think we need more description to make it clear

@review-notebook-app
Copy link

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB


# Remove script and style elements completely
text = re.sub(
r'<script[^>]*>.*?</script>',

Check failure

Code scanning / CodeQL

Bad HTML filtering regexp High

This regular expression does not match script end tags like </script >.
@waleedalzarooni
Copy link
Collaborator Author

@Wendong-Fan, new commit addressing review comments.

As discussed I will open a new PR for the Eigent Authentication integration

Sorry about the messy commit history, I've tried multiple approaches to prevent this but neither are working. When I'm ready to push and I get a warning that my feature branch is diverging from master I have tried git merge and rebase but both cause this issue.

I will try and find a solution as I'm not quite sure what I'm doing wrong

Copy link
Collaborator

@MuggleJinx MuggleJinx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current OAuth2 requires

  • GOOGLE_CLIENT_ID
  • GOOGLE_CLIENT_SECRET
    We shoud mention them in the docs

os.unlink(temp_file_path)
except Exception as e:
logger.warning(
f"Failed to delete temp file {temp_file}: {e}"
Copy link
Collaborator

@MuggleJinx MuggleJinx Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"temp_file" is possibly unbound. should be temp_file_path I guess.

if text:
text_parts.append(text)

# Lines 1458-1462
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

Copy link
Collaborator

@MuggleJinx MuggleJinx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we removing .env.example ?

Copy link
Collaborator

@MuggleJinx MuggleJinx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thing: The current functions — fetch_emails, list_threads, list_drafts, and get_contacts — only return next_page_token. It is recommended to also accept a page_token parameter as input so that the upper layers can request subsequent pages.
Location: At the points where next_page_token is returned, such as in camel/toolkits/gmail_toolkit.py:521, 756, 796, 1043.

logger = get_logger(__name__)

SCOPES = [
'https://mail.google.com/',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove the first one since it's too broad. might not be necessary.

logger.error("Failed to send email: %s", e)
return {"error": f"Failed to send email: {e!s}"}

def reply_to_email(
Copy link
Collaborator

@MuggleJinx MuggleJinx Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reply_to_email function does not set the threadId and does not include the In-Reply-To or References headers. This may cause Gmail to create a new thread instead of appending the message to the existing conversation.
Recommendation: When sending a reply, include the original email’s threadId in the request body (e.g., {'raw': ..., 'threadId': ...}), and update _create_message to optionally set the In-Reply-To and References headers before returning.

logger.error("Failed to reply to email: %s", e)
return {"error": f"Failed to reply to email: {e!s}"}

def forward_email(
Copy link
Collaborator

@MuggleJinx MuggleJinx Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The forward_email function’s documentation states that “inline images are not included”, but the implementation does not filter by is_inline. As a result, inline images (such as logos or embedded images within the email body) are also treated as attachments when forwarding.
Recommendation: Filter the return value of _extract_attachments to include only attachments where not att['is_inline'].

creds = None

# COMPONENT 2: Refresh if expired
if creds and creds.expired and creds.refresh_token:
Copy link
Collaborator

@MuggleJinx MuggleJinx Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OAuth Token Refresh Not Persisted
After refreshing, the function directly returns creds without writing the updated token back to ~/.camel/gmail_token.json.
Recommendation: After a successful refresh, persist the latest token to disk, consistent with the behavior used during the initial authorization.

env_file = Path(__file__).parent.parent.parent / '.env'
load_dotenv(env_file)

client_id = os.environ.get('GOOGLE_CLIENT_ID')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if None, we should generate error

'pageSize': max_results,
}

if query:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The people().connections().list method does not support query-based filtering — any provided query parameter is ignored. However, the function’s docstring and signature currently imply that search functionality is supported.
see https://developers.google.com/people/api/rest/v1/people.connections/list

r"""Delete a Gmail label.
Args:
label_id (str): List of label IDs to filter emails by.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not List of label IDs..

Copy link
Collaborator

@MuggleJinx MuggleJinx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @waleedalzarooni for the contribution! I left some comments!
Sorry about the mess. I think I mis-click a few times of submitting review, so now the overall comments look a bit messy 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] Integrate Gmail Tool

3 participants