diff --git a/cdp-agentkit-core/cdp_agentkit_core/actions/social/twitter/__init__.py b/cdp-agentkit-core/cdp_agentkit_core/actions/social/twitter/__init__.py index 4555629c5..8af0f009c 100644 --- a/cdp-agentkit-core/cdp_agentkit_core/actions/social/twitter/__init__.py +++ b/cdp-agentkit-core/cdp_agentkit_core/actions/social/twitter/__init__.py @@ -1,9 +1,14 @@ -from cdp_agentkit_core.actions.social.twitter.post_tweet import ( - POST_TWEET_PROMPT as POST_TWEET_PROMPT, +from cdp_agentkit_core.actions.social.twitter.account_details import ( + ACCOUNT_DETAILS_PROMPT as ACCOUNT_DETAILS_PROMPT, ) -from cdp_agentkit_core.actions.social.twitter.post_tweet import ( - PostTweetInput as PostTweetInput, +from cdp_agentkit_core.actions.social.twitter.account_details import ( + AccountDetailsInput as AccountDetailsInput, +) +from cdp_agentkit_core.actions.social.twitter.account_details import ( + account_details as account_details, ) from cdp_agentkit_core.actions.social.twitter.post_tweet import ( - post_tweet as post_tweet, + POST_TWEET_PROMPT as POST_TWEET_PROMPT, ) +from cdp_agentkit_core.actions.social.twitter.post_tweet import PostTweetInput as PostTweetInput +from cdp_agentkit_core.actions.social.twitter.post_tweet import post_tweet as post_tweet diff --git a/cdp-agentkit-core/cdp_agentkit_core/actions/social/twitter/account_details.py b/cdp-agentkit-core/cdp_agentkit_core/actions/social/twitter/account_details.py new file mode 100644 index 000000000..dc2902830 --- /dev/null +++ b/cdp-agentkit-core/cdp_agentkit_core/actions/social/twitter/account_details.py @@ -0,0 +1,36 @@ +import tweepy +from pydantic import BaseModel, Field + +ACCOUNT_DETAILS_PROMPT = """ +This tool will return account details for the currently authenticated Twitter (X) user context.""" + +class AccountDetailsInput(BaseModel): + """Input argument schema for Twitter account details action.""" + + no_input: str = Field( + "", + description="No input required, e.g. `` (empty string).", + ) + +def account_details(client: tweepy.Client) -> str: + """Get the authenticated Twitter (X) user account details. + + Returns: + str: A message containing account details for the authenticated user context. + + """ + message = "" + + try: + response = client.get_me() + user = response.data + + message = f"""Successfully retrieved authenticated user account details. Please present the following as json and not markdown: + id: {user.id} + name: {user.name} + username: {user.username} + link: https://x.com/{user.username}""" + except tweepy.errors.TweepyException as e: + message = f"Error retrieving authenticated user account details: {e}" + + return message diff --git a/twitter-langchain/examples/account_details/Makefile b/twitter-langchain/examples/account_details/Makefile new file mode 100644 index 000000000..b2bf7df51 --- /dev/null +++ b/twitter-langchain/examples/account_details/Makefile @@ -0,0 +1,10 @@ +ifneq (,$(wildcard ./.env)) + include .env +endif + +export + +.PHONY: run +run: + poetry install --with dev + poetry run python account_details.py diff --git a/twitter-langchain/examples/account_details/account_details.py b/twitter-langchain/examples/account_details/account_details.py new file mode 100644 index 000000000..f5559a9ee --- /dev/null +++ b/twitter-langchain/examples/account_details/account_details.py @@ -0,0 +1,59 @@ + +from langchain_core.messages import HumanMessage +from langchain_openai import ChatOpenAI +from langgraph.prebuilt import create_react_agent + +from twitter_langchain import TwitterApiWrapper, TwitterToolkit + +# Initialize TwitterApiwrapper +twitter_api_wrapper = TwitterApiWrapper() + +# Create TwitterToolkit from the api wrapper +twitter_toolkit = TwitterToolkit.from_twitter_api_wrapper(twitter_api_wrapper) + +# View available tools +tools = twitter_toolkit.get_tools() +for tool in tools: + print(tool.name) + +# Initialize LLM +llm = ChatOpenAI(model="gpt-4o-mini") + +# Create agent +agent_executor = create_react_agent(llm, tools) + +# Example - get account details +events = agent_executor.stream( + { + "messages": [ + HumanMessage(content="Please obtain my twitter account information"), + ], + }, + stream_mode="values", +) + +for event in events: + event["messages"][-1].pretty_print() + +# ================================ Human Message ================================= +# Please obtain my twitter account information +# ================================== Ai Message ================================== +# Tool Calls: +# account_details (call_pYME8H1tHfdMakFZ1FTS0VBX) +# Call ID: call_pYME8H1tHfdMakFZ1FTS0VBX +# Args: +# ================================= Tool Message ================================= +# Name: account_details + +# Successfully retrieved authenticated user account details. Please present the following as json and not markdown: +# id: 1234567890123456789 +# name: My Twitter Name +# username: MyTwitterUserName +# link: https://x.com/MyTwitterUserName +# ================================== Ai Message ================================== +# { +# "id": "1234567890123456789", +# "name": "My Twitter Name", +# "username": "MyTwitterUserName", +# "link": "https://x.com/MyTwitterUserName" +# } diff --git a/twitter-langchain/examples/post_tweet/post_tweet.py b/twitter-langchain/examples/post_tweet/post_tweet.py index 69fb690dc..d5faba9cd 100644 --- a/twitter-langchain/examples/post_tweet/post_tweet.py +++ b/twitter-langchain/examples/post_tweet/post_tweet.py @@ -1,13 +1,10 @@ import uuid -from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage +from langchain_openai import ChatOpenAI from langgraph.prebuilt import create_react_agent -from twitter_langchain import ( - TwitterApiWrapper, - TwitterToolkit -) +from twitter_langchain import TwitterApiWrapper, TwitterToolkit # Initialize TwitterApiwrapper twitter_api_wrapper = TwitterApiWrapper() diff --git a/twitter-langchain/twitter_langchain/twitter_api_wrapper.py b/twitter-langchain/twitter_langchain/twitter_api_wrapper.py index 7acdde7e5..5b63dc031 100644 --- a/twitter-langchain/twitter_langchain/twitter_api_wrapper.py +++ b/twitter-langchain/twitter_langchain/twitter_api_wrapper.py @@ -1,20 +1,18 @@ """Util that calls Twitter API.""" - from typing import Any -from langchain_core.utils import get_from_dict_or_env -from pydantic import BaseModel, model_validator - from cdp_agentkit_core.actions.social.twitter import ( + account_details, post_tweet, ) +from langchain_core.utils import get_from_dict_or_env +from pydantic import BaseModel, model_validator class TwitterApiWrapper(BaseModel): """Wrapper for Twitter API.""" - bearer_token: str | None = None client: Any | None = None @model_validator(mode="before") @@ -48,6 +46,15 @@ def validate_environment(cls, values: dict) -> Any: return values + def account_details_wrapper(self) -> str: + """Get the authenticated Twitter (X) user account details. + + Returns: + str: A message containing account details for the authenticated user context in JSON format. + + """ + return account_details(client=self.client) + def post_tweet_wrapper(self, tweet: str) -> str: """Post tweet to Twitter. @@ -59,12 +66,13 @@ def post_tweet_wrapper(self, tweet: str) -> str: str: A message containing the result of the post action and the tweet. """ - return post_tweet(client=self.client, tweet=tweet) def run(self, mode: str, **kwargs) -> str: """Run the action via the Twitter API.""" - if mode == "post_tweet": + if mode == "account_details": + return self.account_details_wrapper() + elif mode == "post_tweet": return self.post_tweet_wrapper(**kwargs) else: raise ValueError("Invalid mode: " + mode) diff --git a/twitter-langchain/twitter_langchain/twitter_toolkit.py b/twitter-langchain/twitter_langchain/twitter_toolkit.py index a712e1bed..af46e4e0c 100644 --- a/twitter-langchain/twitter_langchain/twitter_toolkit.py +++ b/twitter-langchain/twitter_langchain/twitter_toolkit.py @@ -1,12 +1,14 @@ """TwitterToolkit.""" -from langchain_core.tools import BaseTool -from langchain_core.tools.base import BaseToolkit - from cdp_agentkit_core.actions.social.twitter import ( + ACCOUNT_DETAILS_PROMPT, POST_TWEET_PROMPT, + AccountDetailsInput, PostTweetInput, ) +from langchain_core.tools import BaseTool +from langchain_core.tools.base import BaseToolkit + from twitter_langchain.twitter_action import TwitterAction from twitter_langchain.twitter_api_wrapper import TwitterApiWrapper @@ -55,6 +57,7 @@ class TwitterToolkit(BaseToolkit): .. code-block:: none + account_details post_tweet Use within an agent: @@ -120,6 +123,12 @@ def from_twitter_api_wrapper(cls, twitter_api_wrapper: TwitterApiWrapper) -> "Tw """ actions: list[dict] = [ + { + "mode": "account_details", + "name": "account_details", + "description": ACCOUNT_DETAILS_PROMPT, + "args_schema": AccountDetailsInput, + }, { "mode": "post_tweet", "name": "post_tweet",