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

Add functionality to decide for the bet's choice #2

Merged
merged 41 commits into from
Jul 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
389c58c
feat: add mech-client to the dependencies
Adamantios Jul 6, 2023
196e463
fix: add missing `http` protocol to the market manager abci
Adamantios Jul 6, 2023
64ceb66
feat: create functionality to decide for the bet's choice
Adamantios Jul 6, 2023
b18834a
refactor: move behaviours to package
Adamantios Jul 6, 2023
83d9713
refactor: bets include the market
Adamantios Jul 6, 2023
a927672
refactor: the mech task accepts the prompt as an input
Adamantios Jul 6, 2023
a341a78
refactor: rename `message` to `error`
Adamantios Jul 6, 2023
3dd7d4e
feat: add `vote` property
Adamantios Jul 6, 2023
5016975
refactor: `vote` and `confidence` can be `None`
Adamantios Jul 6, 2023
7c38c5e
feat: implement yes and no properties for the bet
Adamantios Jul 6, 2023
e17519a
fix: decision maker is a collect same until threshold round
Adamantios Jul 6, 2023
f0ab8c8
refactor: sanitize outcomes & do not keep bets without any
Adamantios Jul 7, 2023
e1aa303
fix: avoid `StopIteration` and return `None` as a default
Adamantios Jul 7, 2023
4a57995
style: correct type hint
Adamantios Jul 7, 2023
4dd4847
refactor: move the response key into the api spec
Adamantios Jul 7, 2023
af675b2
refactor: raise if the outcome is non-binary
Adamantios Jul 7, 2023
f248c0a
fix: bets is a list
Adamantios Jul 7, 2023
aa378a7
feat: improve the `outcomes` logic
Adamantios Jul 7, 2023
9099c7b
feat: finish the decision-making core logic
Adamantios Jul 7, 2023
14b0da0
chore: add decision maker to the generators and the checks
Adamantios Jul 7, 2023
0208841
refactor: fail early in case of unsupported number of slots
Adamantios Jul 7, 2023
41b53aa
fix: correct the return value for the vote
Adamantios Jul 7, 2023
93a96ef
chore: linting
Adamantios Jul 7, 2023
50dc1cb
feat: implement check for profitability of a bet
Adamantios Jul 7, 2023
dcb41ea
fix: private key file's path
Adamantios Jul 7, 2023
a101113
refactor: no need to define 2 constants for the same thing
Adamantios Jul 7, 2023
e3859b3
chore: bump `mech-client`
Adamantios Jul 7, 2023
e32db39
refactor: extract reusable behaviour methods to base class
Adamantios Jul 7, 2023
6998ca1
docs: improve module's docstring
Adamantios Jul 7, 2023
05bdcdb
refactor: move serialization method in the bets' structures
Adamantios Jul 7, 2023
bece9e4
feat: add functionality for temporarily blacklisting a bet
Adamantios Jul 7, 2023
7c659a2
feat: add functionality for sampling a bet as a first step
Adamantios Jul 7, 2023
f80bd8c
style: remove superfluous statuses
Adamantios Jul 7, 2023
fbe02f1
style: rename `sampled_bet_id` to `sampled_bet_index`
Adamantios Jul 7, 2023
45446a2
refactor: use `SynchronizedData` instead of the base class
Adamantios Jul 7, 2023
d87a2ea
docs: correct blacklisting round's docstring
Adamantios Jul 7, 2023
e8277f5
refactor: extract rounds into dedicated package
Adamantios Jul 7, 2023
2696f74
fix: add missing final state and missing behaviours
Adamantios Jul 7, 2023
c8920d2
refactor: structure abci app in a way it can be parsed
Adamantios Jul 7, 2023
e4855ec
Merge pull request #4 from valory-xyz/feat/sampling
Adamantios Jul 10, 2023
f64bdcb
Merge pull request #3 from valory-xyz/feat/blacklisting
Adamantios Jul 10, 2023
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
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ all-checks: clean format code-checks security generators common-checks-1 common-
.PHONY: fix-abci-app-specs
fix-abci-app-specs:
autonomy analyse fsm-specs --update --app-class MarketManagerAbciApp --package packages/valory/skills/market_manager_abci
autonomy analyse fsm-specs --update --app-class DecisionMakerAbciApp --package packages/valory/skills/decision_maker_abci
echo "Successfully validated abcis!"

protolint_install:
Expand Down
5 changes: 5 additions & 0 deletions packages/valory/skills/decision_maker_abci/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# DecisionMaker abci

