Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement PredictProbabilityForQuestion function for microchain agent #75

Merged
merged 27 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
641c796
Implement BuyTokens and SellTokens microchain Functions
evangriffiths Apr 11, 2024
28629bc
Add Buy/SellTokens test
evangriffiths Apr 11, 2024
023bd64
Merge main
evangriffiths Apr 11, 2024
2aebc66
Tidy
evangriffiths Apr 11, 2024
1c64ab8
Remove incorrect error message
evangriffiths Apr 11, 2024
448c1e5
Change test assertion
evangriffiths Apr 11, 2024
c5d5153
Implement PredictPropabilityForQuestion function for microchain agent
evangriffiths Apr 11, 2024
319d10d
Fix json parsing, add comments
evangriffiths Apr 12, 2024
e933009
Merge main
evangriffiths Apr 12, 2024
4bfb58b
Merge branch 'main' into evan/implement-PredictPropabilityForQuestion
evangriffiths Apr 12, 2024
8e6c232
Fix PredictPropabilityForQuestion example arg
evangriffiths Apr 12, 2024
074b7b0
Add min balance check to PredictPropabilityForQuestion
evangriffiths Apr 15, 2024
2b480cf
mypy
evangriffiths Apr 15, 2024
509ffa7
Remove duplicate bet_from_private_key
evangriffiths Apr 15, 2024
19b9348
Fix dependencies
evangriffiths Apr 15, 2024
331d8e5
Add setuptools dep
evangriffiths Apr 15, 2024
6f809e0
Add 'local' mech calling Function
evangriffiths Apr 15, 2024
f336af8
Add prediction mech as a git submodule
evangriffiths Apr 15, 2024
b10fbd6
Use submodules in CI
evangriffiths Apr 15, 2024
fc569d4
Fix ci
evangriffiths Apr 15, 2024
8ab4431
Fix ci
evangriffiths Apr 15, 2024
98cc683
Fix deps
evangriffiths Apr 15, 2024
e3ad0c7
Add comment
evangriffiths Apr 15, 2024
816c698
PredictPropabilityForQuestion -> PredictProbabilityForQuestion
evangriffiths Apr 16, 2024
55ad3d7
Update deps
evangriffiths Apr 16, 2024
3fcc7ea
Review comment
evangriffiths Apr 16, 2024
59604f8
Use latest PMAT release
evangriffiths Apr 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ __pycache__
logs
.mypy_cache
.pytest_cache

# Created on mech-client `interact` call
acn_cert.txt
3,198 changes: 768 additions & 2,430 deletions poetry.lock

Large diffs are not rendered by default.

43 changes: 34 additions & 9 deletions prediction_market_agent/agents/microchain_agent/functions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import json
import typing as t
from decimal import Decimal

from eth_utils import to_checksum_address
from mech_client.interact import interact
from microchain import Function
from prediction_market_agent_tooling.markets.agent_market import AgentMarket
from prediction_market_agent_tooling.markets.data_models import Currency, TokenAmount
Expand All @@ -12,13 +14,15 @@
)

from prediction_market_agent.agents.microchain_agent.utils import (
MechResult,
MicroMarket,
get_balance,
get_binary_markets,
get_boolean_outcome,
get_example_market_id,
get_no_outcome,
get_yes_outcome,
saved_str_to_tmpfile,
)
from prediction_market_agent.utils import APIKeys

Expand Down Expand Up @@ -101,19 +105,40 @@ def __call__(self, market_id: str) -> list[str]:
class PredictPropabilityForQuestion(MarketFunction):
@property
def description(self) -> str:
return "Use this function to research the probability of an event occuring"
return (
"Use this function to research perform research and predict the "
"probability of an event occuring. Returns the probability. The "
"one parameter is the market id of the prediction market you want "
"to predict the probability of. Note, this costs money to run."
)
evangriffiths marked this conversation as resolved.
Show resolved Hide resolved

@property
def example_args(self) -> list[str]:
return ["Will Joe Biden get reelected in 2024?"]

def __call__(self, a: str) -> float:
if a == "Will Joe Biden get reelected in 2024?":
return 0.41
if a == "Will Bitcoin hit 100k in 2024?":
return 0.22
return [get_example_market_id(self.market_type)]

return 0.0
def __call__(self, question: str) -> str:
private_key = APIKeys().bet_from_private_key.get_secret_value()
Copy link
Contributor

Choose a reason for hiding this comment

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

Storing private keys temporarily in files can be risky. Consider using more secure methods to handle private keys, such as using environment variables or secure vault solutions.

# 0.01 xDai is hardcoded cost for an interaction with the mech-client
MECH_CALL_XDAI_LIMIT = 0.011
account_balance = float(get_balance(market_type=self.market_type).amount)
if account_balance < MECH_CALL_XDAI_LIMIT:
return (
f"Your balance of {self.currency} ({account_balance}) is not "
f"large enough to make a mech call (min required "
f"{MECH_CALL_XDAI_LIMIT})."
)
with saved_str_to_tmpfile(private_key) as tmpfile_path:
response = interact(
Copy link
Contributor

Choose a reason for hiding this comment

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

This costs money to run. Should we do some kind of balance check here to see if the agent has enough funds? Otherwise this will throw an error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added. I don't think there's a way to query the cost of a mech call, so I've hard-coded it here.

I tried playing around with the value here that sets the cost of the mech call, and it fails for anything <0.01xDai, and works for anything >=.

Uploading Screenshot 2024-04-15 at 11.37.29.png…

prompt=question,
# Taken from https://github.com/valory-xyz/mech?tab=readme-ov-file#examples-of-deployed-mechs
agent_id=3,
private_key_path=tmpfile_path,
# To see a list of available tools, comment out the tool parameter
# and run the function. You will be prompted to select a tool.
tool="claude-prediction-online",
)
result = json.loads(response["result"])
return str(MechResult.model_validate(result).p_yes)
evangriffiths marked this conversation as resolved.
Show resolved Hide resolved


