Skip to content

Commit

Permalink
second pass implementing post text to twitter
Browse files Browse the repository at this point in the history
  • Loading branch information
stat committed Nov 6, 2024
1 parent aa01e28 commit d2b9c00
Show file tree
Hide file tree
Showing 12 changed files with 158 additions and 68 deletions.
9 changes: 0 additions & 9 deletions cdp-agentkit-core/cdp_agentkit_core/actions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,6 @@
uniswap_v3_create_pool,
)

from cdp_agentkit_core.actions.social.twitter.post_text import (
TWITTER_POST_TEXT_PROMPT,
TwitterPostTextInput,
twitter_post_text,
)

__all__ = [
"UNISWAP_V3_CREATE_POOL_PROMPT",
"UniswapV3CreatePoolInput",
Expand Down Expand Up @@ -86,7 +80,4 @@
"TRANSFER_PROMPT",
"TransferInput",
"transfer",
"TWITTER_POST_TEXT_PROMPT",
"TwitterPostTextInput",
"twitter_post_text"
]
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from cdp_agentkit_core.actions.social.twitter.post_text import (
POST_TEXT_PROMPT as POST_TEXT_PROMPT,
)
from cdp_agentkit_core.actions.social.twitter.post_text import (
PostTextInput as PostTextInput,
)
from cdp_agentkit_core.actions.social.twitter.post_text import (
post_text as post_text,
)
Original file line number Diff line number Diff line change
@@ -1,41 +1,34 @@
from typing import Any
import tweepy

from pydantic import BaseModel, Field

TWITTER_POST_TEXT_PROMPT = """
This tool will post a text on Twitter."""
POST_TEXT_PROMPT = """
This tool will post text on Twitter."""

class TwitterPostTextInput(BaseModel):
class PostTextInput(BaseModel):
"""Input argument schema for twitter post text actions."""

client: Any = Field(
...,
description="The tweepy client used to interface with the Twitter API",
)

text: str = Field(
...,
description="The text to post to twitter",
)

def twitter_post_text(client: tweepy.Client, text: str) -> str:
def post_text(client: tweepy.Client, text: str) -> str:
"""Post text to Twitter.
Args:
client (tweepy.Client): The tweepy client to use.
text (str): The text to post.
Returns:
str: A text containing the result of the post text to twitter response.
"""

message = ""

try:
client.create_tweet(text=text)
message = f"Successfully posted!"
except tweepy.error.TweepError as e:
message = "Successfully posted!"
except tweepy.errors.TweepyException as e:
message = f"Error posting: {e}"

return text
return message
16 changes: 9 additions & 7 deletions cdp-langchain/examples/chatbot/chatbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
from langchain_openai import ChatOpenAI
from langgraph.checkpoint.memory import MemorySaver
from langgraph.prebuilt import create_react_agent

# Import CDP Agentkit Langchain Extension.
from cdp_langchain.agent_toolkits import CdpToolkit
from cdp_langchain.utils import CdpAgentkitWrapper
from twitter_langchain import (
TwitterApiWrapper,
TwitterToolkit,
)

# Import CDP Agentkit Langchain Extension.
from cdp_langchain.agent_toolkits import CdpToolkit
from cdp_langchain.utils import CdpAgentkitWrapper

# Configure a file to persist the agent's CDP MPC Wallet Data.
wallet_data_file = "wallet_data.txt"

Expand Down Expand Up @@ -47,10 +47,11 @@ def initialize_agent():
cdp_toolkit = CdpToolkit.from_cdp_agentkit_wrapper(agentkit)
tools = cdp_toolkit.get_tools()

# Initialize
twitter_api = TwitterApiWrapper(**values)
# Initialize CDP Agentkit Twitter Langchain
twitter_api_values = {}
twitter_api = TwitterApiWrapper(**twitter_api_values)
twitter_toolkit = TwitterToolkit.from_twitter_api_wrapper(twitter_api)
twitter_tools = cdp_toolkit.get_tools()
twitter_tools = twitter_toolkit.get_tools()

tools.extend(twitter_tools)

