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

Agent to replicate markets to Omen #61

Merged
merged 49 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
dffb701
Agent to replicate markets to Omen
kongzii Feb 28, 2024
56dcede
black
kongzii Feb 28, 2024
2c79d73
fix ifx
kongzii Feb 28, 2024
f59d846
Batch deposit
kongzii Feb 28, 2024
1e201d3
fix utc
kongzii Feb 28, 2024
52b7bc1
reuse variable
kongzii Feb 28, 2024
54a4e1c
disable poly
kongzii Feb 28, 2024
78a3513
Fix polymarket
kongzii Feb 28, 2024
d00b8ff
add to log
kongzii Feb 28, 2024
aa6a94b
add to print
kongzii Feb 28, 2024
2c73169
add print
kongzii Feb 28, 2024
0ae0908
add timeout
kongzii Feb 29, 2024
5d20946
fix timezones
kongzii Mar 1, 2024
ea928f5
force at least 24h open market
kongzii Mar 1, 2024
30f7ecc
Add cache and is predictable from evo
kongzii Mar 4, 2024
d100a09
fix tests
kongzii Mar 4, 2024
a1535c0
Merge branch 'main' into peter/replication
kongzii Mar 4, 2024
86de9e8
update lock
kongzii Mar 4, 2024
b3f4124
use validator
kongzii Mar 4, 2024
b1ff99c
fix
kongzii Mar 4, 2024
d5b1241
add _add_timezone_validator
kongzii Mar 4, 2024
9f263e4
fix after merge
kongzii Mar 4, 2024
9dc3890
lint
kongzii Mar 4, 2024
ad5ef62
fix after merge 2
kongzii Mar 4, 2024
5b3de61
Update prediction_market_agent_tooling/tools/is_predictable.py
kongzii Mar 4, 2024
bf98065
Update prediction_market_agent_tooling/tools/is_predictable.py
kongzii Mar 4, 2024
401af4c
refac infer_category
kongzii Mar 4, 2024
6895620
make langchain optional
kongzii Mar 4, 2024
082069d
Improve is_predictable
kongzii Mar 4, 2024
a9e7470
Merge branch 'main' into peter/replication
kongzii Mar 4, 2024
e414107
add extra to the deployment
kongzii Mar 4, 2024
da3c54a
fix for nones after merges
kongzii Mar 4, 2024
81586ec
more fixes after merges :(
kongzii Mar 4, 2024
4d154ba
Update is_predictable prompt and move test from evo repo
kongzii Mar 4, 2024
9bd41bb
fix pydantic warning
kongzii Mar 4, 2024
5e8a300
fix test
kongzii Mar 4, 2024
ddc1b17
Demo move DeployableReplicateToOmenAgent to examples
evangriffiths Mar 5, 2024
77b0738
Update branch to deploy from
evangriffiths Mar 5, 2024
bf05380
Update branch to deploy from
evangriffiths Mar 5, 2024
3218f9e
Add langchain dependency
evangriffiths Mar 5, 2024
449c67d
Fix circular imports
evangriffiths Mar 5, 2024
1a59e7f
Point to commit hash
evangriffiths Mar 5, 2024
49fbbbd
Fix mypy
evangriffiths Mar 5, 2024
bec89a0
Merge pull request #67 from gnosis/evan/replication
evangriffiths Mar 5, 2024
6718904
Small tweaks
kongzii Mar 6, 2024
57ef5b8
Merge branch 'main' into peter/replication
kongzii Mar 6, 2024
89f3cb3
More tweaks
kongzii Mar 6, 2024
e883829
fix
kongzii Mar 6, 2024
de35a4b
fix
kongzii Mar 6, 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
2 changes: 1 addition & 1 deletion .github/actions/python_prepare/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ runs:
run: curl -sSL https://install.python-poetry.org | python3 -
- name: Install dependencies
shell: bash
run: poetry install
run: poetry install --all-extras
55 changes: 43 additions & 12 deletions examples/cloud_deployment/gcp/deploy.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,81 @@
import getpass
import json
from enum import Enum

import typer
from pydantic.types import SecretStr
from web3 import Web3

from prediction_market_agent_tooling.config import APIKeys
from prediction_market_agent_tooling.deploy.agent_example import (
DeployableAgent,
DeployableAlwaysRaiseAgent,
DeployableCoinFlipAgent,
)
from prediction_market_agent_tooling.gtypes import PrivateKey
from prediction_market_agent_tooling.gtypes import private_key_type
from prediction_market_agent_tooling.markets.markets import MarketType
from prediction_market_agent_tooling.tools.web3_utils import verify_address


class AgentName(str, Enum):
coin_flip = "coin_flip"
always_raise = "always_raise"


def main(
agent_name: str,
agent_name: AgentName,
cron_schedule: str = "0 */2 * * *",
github_repo_url: str = "https://github.com/gnosis/prediction-market-agent-tooling",
branch: str = "main",
custom_gcp_fname: str | None = None,
market_type: MarketType = MarketType.MANIFOLD,
manifold_api_key_secret_name: str | None = None,
openai_api_key_secret_name: str | None = None,
bet_from_address: str | None = None,
bet_from_private_key_secret_name: str | None = None,
env_vars: str | None = None,
secrets: str | None = None,
timeout: int = 180,
) -> None:
agent: DeployableAgent = {
"coin_flip": DeployableCoinFlipAgent,
"always_raise": DeployableAlwaysRaiseAgent,
AgentName.coin_flip: DeployableCoinFlipAgent,
AgentName.always_raise: DeployableAlwaysRaiseAgent,
}[agent_name]()
agent.deploy_gcp(
repository=f"git+{github_repo_url}.git@{branch}",
market_type=market_type,
labels={
# Only lowercase letters, numbers, hyphens and underscores are allowed.
"owner": getpass.getuser()
}, # Only lowercase letters, numbers, hyphens and underscores are allowed.
},
env_vars=json.loads(env_vars) if env_vars else None,
# You can allow the cloud function to access secrets by adding the role: `gcloud projects add-iam-policy-binding ${GCP_PROJECT_ID} --member=serviceAccount:${GCP_SVC_ACC} --role=roles/container.admin`.
# Must be in the format "env_var_in_container => secret_name:version", you can create secrets using `gcloud secrets create --labels owner=<your-name> <secret-name>` command.
secrets=json.loads(secrets) if secrets else None,
Comment on lines +50 to +53
Copy link

Choose a reason for hiding this comment

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

The handling of env_vars and secrets using JSON parsing allows for a more dynamic configuration. However, as previously mentioned, consider adding error handling for JSON parsing to gracefully handle parsing errors and improve robustness.

- env_vars=json.loads(env_vars) if env_vars else None,
+ env_vars=try_parse_json(env_vars),
- secrets=json.loads(secrets) if secrets else None,
+ secrets=try_parse_json(secrets),

+ def try_parse_json(json_str):
+     try:
+         return json.loads(json_str) if json_str else None
+     except json.JSONDecodeError:
+         log.error("Failed to parse JSON string.")
+         return None

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
env_vars=json.loads(env_vars) if env_vars else None,
# You can allow the cloud function to access secrets by adding the role: `gcloud projects add-iam-policy-binding ${GCP_PROJECT_ID} --member=serviceAccount:${GCP_SVC_ACC} --role=roles/container.admin`.
# Must be in the format "env_var_in_container => secret_name:version", you can create secrets using `gcloud secrets create --labels owner=<your-name> <secret-name>` command.
secrets=json.loads(secrets) if secrets else None,
env_vars=try_parse_json(env_vars),
# You can allow the cloud function to access secrets by adding the role: `gcloud projects add-iam-policy-binding ${GCP_PROJECT_ID} --member=serviceAccount:${GCP_SVC_ACC} --role=roles/container.admin`.
# Must be in the format "env_var_in_container => secret_name:version", you can create secrets using `gcloud secrets create --labels owner=<your-name> <secret-name>` command.
secrets=try_parse_json(secrets),
def try_parse_json(json_str):
try:
return json.loads(json_str) if json_str else None
except json.JSONDecodeError:
log.error("Failed to parse JSON string.")
return None

memory=512,
api_keys=APIKeys(
BET_FROM_ADDRESS=Web3.to_checksum_address(
"0x3666DA333dAdD05083FEf9FF6dDEe588d26E4307"
BET_FROM_ADDRESS=(
verify_address(bet_from_address) if bet_from_address else None
),
# For GCP deployment, passwords, private keys, api keys, etc. must be stored in Secret Manager and here, only their name + version is passed.
MANIFOLD_API_KEY=SecretStr("JUNG_PERSONAL_GMAIL_MANIFOLD_API_KEY:latest"),
BET_FROM_PRIVATE_KEY=PrivateKey(
"0x3666DA333dAdD05083FEf9FF6dDEe588d26E4307:latest"
MANIFOLD_API_KEY=(
SecretStr(manifold_api_key_secret_name)
if manifold_api_key_secret_name
else None
),
BET_FROM_PRIVATE_KEY=(
private_key_type(bet_from_private_key_secret_name)
if bet_from_private_key_secret_name
else None
),
OPENAI_API_KEY=(
SecretStr(openai_api_key_secret_name)
if openai_api_key_secret_name
else None
),
),
memory=256,
cron_schedule=cron_schedule,
gcp_fname=custom_gcp_fname,
timeout=timeout,
)


Expand Down
80 changes: 80 additions & 0 deletions examples/replicate_markets/agent_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from datetime import datetime, timedelta

import functions_framework
import pytz
from flask import Request
from pydantic_settings import BaseSettings, SettingsConfigDict

from prediction_market_agent_tooling.benchmark.utils import MarketSource
from prediction_market_agent_tooling.config import APIKeys
from prediction_market_agent_tooling.deploy.agent import DeployableAgent, MarketType
from prediction_market_agent_tooling.gtypes import xdai_type
from prediction_market_agent_tooling.markets.omen.omen import (
omen_create_market_deposit_tx,
omen_replicate_from_tx,
)


class ReplicateSettings(BaseSettings):
model_config = SettingsConfigDict(
env_file=".env", env_file_encoding="utf-8", extra="ignore"
)

N_TO_REPLICATE: int
INITIAL_FUNDS: str
CLOSE_TIME_UP_TO_N_DAYS: int


class DeployableReplicateToOmenAgent(DeployableAgent):
def run(
self, market_type: MarketType = MarketType.MANIFOLD, _place_bet: bool = True
) -> None:
keys = APIKeys()
settings = ReplicateSettings()
close_time_before = (
datetime.utcnow() + timedelta(days=settings.CLOSE_TIME_UP_TO_N_DAYS)
).replace(tzinfo=pytz.UTC)
initial_funds_per_market = xdai_type(settings.INITIAL_FUNDS)
deposit_funds_per_replication = xdai_type(
initial_funds_per_market * settings.N_TO_REPLICATE
)

print(f"Replicating from {MarketSource.MANIFOLD}.")
# Deposit enough of xDai for all N markets to be replicated, so we don't re-deposit in case of re-tries.
omen_create_market_deposit_tx(
deposit_funds_per_replication,
keys.bet_from_address,
keys.bet_from_private_key,
)
omen_replicate_from_tx(
market_source=MarketSource.MANIFOLD,
n_to_replicate=settings.N_TO_REPLICATE,
initial_funds=initial_funds_per_market,
from_address=keys.bet_from_address,
from_private_key=keys.bet_from_private_key,
close_time_before=close_time_before,
auto_deposit=False,
)
print(f"Replicating from {MarketSource.POLYMARKET}.")
# Deposit enough of xDai for all N markets to be replicated, so we don't re-deposit in case of re-tries.
omen_create_market_deposit_tx(
deposit_funds_per_replication,
keys.bet_from_address,
keys.bet_from_private_key,
)
omen_replicate_from_tx(
market_source=MarketSource.POLYMARKET,
n_to_replicate=settings.N_TO_REPLICATE,
initial_funds=initial_funds_per_market,
from_address=keys.bet_from_address,
from_private_key=keys.bet_from_private_key,
close_time_before=close_time_before,
auto_deposit=False,
)
print("Done.")


@functions_framework.http
def main(request: Request) -> str:
DeployableReplicateToOmenAgent().run()
return "Success"
Comment on lines +28 to +80
Copy link

Choose a reason for hiding this comment

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

The DeployableReplicateToOmenAgent class provides a comprehensive implementation for replicating markets to Omen from both Manifold and Polymarket sources. The use of the ReplicateSettings class for configuration management and the structured approach to handling API keys and settings are commendable practices that enhance maintainability and readability.

However, there are a couple of areas that could be improved:

  1. Error Handling: Consider adding error handling around external calls, such as omen_create_market_deposit_tx and omen_replicate_from_tx, to gracefully handle any failures or exceptions that might occur during the replication process.
  2. Duplication: The code for depositing funds and replicating markets is duplicated for both Manifold and Polymarket sources. This could be refactored into a separate method to reduce duplication and improve code maintainability.

Consider refactoring the code to reduce duplication and add error handling around external calls. Here's a suggested approach for refactoring:

def replicate_markets(self, market_source):
    print(f"Replicating from {market_source}.")
    # Deposit enough of xDai for all N markets to be replicated, so we don't re-deposit in case of re-tries.
    omen_create_market_deposit_tx(
        self.deposit_funds_per_replication,
        self.keys.bet_from_address,
        self.keys.bet_from_private_key,
    )
    omen_replicate_from_tx(
        market_source=market_source,
        n_to_replicate=self.settings.N_TO_REPLICATE,
        initial_funds=self.initial_funds_per_market,
        from_address=self.keys.bet_from_address,
        from_private_key=self.keys.bet_from_private_key,
        close_time_before=self.close_time_before,
        auto_deposit=False,
    )

And then call this method for each market source in the run method.

39 changes: 39 additions & 0 deletions examples/replicate_markets/deploy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import os

from prediction_market_agent_tooling.deploy.gcp.deploy import (
deploy_to_gcp,
run_deployed_gcp_function,
schedule_deployed_gcp_function,
)
from prediction_market_agent_tooling.deploy.gcp.utils import gcp_function_is_active

fname = "replicate_markets"
current_dir = os.path.dirname(os.path.abspath(__file__))
deploy_to_gcp(
gcp_fname=fname,
requirements_file=None,
extra_deps=[
"git+https://github.com/gnosis/prediction-market-agent-tooling.git@449c67df2ec02f61411e153565c5e3f8ba01dda1",
"langchain",
"langchain_openai",
],
function_file=os.path.join(current_dir, "agent_example.py"),
memory=512,
entrypoint_function_name="main",
timeout=180,
labels=None,
env_vars=None, # TODO add env vars
secrets=None, # TODO add secrets
Copy link

Choose a reason for hiding this comment

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

The TODO comments on lines 25 and 26 indicate that environment variables and secrets need to be added for the deployment. It's crucial to address these TODO items before proceeding with the deployment to ensure that the deployed function has all the necessary configurations and access to secure resources it might need to operate correctly.

Would you like me to help with defining the environment variables and secrets or open a GitHub issue to track this task?

)

# Check that the function is deployed
if not gcp_function_is_active(fname):
raise RuntimeError("Failed to deploy the function")

# Run the function
response = run_deployed_gcp_function(fname)
if not response.ok:
raise RuntimeError("Failed to run the deployed function")

# Schedule the function
schedule_deployed_gcp_function(fname, cron_schedule="0 */6 * * *")
Loading
Loading