class BuyTokens(MarketFunction):
Expand Down
22 changes: 22 additions & 0 deletions prediction_market_agent/agents/microchain_agent/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import os
import tempfile
import typing as t
from contextlib import contextmanager
from decimal import Decimal

from prediction_market_agent_tooling.markets.agent_market import (
Expand Down Expand Up @@ -36,6 +39,13 @@ def __str__(self) -> str:
return f"'{self.question}', id: {self.id}"


class MechResult(BaseModel):
p_yes: float
p_no: float
confidence: float
info_utility: float


def get_binary_markets(market_type: MarketType) -> list[AgentMarket]:
# Get the 5 markets that are closing soonest
cls = market_type.market_class
Expand Down Expand Up @@ -89,3 +99,15 @@ def get_example_market_id(market_type: MarketType) -> str:
return "0x0020d13c89140b47e10db54cbd53852b90bc1391"
else:
raise ValueError(f"Market type '{market_type}' not supported")


@contextmanager
def saved_str_to_tmpfile(s: str) -> t.Iterator[str]:
# Write the string to the temporary file
with tempfile.NamedTemporaryFile(delete=False) as tmp:
tmp.write(s.encode())

yield tmp.name

# Finally remove the temporary file
os.remove(tmp.name)
8 changes: 8 additions & 0 deletions prediction_market_agent/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import typing as t

from prediction_market_agent_tooling.config import APIKeys as APIKeysBase
from prediction_market_agent_tooling.gtypes import PrivateKey
from prediction_market_agent_tooling.tools.utils import (
check_not_none,
should_not_happen,
Expand Down Expand Up @@ -31,6 +32,13 @@ def tavily_api_key(self) -> SecretStr:
self.TAVILY_API_KEY, "OPENAI_API_KEY missing in the environment."
)

@property
def bet_from_private_key(self) -> PrivateKey:
return check_not_none(
self.BET_FROM_PRIVATE_KEY,
"BET_FROM_PRIVATE_KEY missing in the environment.",
)


def get_market_prompt(question: str) -> str:
prompt = (
Expand Down
8 changes: 5 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,30 @@ google-search-results = "*"
pytest = "*"
llama-index = "~0.9.0"
duckduckgo-search = "*"
crewai = {extras = ["tools"], version = "^0.22.5"}
crewai = {extras = ["tools"], version = "^0.11.0"}
# metagpt = "*" # Commented out because requires super old version of langchain, and conflicts with crewai.
replicate = "*"
typer = "^0.9.0"
web3 = "^6.14.0"
mypy = "^1.8.0"
types-requests = "^2.31.0.20240106"
black = "^23.12.1"
google-cloud-functions = "^1.16.0"
google-cloud-resource-manager = "^1.12.0"
poetry = "^1.7.1"
poetry-plugin-export = "^1.6.0"
functions-framework = "^3.5.0"
cron-validator = "^1.0.8"
prediction-market-agent-tooling = "^0.13.1"
prediction-market-agent-tooling = {git = "https://github.com/gnosis/prediction-market-agent-tooling.git", rev = "evan/add-mech-client-dependency"}
pydantic-settings = "^2.1.0"
autoflake = "^2.2.1"
isort = "^5.13.2"
markdownify = "^0.11.6"
tavily-python = "^0.3.1"
# TODO remove when PR is merged
microchain-python = {git = "https://github.com/evangriffiths/microchain.git", rev = "evan/token-tracker"}
types-requests = "2.31.0.0"
setuptools = "^69.2.0"
mech-client = "^0.2.13"

[build-system]
requires = ["poetry-core"]
Expand Down
32 changes: 16 additions & 16 deletions tests/agents/microchain/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,11 @@
from prediction_market_agent_tooling.tools.hexbytes_custom import HexBytes

from prediction_market_agent.agents.microchain_agent.functions import (
MARKET_FUNCTIONS,
MISC_FUNCTIONS,
BuyNo,
BuyYes,
GetBalance,
GetMarketProbability,
GetMarkets,
GetUserPositions,
SellNo,
SellYes,
)
MARKET_FUNCTIONS, MISC_FUNCTIONS, BuyNo, BuyYes, GetBalance,
GetMarketProbability, GetMarkets, GetUserPositions,
PredictPropabilityForQuestion, SellNo, SellYes)
from prediction_market_agent.agents.microchain_agent.utils import (
get_balance,
get_binary_markets,
get_no_outcome,
get_yes_outcome,
)
get_balance, get_binary_markets, get_no_outcome, get_yes_outcome)
from prediction_market_agent.utils import APIKeys
from tests.utils import RUN_PAID_TESTS

Expand Down Expand Up @@ -160,3 +148,15 @@ def get_balances() -> tuple[float, float]:
n_tokens_bought = after_tokens - before_tokens
n_tokens_sold = after_tokens - final_tokens
assert np.isclose(n_tokens_bought, n_tokens_sold, rtol=0.02)


@pytest.mark.skipif(not RUN_PAID_TESTS, reason="This test costs money to run.")
@pytest.mark.parametrize("market_type", [MarketType.OMEN])
def test_predict_probability(market_type: MarketType) -> None:
"""
Test calling a mech to predict the probability of a market
"""
predict_probability = PredictPropabilityForQuestion(market_type=market_type)
market = get_binary_markets(market_type=market_type)[0]
p_yes = predict_probability(market.id)
assert 0.0 <= float(p_yes) <= 1.0
Loading