Expand All @@ -61,6 +62,7 @@ def initialize_agent():
# Create ReAct Agent using the LLM and CDP Agentkit tools.
return create_react_agent(
llm,
# tools=tools,
tools=tools,
checkpointer=memory,
state_modifier="You are a helpful agent that can interact onchain on the Base Layer 2 using the Coinbase Developer Platform Agentkit. You are empowered to interact onchain using your tools. If you ever need funds, you can request them from the faucet. You can also deploy your own ERC-20 tokens, NFTs, and interact with them. If someone asks you to do something you can't do, you can say so, and encourage them to implement it themselves using the CDP SDK + Agentkit, recommend they go to docs.cdp.coinbase.com for more informaton.",
Expand Down
14 changes: 7 additions & 7 deletions cdp-langchain/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions twitter-langchain/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.PHONY: format
format:
ruff format .

.PHONY: lint
lint:
ruff check .

.PHONY: lint-fix
lint-fix:
ruff check . --fix

.PHONY: docs
docs:
sphinx-apidoc -f -o ./docs ./cdp_langchain

.PHONY: local-docs
local-docs: docs
cd docs && make html && open ./_build/html/index.html

.PHONY: test
test:
pytest
64 changes: 64 additions & 0 deletions twitter-langchain/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,65 @@
# Twitter (X) Langchain Toolkit

## Developing
- `cdp-sdk` has a dependency on `cargo`, please install rust and add `cargo` to your path
- [Rust Installation Instructions](https://doc.rust-lang.org/cargo/getting-started/installation.html)
- `export PATH="$HOME/.cargo/bin:$PATH"`
- Agentkit uses `poetry` for package management and tooling
- [Poetry Installation Instructions](https://python-poetry.org/docs/#installation)
- Run `poetry install` to install `cdp-langchain` dependencies
- Run `poetry shell` to activate the virtual environment

## Documentation
- [Agentkit-Core](https://coinbase.github.io/cdp-agentkit-core)
- [Agentkit-Langchain](https://coinbase.github.io/cdp-langchain)
- [Agentkit-Twitter-Langchain](https://coinbase.github.io/twitter-langchain)

### Formatting
`make format`

### Linting
- Check linter
`make lint`

- Fix linter errors
`make lint-fix`

## Adding an Agentic Action to the Langchain Toolkit
1. Ensure the action is implemented in `cdp-agentkit-core`.
2. Add a wrapper method to `TwitterApiWrapper` in `./twitter_langchain/twitter_api_wrapper.py`
- E.g.
```python
def post_text_wrapper(self, text: str) -> str:
"""Post text to Twitter.
Args:
text (str): The text to post.
Returns:
str: A text containing the result of the post text to twitter response.
"""
return post_text(client=self.client, text=text)
```
3. Add call to the wrapper in `TwitterApiWrapper.run` in `./twitter_langchain/twitter_api_wrapper.py`
- E.g.
```python
if mode == "post_text":
return self.post_text_wrapper(**kwargs)

```
4. Add the action to the list of available tools in the `TwitterToolkit` in `./twitter_langchain/twitter_toolkit.py`
- E.g.
```python
actions: List[Dict] = [
{
"mode": "post_text",
"name": "post_text",
"description": POST_TEXT_PROMPT,
"args_schema": PostTextInput,
},
]
```
5. Update `TwitterToolkit` documentation
- Add the action to the list of tools
- Add any additional ENV requirements
2 changes: 1 addition & 1 deletion twitter-langchain/twitter_langchain/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
""" CDP Twitter Toolkit."""
"""CDP Twitter Toolkit."""

from twitter_langchain.twitter_action import TwitterAction
from twitter_langchain.twitter_api_wrapper import TwitterApiWrapper
Expand Down
7 changes: 1 addition & 6 deletions twitter-langchain/twitter_langchain/twitter_action.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
"""Tool allows agents to interact with the Twitter API.
To use this tool, you must first set as environment variables:
# TODO: List ENV VARs required.
"""
"""Tool allows agents to interact with the Twitter API."""

from typing import Any

Expand Down
37 changes: 24 additions & 13 deletions twitter-langchain/twitter_langchain/twitter_api_wrapper.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
"""Util that calls Twitter API."""

import tweepy

from typing import Any

from langchain_core.utils import get_from_dict_or_env
from pydantic import BaseModel, model_validator

from langchain_core.utils import get_from_dict_or_env
from cdp_agentkit_core.actions.social.twitter import (
post_text,
)

# from cdp_agentkit_core.actions import (
# twitter_post_text,
# )

class TwitterApiWrapper(BaseModel):
"""Wrapper for Twitter API."""
Expand All @@ -22,8 +21,10 @@ class TwitterApiWrapper(BaseModel):
@classmethod
def validate_environment(cls, values: dict) -> Any:
"""Validate that Twitter access token, token secret, and tweepy exists in the environment."""

bearer_token = get_from_dict_or_env(values, "twitter_bearer_token", "TWITTER_BEARER_TOKEN")
api_key = get_from_dict_or_env(values, "twitter_api_key", "TWITTER_API_KEY")
api_secret = get_from_dict_or_env(values, "twitter_api_secret", "TWITTER_API_SECRET")
access_token = get_from_dict_or_env(values, "twitter_access_token", "TWITTER_ACCESS_TOKEN")
access_token_secret = get_from_dict_or_env(values, "twitter_access_token_secret", "TWITTER_ACCESS_TOKEN_SECRET")

try:
import tweepy
Expand All @@ -32,8 +33,18 @@ def validate_environment(cls, values: dict) -> Any:
"Tweepy Twitter SDK is not installed. " "Please install it with `pip install tweepy`"
) from None

values["bearer_token"] = bearer_token
client = tweepy.Client(bearer_token)
client = tweepy.Client(
consumer_key=api_key,
consumer_secret=api_secret,
access_token=access_token,
access_token_secret=access_token_secret,
)

values["client"] = client
values["api_key"] = api_key
values["api_secret"] = api_secret
values["access_token"] = access_token
values["access_token_secret"] = access_token_secret

return values

Expand All @@ -47,12 +58,12 @@ def post_text_wrapper(self, text: str) -> str:
str: A text containing the result of the post text to twitter response.
"""
# return twitter_post_text(client=self.client, text=text)
return ""
return post_text(client=self.client, text=text)

def run(self, mode: str, **kwargs) -> str:
"""Run the action via the Twitter API."""
if mode == "post_text":
return self.post_text_wrapper()
return self.post_text_wrapper(**kwargs)
else:
raise ValueError("Invalid mode" + mode)
raise ValueError("Invalid mode: " + mode)

Loading

0 comments on commit d2b9c00

Please sign in to comment.