## Description

This module contains the ABCI decision-making skill for an AEA.
25 changes: 25 additions & 0 deletions packages/valory/skills/decision_maker_abci/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
#
# Copyright 2023 Valory AG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ------------------------------------------------------------------------------

"""This module contains the decision maker skill for the trader."""

from aea.configurations.base import PublicId


PUBLIC_ID = PublicId.from_str("valory/decision_maker_abci:0.1.0")
20 changes: 20 additions & 0 deletions packages/valory/skills/decision_maker_abci/behaviours/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
#
# Copyright 2023 Valory AG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ------------------------------------------------------------------------------

"""This package contains the behaviours for the 'decision_maker_abci' skill."""
50 changes: 50 additions & 0 deletions packages/valory/skills/decision_maker_abci/behaviours/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
#
# Copyright 2023 Valory AG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ------------------------------------------------------------------------------

"""This module contains the base behaviour for the 'decision_maker_abci' skill."""

from abc import ABC
from typing import Generator, cast

from packages.valory.skills.abstract_round_abci.base import BaseTxPayload
from packages.valory.skills.abstract_round_abci.behaviour_utils import BaseBehaviour
from packages.valory.skills.decision_maker_abci.models import DecisionMakerParams
from packages.valory.skills.decision_maker_abci.states.base import SynchronizedData


class DecisionMakerBaseBehaviour(BaseBehaviour, ABC):
"""Represents the base class for the decision-making FSM behaviour."""

@property
def params(self) -> DecisionMakerParams:
"""Return the params."""
return cast(DecisionMakerParams, self.context.params)

@property
def synchronized_data(self) -> SynchronizedData:
"""Return the synchronized data."""
return cast(SynchronizedData, super().synchronized_data)

def finish_behaviour(self, payload: BaseTxPayload) -> Generator:
"""Finish the behaviour."""
with self.context.benchmark_tool.measure(self.behaviour_id).consensus():
yield from self.send_a2a_transaction(payload)
yield from self.wait_until_round_end()

self.set_done()
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
#
# Copyright 2023 Valory AG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ------------------------------------------------------------------------------

"""This module contains the behaviour for the blacklisting of the sampled bet."""

from typing import Generator, Optional

from packages.valory.skills.decision_maker_abci.behaviours.base import (
DecisionMakerBaseBehaviour,
)
from packages.valory.skills.decision_maker_abci.states.blacklisting import (
BlacklistingRound,
)
from packages.valory.skills.market_manager_abci.bets import BetStatus, serialize_bets
from packages.valory.skills.market_manager_abci.payloads import UpdateBetsPayload


class BlacklistingBehaviour(DecisionMakerBaseBehaviour):
"""A behaviour in which the agents blacklist the sampled bet."""

matching_round = BlacklistingRound

@property
def synced_time(self) -> float:
"""Get the synchronized time among agents."""
synced_time = self.shared_state.round_sequence.last_round_transition_timestamp
return synced_time.timestamp()

def _blacklist(self) -> Optional[str]:
"""Blacklist the sampled bet and return the updated version of the bets, serialized."""
bets = self.synchronized_data.bets
sampled_bet_index = self.synchronized_data.sampled_bet_index
sampled_bet = bets[sampled_bet_index]
sampled_bet.status = BetStatus.BLACKLISTED
blacklist_expiration = self.synced_time + self.params.blacklisting_duration
sampled_bet.blacklist_expiration = blacklist_expiration

return serialize_bets(bets)

def async_act(self) -> Generator:
"""Do the action."""

with self.context.benchmark_tool.measure(self.behaviour_id).local():
payload = UpdateBetsPayload(self.context.agent_address, self._blacklist())

yield from self.finish_behaviour(payload)
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
#
# Copyright 2023 Valory AG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ------------------------------------------------------------------------------

"""This module contains the behaviour for the decision-making of the skill."""

from multiprocessing.pool import AsyncResult
from pathlib import Path
from string import Template
from typing import Any, Generator, Optional, Tuple, cast

from mech_client.interact import PRIVATE_KEY_FILE_PATH

from packages.valory.skills.decision_maker_abci.behaviours.base import (
DecisionMakerBaseBehaviour,
)
from packages.valory.skills.decision_maker_abci.payloads import DecisionMakerPayload
from packages.valory.skills.decision_maker_abci.states.decision_maker import (
DecisionMakerRound,
)
from packages.valory.skills.decision_maker_abci.tasks import (
MechInteractionResponse,
MechInteractionTask,
)
from packages.valory.skills.market_manager_abci.bets import BINARY_N_SLOTS


BET_PROMPT = Template(
"""
With the given question "${question}"
and the `yes` option represented by ${yes}
and the `no` option represented by ${no},
what are the respective probabilities of `p_yes` and `p_no` occurring?
"""
)


class DecisionMakerBehaviour(DecisionMakerBaseBehaviour):
"""A behaviour in which the agents decide which answer they are going to choose for the next bet."""

matching_round = DecisionMakerRound

def __init__(self, **kwargs: Any) -> None:
"""Initialize Behaviour."""
super().__init__(**kwargs)
self._async_result: Optional[AsyncResult] = None

@property
def n_slots_unsupported(self) -> bool:
"""Whether the behaviour supports the current number of slots as it currently only supports binary decisions."""
return self.params.slot_count != BINARY_N_SLOTS

def setup(self) -> None:
"""Setup behaviour."""
if self.n_slots_unsupported:
return

mech_task = MechInteractionTask()
sampled_bet = self.synchronized_data.sampled_bet
prompt_params = dict(
question=sampled_bet.title, yes=sampled_bet.yes, no=sampled_bet.no
)
task_kwargs = dict(
prompt=BET_PROMPT.substitute(prompt_params),
agent_id=self.params.mech_agent_id,
tool=self.params.mech_tool,
private_key_path=str(Path(self.context.data_dir) / PRIVATE_KEY_FILE_PATH),
)
task_id = self.context.task_manager.enqueue_task(mech_task, kwargs=task_kwargs)
self._async_result = self.context.task_manager.get_task_result(task_id)

def _get_decision(
self,
) -> Generator[None, None, Optional[Tuple[Optional[int], Optional[float]]]]:
"""Get the vote and it's confidence."""
if self._async_result is None:
return None, None

if not self._async_result.ready():
self.context.logger.debug("The decision making task is not finished yet.")
yield from self.sleep(self.params.sleep_time)
return None

# Get the decision from the task.
mech_response = cast(MechInteractionResponse, self._async_result.get())
self.context.logger.info(f"Decision has been received:\n{mech_response}")

if mech_response.prediction is None:
self.context.logger.info(
f"There was an error on the mech response: {mech_response.error}"
)
return None, None

return mech_response.prediction.vote, mech_response.prediction.confidence

def _is_profitable(self, vote: Optional[int], confidence: Optional[float]) -> bool:
"""Whether the decision is profitable or not."""
if vote is None or confidence is None:
return False

bet_amount = self.params.get_bet_amount(confidence)
fee = self.synchronized_data.sampled_bet.fee
bet_threshold = self.params.bet_threshold
return bet_amount - fee >= bet_threshold

def async_act(self) -> Generator:
"""Do the action."""

with self.context.benchmark_tool.measure(self.behaviour_id).local():
decision = yield from self._get_decision()
if decision is None:
return

vote, confidence = decision
is_profitable = self._is_profitable(vote, confidence)
payload = DecisionMakerPayload(
self.context.agent_address,
self.n_slots_unsupported,
is_profitable,
vote,
confidence,
)

yield from self.finish_behaviour(payload)
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
#
# Copyright 2023 Valory AG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ------------------------------------------------------------------------------

"""This module contains the round behaviour for the 'decision_maker_abci' skill."""

from typing import Set, Type

from packages.valory.skills.abstract_round_abci.behaviours import (
AbstractRoundBehaviour,
BaseBehaviour,
)
from packages.valory.skills.decision_maker_abci.behaviours.blacklisting import (
BlacklistingBehaviour,
)
from packages.valory.skills.decision_maker_abci.behaviours.decision_maker import (
DecisionMakerBehaviour,
)
from packages.valory.skills.decision_maker_abci.behaviours.sampling import (
SamplingBehaviour,
)
from packages.valory.skills.decision_maker_abci.rounds import DecisionMakerAbciApp


class AgentDecisionMakerRoundBehaviour(AbstractRoundBehaviour):
"""This behaviour manages the consensus stages for the decision-making."""

initial_behaviour_cls = DecisionMakerBehaviour
abci_app_cls = DecisionMakerAbciApp
behaviours: Set[Type[BaseBehaviour]] = {
SamplingBehaviour, # type: ignore
DecisionMakerBehaviour, # type: ignore
BlacklistingBehaviour, # type: ignore
}
Loading