-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added first version of Farcaster agent * Added placeholder for farcaster_client.py * Agent posting on Farcaster * formatting fixes * Last fixes before review * Last fixes before review (2) * Added one PR comment * Generating tweets from latest bets on Omen from think-thoroughly agent * Adjusted prompts for 1st person * Added twitter integration * Fixing mypy * Added lock file * Implemented PR comments * Fixed isort * Fixed autoflake * Added new PMAT version * Formatting * Updated lock file * Updated lock file (2) * Another round of PR comments * Updated .env.example with Twitter credentials
- Loading branch information
1 parent
52e5584
commit 95b3f2f
Showing
13 changed files
with
533 additions
and
136 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
6 changes: 6 additions & 0 deletions
6
prediction_market_agent/agents/autogen_general_agent/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
Farcaster agent URL | ||
https://warpcast.com/gnosis-ai-agent/ | ||
|
||
FID (Farcaster ID) - 518226 | ||
|
||
Public key - 0xF0AE06f9ca8bc3DC4C5C77CEC6F14D74AB21F782 |
Empty file.
62 changes: 62 additions & 0 deletions
62
prediction_market_agent/agents/autogen_general_agent/deploy.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
from datetime import timedelta | ||
|
||
from loguru import logger | ||
from prediction_market_agent_tooling.config import PrivateCredentials | ||
from prediction_market_agent_tooling.deploy.agent import DeployableTraderAgent | ||
from prediction_market_agent_tooling.markets.data_models import Bet | ||
from prediction_market_agent_tooling.markets.markets import MarketType | ||
from prediction_market_agent_tooling.tools.utils import utcnow | ||
|
||
from prediction_market_agent.agents.autogen_general_agent.social_agent import ( | ||
build_social_media_text, | ||
) | ||
from prediction_market_agent.agents.autogen_general_agent.social_media.abstract_handler import ( | ||
AbstractSocialMediaHandler, | ||
) | ||
from prediction_market_agent.agents.autogen_general_agent.social_media.farcaster_handler import ( | ||
FarcasterHandler, | ||
) | ||
from prediction_market_agent.agents.autogen_general_agent.social_media.twitter_handler import ( | ||
TwitterHandler, | ||
) | ||
from prediction_market_agent.utils import APIKeys | ||
|
||
|
||
class DeployableSocialMediaAgent(DeployableTraderAgent): | ||
model: str = "gpt-4-turbo-2024-04-09" | ||
social_media_handlers: list[AbstractSocialMediaHandler] = [ | ||
FarcasterHandler(), | ||
TwitterHandler(), | ||
] | ||
|
||
def run(self, market_type: MarketType) -> None: | ||
# It should post a message (cast) on each run. | ||
# We just need one market to get latest bets. | ||
|
||
bets = self.get_bets(market_type=market_type) | ||
# If no bets available for the last 24h, we skip posting. | ||
if not bets: | ||
logger.info("No bets available from last day. No post will be created.") | ||
return | ||
tweet = build_social_media_text(market_type, bets) | ||
self.post(tweet) | ||
|
||
def get_bets(self, market_type: MarketType) -> list[Bet]: | ||
better_address = PrivateCredentials.from_api_keys(APIKeys()).public_key | ||
one_day_ago = utcnow() - timedelta(days=1) | ||
return market_type.market_class.get_bets_made_since( | ||
better_address=better_address, start_time=one_day_ago | ||
) | ||
|
||
def post(self, tweet: str | None) -> None: | ||
if not tweet: | ||
logger.info("No tweet was produced. Exiting.") | ||
return | ||
|
||
for handler in self.social_media_handlers: | ||
handler.post(tweet) | ||
|
||
|
||
if __name__ == "__main__": | ||
agent = DeployableSocialMediaAgent() | ||
agent.deploy_local(market_type=MarketType.OMEN, sleep_time=540, timeout=180) |
20 changes: 20 additions & 0 deletions
20
prediction_market_agent/agents/autogen_general_agent/prompts.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
INFLUENCER_PROMPT = """ | ||
You are an AI agent that places bets on future events. | ||
You can find below a list of recent BETS that you have placed. | ||
[BETS] | ||
$BETS | ||
Write one engaging tweet that attracts attention to the recent bets that the AI agent has placed, | ||
in order to increase his audience that wants to follow his betting activity. Make sure to include | ||
the outcome that the AI agent has placed a bet on. | ||
Pick a single topic for the tweet. | ||
You must not add any reasoning or additional explanation, simply output the tweet. | ||
""" | ||
|
||
CRITIC_PROMPT = """ | ||
Reflect and provide critique on the following tweet. \n\n $TWEET | ||
Note that it should not include inappropriate language. | ||
Note also that the tweet should not sound robotic, instead as human-like as possible. | ||
Also make sure to ask the recipient for an improved version of the tweet and nothing else. | ||
""" |
154 changes: 154 additions & 0 deletions
154
prediction_market_agent/agents/autogen_general_agent/social_agent.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
from datetime import datetime | ||
from enum import Enum | ||
from string import Template | ||
from typing import Any, Dict, Optional | ||
|
||
import autogen | ||
from autogen import AssistantAgent, UserProxyAgent | ||
from autogen.cache import Cache | ||
from prediction_market_agent_tooling.markets.data_models import Bet | ||
from pydantic import BaseModel | ||
|
||
from prediction_market_agent.agents.autogen_general_agent.prompts import ( | ||
CRITIC_PROMPT, | ||
INFLUENCER_PROMPT, | ||
) | ||
from prediction_market_agent.utils import APIKeys | ||
|
||
|
||
class BetInputPrompt(BaseModel): | ||
title: str | ||
boolean_outcome: bool | ||
collateral_amount: float | ||
creation_datetime: datetime | ||
|
||
@staticmethod | ||
def from_bet(bet: Bet) -> "BetInputPrompt": | ||
return BetInputPrompt( | ||
title=bet.market_question, | ||
boolean_outcome=bet.outcome, | ||
collateral_amount=bet.amount.amount, | ||
creation_datetime=bet.created_time, | ||
) | ||
|
||
|
||
class AutogenAgentType(str, Enum): | ||
WRITER = "writer" | ||
CRITIC = "critic" | ||
USER = "user" | ||
|
||
|
||
def reflection_message( | ||
recipient: UserProxyAgent, | ||
messages: list[Dict[Any, Any]], | ||
sender: AssistantAgent, | ||
config: Optional[Any] = None, | ||
) -> str: | ||
reflect_prompt = Template(CRITIC_PROMPT).substitute( | ||
TWEET=recipient.chat_messages_for_summary(sender)[-1]["content"] | ||
) | ||
return reflect_prompt | ||
|
||
|
||
def build_llm_config(model: str) -> Dict[str, Any]: | ||
keys = APIKeys() | ||
return { | ||
"config_list": [ | ||
{"model": model, "api_key": keys.openai_api_key.get_secret_value()} | ||
], | ||
} | ||
|
||
|
||
def build_agents(model: str) -> Dict[AutogenAgentType, autogen.ConversableAgent]: | ||
llm_config = build_llm_config(model) | ||
|
||
user_proxy = autogen.UserProxyAgent( | ||
name="User", | ||
human_input_mode="NEVER", | ||
is_termination_msg=lambda x: x.get("content", "").find("TERMINATE") >= 0, | ||
code_execution_config={ | ||
"last_n_messages": 1, | ||
"work_dir": "tasks", | ||
"use_docker": False, | ||
}, | ||
) | ||
|
||
writer = autogen.AssistantAgent( | ||
name="Writer", | ||
llm_config=llm_config, | ||
system_message=""" | ||
You are a professional influencer, known for your insightful and engaging tweets. | ||
You transform complex concepts into compelling narratives. | ||
You should improve the quality of the content based on the feedback from the user. | ||
You must always return only the tweet. | ||
""", | ||
) | ||
|
||
critic = autogen.AssistantAgent( | ||
name="Critic", | ||
llm_config=llm_config, | ||
system_message=""" | ||
You are a critic, known for your thoroughness and commitment to standards. | ||
Your task is to scrutinize content for any harmful elements or regulatory violations, ensuring | ||
all materials align with required guidelines. | ||
References to betting and gambling are allowed. | ||
""", | ||
) | ||
|
||
return { | ||
AutogenAgentType.CRITIC: critic, | ||
AutogenAgentType.WRITER: writer, | ||
AutogenAgentType.USER: user_proxy, | ||
} | ||
|
||
|
||
def build_social_media_text(model: str, bets: list[Bet]) -> str: | ||
""" | ||
Builds a tweet based on past betting activity from a given participant. | ||
This function utilizes the writer and critic agents to generate the tweet content. It first initializes the | ||
necessary agents based on the provided model. Then, it registers a chat between the writer and critic agents. | ||
The tweet content is generated using a template that includes questions about each market's title and likelihood. | ||
""" | ||
|
||
agents = build_agents(model) | ||
user_proxy, writer, critic = ( | ||
agents[AutogenAgentType.USER], | ||
agents[AutogenAgentType.WRITER], | ||
agents[AutogenAgentType.CRITIC], | ||
) | ||
user_proxy.register_nested_chats( | ||
[ | ||
{ | ||
"recipient": critic, | ||
"message": reflection_message, | ||
"summary_method": "last_msg", | ||
"max_turns": 1, | ||
} | ||
], | ||
trigger=writer, | ||
) | ||
|
||
# ToDO - fetch agent reasoning from DB and construct better tweets | ||
# See https://github.com/gnosis/prediction-market-agent/issues/150 | ||
|
||
task = Template(INFLUENCER_PROMPT).substitute( | ||
BETS=[BetInputPrompt.from_bet(bet) for bet in bets] | ||
) | ||
|
||
# in case we trigger repeated runs, Cache makes it faster. | ||
with Cache.disk(cache_seed=42) as cache: | ||
# max_turns = the maximum number of turns for the chat between the two agents. One turn means one conversation round trip. | ||
res = user_proxy.initiate_chat( | ||
recipient=writer, | ||
message=task, | ||
max_turns=2, | ||
summary_method="last_msg", | ||
cache=cache, | ||
) | ||
|
||
tweet = res.summary | ||
return str( | ||
tweet | ||
) # Casting needed as summary is of type any and no Pydantic support |
Empty file.
10 changes: 10 additions & 0 deletions
10
prediction_market_agent/agents/autogen_general_agent/social_media/abstract_handler.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import typing as t | ||
from abc import ABCMeta, abstractmethod | ||
|
||
|
||
class AbstractSocialMediaHandler(metaclass=ABCMeta): | ||
client: t.Any | ||
|
||
@abstractmethod | ||
def post(self, text: str) -> None: | ||
pass |
19 changes: 19 additions & 0 deletions
19
prediction_market_agent/agents/autogen_general_agent/social_media/farcaster_handler.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from farcaster import Warpcast | ||
from loguru import logger | ||
|
||
from prediction_market_agent.agents.autogen_general_agent.social_media.abstract_handler import ( | ||
AbstractSocialMediaHandler, | ||
) | ||
from prediction_market_agent.utils import SocialMediaAPIKeys | ||
|
||
|
||
class FarcasterHandler(AbstractSocialMediaHandler): | ||
def __init__(self) -> None: | ||
api_keys = SocialMediaAPIKeys() | ||
self.client = Warpcast( | ||
private_key=api_keys.farcaster_private_key.get_secret_value() | ||
) | ||
|
||
def post(self, text: str) -> None: | ||
cast = self.client.post_cast(text=text) | ||
logger.info(f"Posted cast {cast.cast.text} - hash {cast.cast.hash}") |
27 changes: 27 additions & 0 deletions
27
prediction_market_agent/agents/autogen_general_agent/social_media/twitter_handler.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import tweepy | ||
from loguru import logger | ||
from tweepy import Client | ||
|
||
from prediction_market_agent.agents.autogen_general_agent.social_media.abstract_handler import ( | ||
AbstractSocialMediaHandler, | ||
) | ||
from prediction_market_agent.utils import SocialMediaAPIKeys | ||
|
||
|
||
class TwitterHandler(AbstractSocialMediaHandler): | ||
client: Client | ||
|
||
def __init__(self) -> None: | ||
keys = SocialMediaAPIKeys() | ||
|
||
self.client = tweepy.Client( | ||
keys.twitter_bearer_token.get_secret_value(), | ||
keys.twitter_api_key.get_secret_value(), | ||
keys.twitter_api_key_secret.get_secret_value(), | ||
keys.twitter_access_token.get_secret_value(), | ||
keys.twitter_access_token_secret.get_secret_value(), | ||
) | ||
|
||
def post(self, text: str) -> None: | ||
response = self.client.create_tweet(text=text) | ||
logger.info(f"Posted tweet {text} - response {response}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters