From dcc4101bd45dcd53a6bd5cfdf8ea1af0a40fcad2 Mon Sep 17 00:00:00 2001
From: S Ali Hosseini <38721653+5A11@users.noreply.github.com>
Date: Tue, 17 Jan 2023 14:59:25 +0000
Subject: [PATCH] docs: documentation cleanup
---
.github/PULL_REQUEST_TEMPLATE/release.md | 2 +-
AUTHORS.md | 24 +-
CODE_OF_CONDUCT.md | 26 +-
CONTRIBUTING.md | 38 +-
DEVELOPING.md | 161 ++-
HISTORY.md | 57 +-
README.md | 8 +-
SECURITY.md | 2 +-
aea/connections/scaffold/connection.yaml | 2 +-
aea/connections/scaffold/readme.md | 2 +
benchmark/README.md | 17 +-
deploy-image/README.md | 7 +-
develop-image/README.md | 92 +-
docs/12-factor.md | 217 +--
docs/acn-internals.md | 525 +++----
docs/acn.md | 27 +-
docs/aea-vs-mvc.md | 8 +-
docs/agent-oriented-development.md | 50 +-
docs/agent-vs-aea.md | 278 ++--
docs/aggregation-demo.md | 88 +-
docs/app-areas.md | 39 +-
docs/aries-cloud-agent-demo.md | 168 ++-
docs/assets/images/favicon.ico | Bin 0 -> 1150 bytes
docs/assets/images/logo.png | Bin 0 -> 14201 bytes
docs/build-aea-programmatically.md | 268 ++--
docs/build-aea-step-by-step.md | 21 +-
docs/car-park-skills.md | 176 +--
docs/cli-commands.md | 100 +-
docs/cli-how-to.md | 16 +-
docs/cli-vs-programmatic-aeas.md | 479 ++++---
docs/config.md | 22 +-
docs/connect-a-frontend.md | 6 +-
docs/connection.md | 25 +-
docs/contract.md | 37 +-
docs/core-components-1.md | 53 +-
docs/core-components-2.md | 26 +-
docs/css/my-styles.css | 86 +-
docs/debug.md | 5 +-
docs/decision-maker-transaction.md | 555 ++++----
docs/decision-maker.md | 30 +-
docs/defining-data-models.md | 36 +-
docs/demos.md | 4 +-
docs/deployment.md | 4 +-
docs/design-principles.md | 20 +-
docs/development-setup.md | 32 +-
docs/diagram.md | 29 +-
docs/erc1155-skills.md | 207 ++-
docs/generic-skills-step-by-step.md | 85 +-
docs/generic-skills.md | 155 +-
docs/generic-storage.md | 38 +-
docs/glossary.md | 10 +-
docs/gym-example.md | 17 +-
docs/gym-skill.md | 79 +-
docs/http-connection-and-skill.md | 20 +-
docs/identity.md | 9 +-
docs/index.md | 57 +-
docs/interaction-protocol.md | 25 +-
docs/known-limits.md | 4 +-
docs/language-agnostic-definition.md | 246 ++--
docs/ledger-integration.md | 39 +-
docs/limits.md | 100 +-
docs/logging.md | 10 +-
docs/message-routing.md | 24 +-
docs/ml-skills.md | 211 ++-
docs/modes.md | 1 +
docs/multi-agent-manager.md | 15 +-
docs/multiplexer-standalone.md | 233 +--
docs/oef-ledger.md | 13 +-
docs/oracle-demo.md | 439 +++---
docs/orm-integration.md | 166 ++-
docs/p2p-connection.md | 112 +-
docs/package-imports.md | 21 +-
docs/performance-benchmark.md | 77 +-
docs/por.md | 3 +-
docs/prometheus.md | 212 +--
docs/protocol-generator.md | 116 +-
docs/protocol.md | 111 +-
docs/query-language.md | 54 +-
docs/questions-and-answers.md | 101 +-
docs/quickstart.md | 421 +++---
docs/raspberry-set-up.md | 77 +-
docs/runtime-cost.md | 6 +-
docs/scaffolding.md | 17 +-
docs/security.md | 3 +-
docs/simple-oef-usage.md | 45 +-
docs/skill-guide.md | 1192 ++++++++--------
docs/skill-testing.md | 32 +-
docs/skill.md | 71 +-
docs/standalone-transaction.md | 157 +-
docs/step-one.md | 18 +-
docs/tac-skills-contract.md | 557 ++++----
docs/tac-skills.md | 299 ++--
docs/tac.md | 25 +-
docs/thermometer-skills.md | 162 +--
docs/trust.md | 6 +-
docs/upgrading.md | 155 +-
docs/version.md | 4 +-
docs/vision.md | 26 +-
docs/wealth.md | 37 +-
docs/weather-skills.md | 178 +--
examples/aealite_go/README.md | 2 +-
examples/gym_ex/README.md | 2 +-
examples/tac_deploy/README.md | 22 +-
libs/go/aealite/README.md | 115 +-
libs/go/libp2p_node/README.md | 11 +-
mkdocs.yml | 109 +-
packages/fetchai/agents/aries_alice/README.md | 8 +-
packages/fetchai/agents/aries_faber/README.md | 17 +-
.../fetchai/agents/car_data_buyer/README.md | 6 +-
.../fetchai/agents/car_detector/README.md | 4 +-
.../fetchai/agents/coin_price_feed/README.md | 2 +-
.../agents/coin_price_oracle/README.md | 3 +-
.../agents/coin_price_oracle_client/README.md | 1 -
.../fetchai/agents/erc1155_client/README.md | 4 +-
.../fetchai/agents/erc1155_deployer/README.md | 4 +-
.../fetchai/agents/fipa_dummy_buyer/README.md | 2 +-
.../fetchai/agents/generic_buyer/README.md | 6 +-
.../fetchai/agents/generic_seller/README.md | 6 +-
packages/fetchai/agents/gym_aea/README.md | 8 +-
packages/fetchai/agents/hello_world/README.md | 4 +-
.../agents/latest_block_feed/README.md | 2 +-
.../fetchai/agents/ml_data_provider/README.md | 6 +-
.../fetchai/agents/ml_model_trainer/README.md | 4 +-
.../fetchai/agents/my_first_aea/README.md | 4 +-
.../agents/simple_aggregator/README.md | 1 -
.../simple_service_registration/README.md | 2 +-
.../agents/simple_service_search/README.md | 2 +-
.../fetchai/agents/tac_controller/README.md | 4 +-
.../agents/tac_controller_contract/README.md | 4 +-
.../fetchai/agents/tac_participant/README.md | 4 +-
.../agents/tac_participant_contract/README.md | 4 +-
.../fetchai/agents/thermometer_aea/README.md | 6 +-
.../agents/thermometer_client/README.md | 4 +-
.../fetchai/agents/weather_client/README.md | 4 +-
.../fetchai/agents/weather_station/README.md | 6 +-
packages/fetchai/connections/gym/README.md | 2 +-
.../fetchai/connections/gym/connection.yaml | 2 +-
.../fetchai/connections/http_server/README.md | 2 +-
.../connections/http_server/connection.yaml | 2 +-
packages/fetchai/connections/oef/README.md | 4 +-
.../fetchai/connections/oef/connection.yaml | 2 +-
.../connections/p2p_libp2p/connection.yaml | 2 +-
.../p2p_libp2p/libp2p_node/README.md | 11 +-
.../connections/p2p_libp2p_client/README.md | 3 +-
.../p2p_libp2p_client/connection.yaml | 2 +-
.../connections/p2p_libp2p_mailbox/README.md | 3 +-
.../p2p_libp2p_mailbox/connection.yaml | 2 +-
.../fetchai/connections/prometheus/README.md | 1 +
.../connections/prometheus/connection.yaml | 2 +-
packages/fetchai/connections/soef/README.md | 2 +-
.../fetchai/connections/soef/connection.yaml | 2 +-
packages/fetchai/connections/stub/README.md | 2 +
.../fetchai/connections/stub/connection.yaml | 2 +-
packages/fetchai/contracts/erc1155/README.md | 29 +-
.../fetchai/contracts/erc1155/contract.yaml | 2 +-
.../fetchai/contracts/fet_erc20/README.md | 4 +-
.../fetchai/contracts/fet_erc20/contract.yaml | 2 +-
packages/fetchai/contracts/oracle/README.md | 4 +-
.../fetchai/contracts/oracle/contract.yaml | 2 +-
.../fetchai/contracts/oracle_client/README.md | 2 +-
.../contracts/oracle_client/contract.yaml | 2 +-
.../fetchai/contracts/staking_erc20/README.md | 2 +-
.../contracts/staking_erc20/contract.yaml | 2 +-
.../fetchai/protocols/cosm_trade/README.md | 5 +-
.../protocols/cosm_trade/protocol.yaml | 2 +-
packages/fetchai/protocols/fipa/README.md | 2 +-
packages/fetchai/protocols/fipa/protocol.yaml | 2 +-
packages/fetchai/protocols/gym/README.md | 2 +-
packages/fetchai/protocols/gym/protocol.yaml | 2 +-
packages/fetchai/protocols/http/README.md | 2 +-
packages/fetchai/protocols/http/protocol.yaml | 2 +-
packages/fetchai/protocols/tac/README.md | 2 +-
packages/fetchai/protocols/tac/protocol.yaml | 2 +-
.../skills/advanced_data_request/README.md | 4 +-
.../skills/advanced_data_request/skill.yaml | 2 +-
packages/fetchai/skills/aries_alice/README.md | 12 +-
.../fetchai/skills/aries_alice/skill.yaml | 2 +-
packages/fetchai/skills/aries_faber/README.md | 19 +-
.../fetchai/skills/aries_faber/skill.yaml | 2 +-
.../fetchai/skills/carpark_client/README.md | 16 +-
.../fetchai/skills/carpark_client/skill.yaml | 2 +-
.../skills/carpark_detection/README.md | 10 +-
.../skills/carpark_detection/skill.yaml | 2 +-
.../fetchai/skills/confirmation_aw1/README.md | 12 +-
.../skills/confirmation_aw1/skill.yaml | 2 +-
.../fetchai/skills/confirmation_aw2/README.md | 18 +-
.../skills/confirmation_aw2/skill.yaml | 2 +-
.../fetchai/skills/confirmation_aw3/README.md | 18 +-
.../skills/confirmation_aw3/skill.yaml | 2 +-
packages/fetchai/skills/echo/README.md | 10 +-
packages/fetchai/skills/echo/skill.yaml | 2 +-
.../fetchai/skills/erc1155_client/README.md | 16 +-
.../fetchai/skills/erc1155_client/skill.yaml | 2 +-
.../fetchai/skills/erc1155_deploy/README.md | 14 +-
.../fetchai/skills/erc1155_deploy/skill.yaml | 2 +-
packages/fetchai/skills/error/README.md | 3 +-
packages/fetchai/skills/error/skill.yaml | 2 +-
.../fetchai/skills/error_test_skill/README.md | 4 +-
.../skills/error_test_skill/skill.yaml | 2 +-
packages/fetchai/skills/fetch_block/README.md | 4 +-
.../fetchai/skills/fetch_block/skill.yaml | 2 +-
.../fetchai/skills/generic_buyer/README.md | 18 +-
.../fetchai/skills/generic_buyer/skill.yaml | 2 +-
.../fetchai/skills/generic_seller/README.md | 12 +-
.../fetchai/skills/generic_seller/skill.yaml | 2 +-
packages/fetchai/skills/gym/README.md | 9 +-
packages/fetchai/skills/gym/skill.yaml | 2 +-
packages/fetchai/skills/http_echo/README.md | 4 +-
packages/fetchai/skills/http_echo/skill.yaml | 2 +-
.../fetchai/skills/ml_data_provider/README.md | 10 +-
.../skills/ml_data_provider/skill.yaml | 2 +-
packages/fetchai/skills/ml_train/README.md | 15 +-
packages/fetchai/skills/ml_train/skill.yaml | 2 +-
.../fetchai/skills/registration_aw1/README.md | 8 +-
.../skills/registration_aw1/skill.yaml | 2 +-
.../skills/simple_aggregation/README.md | 8 +-
.../skills/simple_aggregation/skill.yaml | 2 +-
.../fetchai/skills/simple_buyer/README.md | 16 +-
.../fetchai/skills/simple_buyer/skill.yaml | 2 +-
.../skills/simple_data_request/README.md | 4 +-
.../skills/simple_data_request/skill.yaml | 2 +-
.../fetchai/skills/simple_oracle/README.md | 10 +-
.../fetchai/skills/simple_oracle/skill.yaml | 2 +-
.../skills/simple_oracle_client/README.md | 8 +-
.../skills/simple_oracle_client/skill.yaml | 2 +-
.../fetchai/skills/simple_seller/README.md | 10 +-
.../fetchai/skills/simple_seller/skill.yaml | 2 +-
.../simple_service_registration/README.md | 6 +-
.../simple_service_registration/skill.yaml | 2 +-
.../skills/simple_service_search/README.md | 6 +-
.../skills/simple_service_search/skill.yaml | 2 +-
packages/fetchai/skills/tac_control/README.md | 8 +-
.../fetchai/skills/tac_control/skill.yaml | 2 +-
.../skills/tac_control_contract/README.md | 14 +-
.../skills/tac_control_contract/skill.yaml | 2 +-
.../fetchai/skills/tac_negotiation/README.md | 16 +-
.../fetchai/skills/tac_negotiation/skill.yaml | 2 +-
.../skills/tac_participation/README.md | 10 +-
.../skills/tac_participation/skill.yaml | 2 +-
.../fetchai/skills/task_test_skill/README.md | 2 -
.../fetchai/skills/task_test_skill/skill.yaml | 2 +-
packages/fetchai/skills/thermometer/README.md | 10 +-
.../fetchai/skills/thermometer/skill.yaml | 2 +-
.../skills/thermometer_client/README.md | 15 +-
.../skills/thermometer_client/skill.yaml | 2 +-
.../fetchai/skills/weather_client/README.md | 15 +-
.../fetchai/skills/weather_client/skill.yaml | 2 +-
.../fetchai/skills/weather_station/README.md | 10 +-
.../fetchai/skills/weather_station/skill.yaml | 2 +-
packages/hashes.csv | 174 +--
plugins/aea-cli-ipfs/README.md | 1 +
plugins/aea-ledger-ethereum/setup.py | 2 +-
poetry.lock | 1262 +++++++++--------
pyproject.toml | 4 +-
scripts/NOTES.md | 2 +-
scripts/RELEASE_PROCESS.md | 4 +-
scripts/acn/README.md | 33 +-
.../test_helpers/test_ipfs/test_base.py | 2 +-
tests/test_docs/helper.py | 89 +-
.../md_files/bash-aggregation-demo.md | 19 +-
.../md_files/bash-aries-cloud-agent-demo.md | 27 +-
.../md_files/bash-car-park-skills.md | 18 +-
.../md_files/bash-cli-how-to.md | 6 +-
.../md_files/bash-cli-vs-programmatic-aeas.md | 8 +
.../test_bash_yaml/md_files/bash-config.md | 8 +
.../md_files/bash-connection.md | 3 +-
.../test_bash_yaml/md_files/bash-contract.md | 1 +
.../md_files/bash-decision-maker.md | 1 +
.../md_files/bash-erc1155-skills.md | 24 +-
.../bash-generic-skills-step-by-step.md | 29 +-
.../md_files/bash-generic-skills.md | 21 +-
.../md_files/bash-gym-example.md | 2 +
.../test_bash_yaml/md_files/bash-gym-skill.md | 11 +
.../bash-http-connection-and-skill.md | 12 +-
.../bash-language-agnostic-definition.md | 4 +-
.../test_bash_yaml/md_files/bash-logging.md | 3 +
.../test_bash_yaml/md_files/bash-ml-skills.md | 20 +-
.../md_files/bash-oef-ledger.md | 1 +
.../md_files/bash-oracle-demo.md | 31 +
.../md_files/bash-orm-integration.md | 24 +-
.../md_files/bash-p2p-connection.md | 78 +-
.../md_files/bash-package-imports.md | 1 +
.../md_files/bash-performance-benchmark.md | 2 +
.../test_bash_yaml/md_files/bash-por.md | 2 +-
.../md_files/bash-protocol-generator.md | 4 +
.../md_files/bash-quickstart.md | 34 +-
.../md_files/bash-raspberry-set-up.md | 8 +
.../md_files/bash-scaffolding.md | 5 +
.../md_files/bash-skill-guide.md | 17 +-
.../test_bash_yaml/md_files/bash-skill.md | 5 +-
.../md_files/bash-tac-skills-contract.md | 40 +-
.../md_files/bash-tac-skills.md | 22 +-
.../test_bash_yaml/md_files/bash-tac.md | 9 +
.../md_files/bash-thermometer-skills.md | 20 +-
.../test_bash_yaml/md_files/bash-version.md | 2 +-
.../test_bash_yaml/md_files/bash-wealth.md | 11 +-
.../md_files/bash-weather-skills.md | 18 +-
.../test_language_agnostic_definition.py | 2 +-
tests/test_docs/test_quickstart.py | 2 +-
user-image/README.md | 2 +-
300 files changed, 7112 insertions(+), 6379 deletions(-)
create mode 100644 docs/assets/images/favicon.ico
create mode 100644 docs/assets/images/logo.png
diff --git a/.github/PULL_REQUEST_TEMPLATE/release.md b/.github/PULL_REQUEST_TEMPLATE/release.md
index 1b4b7ba0cc..49ff36f139 100644
--- a/.github/PULL_REQUEST_TEMPLATE/release.md
+++ b/.github/PULL_REQUEST_TEMPLATE/release.md
@@ -18,7 +18,7 @@ _Put an `x` in the boxes that apply._
- [ ] I have added an item in `HISTORY.md` for this release.
- [ ] I bumped the version number in the `aea/__version__.py` file.
- [ ] I bumped the version number in every Docker image of the repo and published it. Also, I built and published them with tag `latest`
- (check the READMEs of [`aea-develop`](https://github.com/fetchai/agents-aea/blob/master/develop-image/README.md#publish)
+ (check the READMEs of [`aea-develop`](https://github.com/fetchai/agents-aea/blob/master/develop-image/README.md#publish)
and [`aea-user`](https://github.com/fetchai/agents-aea/blob/master/develop-image/user-image/README.md#publish))
- [ ] I have pushed the latest packages to the registry.
- [ ] I have uploaded the latest `aea` to PyPI.
diff --git a/AUTHORS.md b/AUTHORS.md
index 5bc6dfd308..61ce63145d 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -4,18 +4,18 @@ This is the official list of the AEA Framework authors:
### Lead
-* Ali Hosseini [5A11](https://github.com/5A11)
+- Ali Hosseini [5A11](https://github.com/5A11)
### Current and Past Contributors
-* James Riehl [jrriehl](https://github.com/jrriehl)
-* David Minarsch [DavidMinarsch](https://github.com/DavidMinarsch)
-* Marco Favorito [MarcoFavorito](https://github.com/MarcoFavorito)
-* Yuri Turchenkov [solarw](https://github.com/solarw)
-* Oleg Panasevych [Panasevychol](https://github.com/panasevychol)
-* Lokman Rahmani [lrahmani](https://github.com/lrahmani)
-* Jiří Vestfál [MissingNO57](https://github.com/MissingNO57)
-* Aristotelis Triantafyllidis [Totoual](https://github.com/Totoual)
-* Diarmid Campbell [dishmop](https://github.com/dishmop)
-* Kevin Chen [Kevin-Chen0](https://github.com/Kevin-Chen0)
-* Ed Fitzgerald [ejfitzgerald](https://github.com/ejfitzgerald)
+- James Riehl [jrriehl](https://github.com/jrriehl)
+- David Minarsch [DavidMinarsch](https://github.com/DavidMinarsch)
+- Marco Favorito [MarcoFavorito](https://github.com/MarcoFavorito)
+- Yuri Turchenkov [solarw](https://github.com/solarw)
+- Oleg Panasevych [Panasevychol](https://github.com/panasevychol)
+- Lokman Rahmani [lrahmani](https://github.com/lrahmani)
+- Jiří Vestfál [MissingNO57](https://github.com/MissingNO57)
+- Aristotelis Triantafyllidis [Totoual](https://github.com/Totoual)
+- Diarmid Campbell [dishmop](https://github.com/dishmop)
+- Kevin Chen [Kevin-Chen0](https://github.com/Kevin-Chen0)
+- Ed Fitzgerald [ejfitzgerald](https://github.com/ejfitzgerald)
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 8c98b067fe..685073776e 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -10,19 +10,19 @@ We pledge to act and interact in ways that contribute to an open, welcoming, div
Examples of behavior that contributes to a positive environment for our community include:
-* Demonstrating empathy and kindness toward other people
-* Being respectful of differing opinions, viewpoints, and experiences
-* Giving and gracefully accepting constructive feedback
-* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
-* Focusing on what is best not just for us as individuals, but for the overall community
+- Demonstrating empathy and kindness toward other people
+- Being respectful of differing opinions, viewpoints, and experiences
+- Giving and gracefully accepting constructive feedback
+- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
+- Focusing on what is best not just for us as individuals, but for the overall community
Examples of unacceptable behavior include:
-* The use of sexualized language or imagery, and sexual attention or advances of any kind
-* Trolling, insulting or derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others’ private information, such as a physical or email address, without their explicit permission
-* Other conduct which could reasonably be considered inappropriate in a professional setting
+- The use of sexualized language or imagery, and sexual attention or advances of any kind
+- Trolling, insulting or derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others’ private information, such as a physical or email address, without their explicit permission
+- Other conduct which could reasonably be considered inappropriate in a professional setting
## Enforcement Responsibilities
@@ -70,9 +70,11 @@ Community leaders will follow these Community Impact Guidelines in determining t
## Attribution
-This Code of Conduct is adapted from the [Contributor Covenant][https://www.contributor-covenant.org], version 2.1,
+This Code of Conduct is adapted from the [Contributor Covenant][contributor covenant], version 2.1,
available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html).
Community Impact Guidelines were inspired by [Mozilla’s code of conduct enforcement ladder](https://github.com/mozilla/diversity).
-For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq). Translations are available at [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations).
\ No newline at end of file
+For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq). Translations are available at [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations).
+
+[contributor covenant]: https://www.contributor-covenant.org
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8492224414..79802b0cfe 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -16,10 +16,10 @@ Please read and follow our [Code of Conduct][coc].
## Question or Problem?
-Please use [Github Discussions][ghdiscussion] for support related questions and general discussions. Do NOT open issues as they are for bug reports and feature requests. This is because:
+Please use [GitHub Discussions][ghdiscussion] for support related questions and general discussions. Do NOT open issues as they are for bug reports and feature requests. This is because:
- Questions and answers stay available for public viewing so your question/answer might help someone else.
-- Github Discussions voting system ensures the best answers are prominently visible.
+- GitHub Discussions voting system ensures the best answers are prominently visible.
## Found a Bug?
@@ -27,11 +27,12 @@ If you find a bug in the source code [submit a bug report issue](#submit-issue).
Even better, you can [submit a Pull Request](#submit-pr) with a fix.
## Missing a Feature?
+
You can *request* a new feature by [submitting a feature request issue](#submit-issue).
If you would like to *implement* a new feature:
-* For a **Major Feature**, first [open an issue](#submit-issue) and outline your proposal so that it can be discussed.
-* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
+- For a **Major Feature**, first [open an issue](#submit-issue) and outline your proposal so that it can be discussed.
+- **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
## Submission Guidelines
@@ -74,7 +75,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
#### Reviewing a Pull Request
-The AEA team reserves the right not to accept pull requests from community members who haven't been good citizens of the community. Such behavior includes not following our [code of conduct][coc] and applies within or outside of the managed channels.
+The AEA team reserves the right not to accept pull requests from community members who haven't been good citizens of the community. Such behavior includes not following our [code of conduct][coc] and applies within or outside the managed channels.
When you contribute a new feature, the maintenance burden is transferred to the core team. This means that the benefit of the contribution must be compared against the cost of maintaining the feature.
@@ -93,25 +94,26 @@ If we ask for changes via code reviews then:
After your pull request is merged, you can safely delete your branch and pull the changes from the (upstream) repository.
## Coding Rules
+
To ensure consistency throughout the source code, keep these rules in mind as you are working:
-* All code must pass our code quality checks (linters, formatters, etc). See the [development guide][developing] section for more detail.
-* All features or bug fixes **must be tested** via unit-tests and if applicable integration-tests. These help to, a) prove that your code works correctly, and b) guard against future breaking changes and lower the maintenance cost.
-* All public features **must be documented**.
-* All files must include a license header.
+- All code must pass our code quality checks (linters, formatters, etc). See the [development guide][developing] section for more detail.
+- All features or bug fixes **must be tested** via unit-tests and if applicable integration-tests. These help to, a. prove that your code works correctly, and b. guard against future breaking changes and lower the maintenance cost.
+- All public features **must be documented**.
+- All files must include a license header.
## Commit Message Convention
Please follow the [Conventional Commits v1.0.0][convcommit]. The commit types must be one of the following:
-* **build**: Changes that affect the build system or external dependencies
-* **ci**: Changes to our CI configuration files and scripts
-* **docs**: Documentation only changes
-* **feat**: A new feature
-* **fix**: A bug fix
-* **nfunc**: Code that improves some non-functional characteristic, such as performance, security, ...
-* **refactor**: A code change that neither fixes a bug nor adds a feature
-* **test**: Adding missing tests or correcting existing tests
+- **build**: Changes that affect the build system or external dependencies
+- **ci**: Changes to our CI configuration files and scripts
+- **docs**: Documentation only changes
+- **feat**: A new feature
+- **fix**: A bug fix
+- **nfunc**: Code that improves some non-functional characteristic, such as performance, security, ...
+- **refactor**: A code change that neither fixes a bug nor adds a feature
+- **test**: Adding missing tests or correcting existing tests
[coc]: https://github.com/fetchai/agents-aea/blob/main/CODE_OF_CONDUCT.md
[developing]: https://github.com/fetchai/agents-aea/blob/main/DEVELOPING.md
@@ -120,4 +122,4 @@ Please follow the [Conventional Commits v1.0.0][convcommit]. The commit types mu
[new-issue]: https://github.com/fetchai/agents-aea/issues/new/choose
[prs]: https://github.com/fetchai/agents-aea/pulls
[convcommit]: https://www.conventionalcommits.org/en/v1.0.0/
-[github]: https://github.com/fetchai/agents-aea
\ No newline at end of file
+[github]: https://github.com/fetchai/agents-aea
diff --git a/DEVELOPING.md b/DEVELOPING.md
index 86126e48c7..3cdd8d48d4 100644
--- a/DEVELOPING.md
+++ b/DEVELOPING.md
@@ -15,97 +15,190 @@
## Getting the Source
-1. Fork the [agents-aea repository][repo].
+1. Fork the [agents-aea repository][repo].
2. Clone your fork of the agents-aea repository:
- ```shell
+
+ ``` shell
git clone git@github.com:/agents-aea.git
```
+
3. Define an `upstream` remote pointing back to the main agents-aea repository:
- ```shell
+
+ ``` shell
git remote add upstream https://github.com/fetchai/agents-aea.git
```
-## Setting up a New Development Environment:
+## Setting up a New Development Environment
+
+1. Ensure you have Python (version `3.8`, `3.9` or `3.10`) and [`poetry`][poetry].
+
+2. ``` shell
+ make new-env
+ ```
+
+ This will create a new virtual environment using poetry with the project and all the development dependencies installed.
-1. Ensure you have Python (version `3.8`, `3.9` or `3.10`) and [`poetry`][poetry].
-2. make new-env
- This will create a new virtual environment using poetry with the project and all the development dependencies installed.
-3. poetry shell
- To enter the virtual environment.
+3. ``` shell
+ poetry shell
+ ```
+
+ To enter the virtual environment.
Depending on what you want to do, you might need extra tools on your system:
- The project uses [Google Protocol Buffers][protobuf] compiler for message serialization. The compiler's version must match the `protobuf` library installed with the project (see `pyproject.toml`).
- The `fetchai/p2p_libp2p` package is partially developed in Go. To make changes, [install Golang][go].
-- To update fingerprint hashes of packages, you will need the [IPFS daemon][ipfs].
+- To update fingerprint hashes of packages, you will need the [IPFS daemon][ipfs].
## Development
### General code quality checks
+
To run general code quality checkers, formatters and linters:
-- make lint
+
+- ``` shell
+ make lint
+ ```
+
Automatically formats your code and sorts your imports, checks your code's quality and scans for any unused code.
-- make mypy
+
+- ``` shell
+ make mypy
+ ```
+
Statically checks the correctness of the types.
-- make pylint
+
+- ``` shell
+ make pylint
+ ```
+
Analyses the quality of your code.
-- make security
+
+- ``` shell
+ make security
+ ```
+
Checks the code for known vulnerabilities and common security issues.
-- make clean
+
+- ``` shell
+ make clean
+ ```
+
Cleans your development environment and deletes temporary files and directories.
+
- For the Go parts, we use [`golines`][golines] and [`golangci-lint`][golangci-lint] for linting.
### Updating documentation
+
We use [`mkdocs`][mkdocs] and [`material-for-mkdocs`][material] for static documentation pages. To make changes to the documentation:
-- make docs-live
- This starts a live-reloading docs server on localhost which you can access by going to http://127.0.0.1:8000/ in your browser. Making changes to the documentation automatically reloads this page, showing you the latest changes.
+
+- ``` shell
+ make docs-live
+ ```
+
+ This starts a live-reloading docs server on localhost which you can access by going to in your browser. Making changes to the documentation automatically reloads this page, showing you the latest changes.
To create a new documentation page, add a markdown file under `/docs/` and add a reference to this page in `mkdocs.yml` under `nav`.
### Updating API documentation
+
If you've made changes to the core `aea` package that affects the public API:
-- make generate-api-docs
+
+- ``` shell
+ make generate-api-docs
+ ```
+
This regenerates the API docs. If pages are added/deleted, or there are changes in their structure, these need to be reflected manually in the `nav` section of `mkdocs.yaml`.
### Updating dependencies
-We use [`poetry`][poetry] and `pyproject.toml` to manage the project's dependencies.
+
+We use [`poetry`][poetry] and `pyproject.toml` to manage the project's dependencies.
If you've made any changes to the dependencies (e.g. added/removed dependencies, or updated package version requirements):
-- poetry lock
+
+- ``` shell
+ poetry lock
+ ```
+
This re-locks the dependencies. Ensure that the `poetry.lock` file is pushed into the repository (by default it is).
-- make liccheck
+
+- ``` shell
+ make liccheck
+ ```
+
Checks that the licence for the framework is correct, taking into account the licences for all dependencies, their dependencies and so forth.
### Updating packages
+
If you've made changes to the packages included in the repository (e.g. skills, protocols, connections, contracts):
-- make update-package-hashes
+
+- ``` shell
+ make update-package-hashes
+ ```
+
Updates the fingerprint hashes of every package in the repository.
-- make package-checks
- Checks, a) that the package hashes are correct, b) the documentation correctly references the latest packages, and c) runs other correctness checks on packages.
+
+- ``` shell
+ make package-checks
+ ```
+
+ Checks, a. that the package hashes are correct, b. the documentation correctly references the latest packages, and c) runs other correctness checks on packages.
### Tests
+
To test the Python part of the project, we use `pytest`. To run the tests:
-- make test
+- ``` shell
+ make test
+ ```
+
Runs all the tests.
-- make test-plugins
+
+- ``` shell
+ make test-plugins
+ ```
+
Runs all plugin tests.
-- make dir={SUBMODULE} tdir={TESTMODULE} test-sub
+
+- ``` shell
+ make dir={SUBMODULE} tdir={TESTMODULE} test-sub
+ ```
+
Runs the tests for `aea.{SUBMODULE}`. For example, to run the tests for the CLI: `make dir=cli tdir=cli test-sub`
-To test the Go parts of the project:
-- go test -p 1 -timeout 0 -count 1 -v ./...
- from the root directory of a Go package (e.g. `fetchai/p2p_libp2p`) to run the Golang tests.
+To test the Go parts of the project:
+
+- ``` shell
+ go test -p 1 -timeout 0 -count 1 -v ./...
+ ```
+
+ from the root directory of a Go package (e.g. `fetchai/p2p_libp2p`) to run the Golang tests.
If you experience installation or build issues, run `go clean -modcache`.
### Miscellaneous checks
-- copyright-check
+
+- ``` shell
+ copyright-check
+ ```
+
Checks that all files have the correct copyright header (where applicable).
-- check-doc-links
+
+- ``` shell
+ check-doc-links
+ ```
+
Checks that the links in the documentations are valid and alive.
-- make libp2p-diffs
+
+- ``` shell
+ make libp2p-diffs
+ ```
+
Checks the libp2p code under `libs` and in the connection packages aren't different.
-- make plugin-diffs
+
+- ``` shell
+ make plugin-diffs
+ ```
+
Checks the plugin licenses and the codes under `cosmos` and `fetchai` ledger plugins aren't different.
## Contributing
diff --git a/HISTORY.md b/HISTORY.md
index 7565698b4b..0b5b05173c 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -3,13 +3,16 @@
## 1.2.4(2022-11-30)
Agents:
+
- Add hello_world skill and agent
Docs:
+
- Update repository documentations
- Update installation guide for Raspberry Pis (add a link to prebuilt image for Raspberry Pis)
Misc:
+
- Update the dependencies (protobuf, jsonschema, cosmpy)
- Various improvements in the repository structure (e.g. makefile, tox, workflows)
- Speed up CI/CD
@@ -18,68 +21,81 @@ Misc:
## 1.2.3(2022-11-03)
AEA:
+
- core and development are dependencies updated.
- ci improvements
- cosmpy updated to 0.6.0
- Small code format improvements and better linting
Plugins:
+
- Small code format improvements
- cosmpy updated to 0.6.0
Packages:
+
- Small code format improvements
## 1.2.2 (2022-10-17)
AEA:
+
- Dependency management switched from pipenv to poetry.
- Protocol generator updated to support Unions
- Dependencies versions updates: click, mypy, black, ipfshttpclient
- Small code format improvements
Plugins:
+
- Update web3 to version 5.31
- Small code format improvements
- ipfshttpclient dependency version updated to 0.8.0a2
Packages:
+
- Protocols regenerated according to the latest protocol generator improvements: Union support
- Small code format improvements
## 1.2.1 (2022-07-12)
AEA:
+
- Protocol generator uses int64 instead of int32
- Dependencies versions updates
Plugins:
+
- Upgrades fetchai plugin to use cosmpy>=0.5.0
- Upgrades cosmos plugin to use cosmpy>=0.5.0
Packages:
+
- Fixes for skills to work with dorado testnet (tx fee adjusted)
## 1.2.0 (2022-05-05)
AEA:
+
- Adds support for Python 3.10
-- Updates protobuf dependency
+- Updates protobuf dependency
- Updates asyncio dependency
- Updates golang modules
- Updates many dependencies to their latest versions
- Fixes dependency issues
Plugins:
+
- Upgrades fetchai plugin to be compatible with Dorado networks
- Upgrades cosmos plugin to be compatible with Dorado networks
Packages:
+
- Adds more logging to the p2p_libp2p packages (vanilla, client, mailbox)
- Aries demo updated to cover the full base scenario
- Protocols were regenerated with newer protobuf
Chores:
+
- Fixed various tests
- Fixed docker container issue in tests
- Added automated script to add support for new versions of the Fetchai network
@@ -91,23 +107,28 @@ Chores:
## 1.1.1 (2021-12-15)
AEA:
+
- Updates the protocol generator to generate protocols that satisfy linter constraints
Plugins:
- - aea-cli-ipfs plugin small update
+
+- aea-cli-ipfs plugin small update
Packages:
+
- Fixes fetchai/p2p_libp2p connection to address a slow DHT lookup problem
- Updates protocols with the latest protocol generator
- Updates random beacon agent so it produces block data instead of the (now deprecated feature of the test-net) random beacon data
Misc
+
- Bumps go library versions
- Various fixes and improvements
## 1.1.0 (2021-10-13)
AEA:
+
- Adds public keys to agent identity and skill context
- Adds contract test tool
- Adds multiprocess support for task manager
@@ -124,27 +145,30 @@ AEA:
- Fixes IPFS hash calculation for large files
- Fixes protobuf dictionary serializer's uncovered cases and makes it deterministic
- Fixes scaffolding of error and decision maker handlers
-- Fixes pywin32 problem when checking dependency
+- Fixes pywin32 problem when checking dependency
- Improves existing testing tools
Benchmarks:
+
- Adds agents construction and decision maker benchmark cases
Plugins:
+
- Upgrades fetchai plugin to use CosmPy instead of CLI calls
- Upgrades cosmos plugin to use CosmPy instead of CLI calls
-- Upgrades fetchai plugin to use StargateWorld
+- Upgrades fetchai plugin to use StargateWorld
- Upgrades cosmos plugin to Stargate
- Sets the correct maximum Gas for fetch.ai plugin
Packages:
+
- Adds support for Tac to be run against fetchai StargateWorld test-net
- Adds more informative error messages to CosmWasm ERC1155 contract
- Adds support for atomic swap to CosmWasm ERC1155 contract
-- Adds an ACN protocol that formalises ACN communication using the framework's protocol language
+- Adds an ACN protocol that formalises ACN communication using the framework's protocol language
- Adds `cosm_trade` protocol for preparing atomic swap transactions for cosmos-based networks
- Adds https support for server connection
-- Adds parametrising of http(s) in soef connection
+- Adds parametrising of http(s) in soef connection
- Fixes http server content length response problem
- Updates Oracle contract to 0.14
- Implements the full ACN spec throughout the ACN packages
@@ -156,22 +180,26 @@ Packages:
- Multiple fixes and stability improvements for `p2p_libp2p` connections
Docs:
+
- Adds ACN internals documentation
- Fixes tutorial for HTTP connection and skill
- Multiple additional docs updates
- Adds more context to private keys docs
Chores:
+
- Various development features bumped
- Bumped Mermaid-JS, for UML diagrams to major version 8
- Applies darglint to the code
Examples:
+
- Adds a unified script for running various versions/modes of Tac
## 1.0.2 (2021-06-03)
AEA:
+
- Bounds versions of dependencies by next major
- Fixes incoherent warning message during package loading
- Improves various incomprehensible error messages
@@ -184,9 +212,11 @@ AEA:
- Fixes `aea get-multiaddress` command to consider overrides
Plugins:
+
- Bounds versions of dependencies by next major
Packages:
+
- Updates `p2p_libp2p` connection to use TCP sockets for all platforms
- Multiple fixes on `libp2p_node` including better error handling and stream creation
- Adds sending queue in `p2p_libp2p` connection to handle sending failures
@@ -201,11 +231,13 @@ Packages:
- Multiple additional tests and test stability fixes
Docs:
+
- Extends demo docs to include guidance of usage in AEA Manager
- Adds short guide on Kubernetes deployment
- Multiple additional docs updates
Chores:
+
- Adds `--no-bump` option to `generate_all_protocols` script
- Adds script to detect if aea or plugins need bumping
- Bumps various development dependencies
@@ -213,11 +245,13 @@ Chores:
- Adds `darglint` to CI
Examples:
+
- Updates TAC deployment scripts and images
## - (2021-05-05)
Packages:
+
- Adds node watcher to `p2p_libp2p` connection
- Improves logging and error handling in `p2p_libp2p` node
- Addresses potential overflow issue in `p2p_libp2p` node
@@ -231,16 +265,19 @@ Packages:
## 1.0.1 (2021-04-30)
AEA:
+
- Fixes wheels issue for Windows
- Fixes password propagation for certificate issuance in `MultiAgentManager`
- Improves error message when local registry not present
AEALite:
+
- Adds full protocol support
- Adds end-to-end interaction example with AEA (based on `fetchai/fipa` protocol)
- Multiple additional tests and test stability fixes
Packages:
+
- Fixes multiple bugs in `ERC1155` version of TAC
- Refactors p2p connections for better separation of concerns and maintainability
- Integrates aggregation with simple oracle skill
@@ -253,11 +290,13 @@ Packages:
- Multiple additional tests and test stability fixes
Docs:
+
- Extends car park demo with usage guide for AEA manager
- Multiple additional docs updates
Examples:
-- Adds TAC deployment example
+
+- Adds TAC deployment example
## 1.0.0 (2021-03-30)
@@ -311,7 +350,6 @@ Examples:
- Multiple docs updates to fix order of CLI commands with respect to installing dependencies
- Multiple additional tests and test stability fixes
-
## 0.11.2 (2021-03-17)
- Fixes a package import issue
@@ -755,7 +793,7 @@ Examples:
- Updates connection loading mechanism
- Updates all connections for compatibility with new loading mechanism
- Extracts multiplexer into its own module
-- Implements list all CLI command
+- Implements list all CLI command
- Updates wallet to split into several crypto stores
- Refactors component registry and resources
- Extends soef connection functionality
@@ -1049,7 +1087,6 @@ Examples:
- Increased test coverage
- Multiple additional minor fixes and changes
-
## 0.1.4 (2019-09-20)
- Adds CLI functionality to add connections
diff --git a/README.md b/README.md
index 82b54bb9cb..d1215ec033 100644
--- a/README.md
+++ b/README.md
@@ -14,10 +14,10 @@ Create Autonomous Economic Agents (AEAs)
-
+
-
+
@@ -32,6 +32,7 @@ Create Autonomous Economic Agents (AEAs)
The AEA framework allows you to create **Autonomous Economic Agents**:
+
- An AEA is an Agent, representing an individual, family, organisation or object (a.k.a. its "owner") in the digital world. It looks after its owner's interests and has their preferences in mind when acting on their behalf.
- AEAs are Autonomous; acting with no, or minimal, interference from their owners.
- AEAs have a narrow and specific focus: creating Economic value for their owners.
@@ -56,7 +57,7 @@ The full documentation, including how to get started, can be found [here][docs].
## Contributing
-All contributions are very welcome! Remember, contribution is not only PRs and code, but any help with docs or helping other developers solve their issues are very appreciated!
+All contributions are very welcome! Remember, contribution is not only PRs and code, but any help with docs or helping other developers solve their issues are very appreciated!
Read below to learn how you can take part in the AEA project.
@@ -90,7 +91,6 @@ If you want to cite this software in a publication, please use the `Cite this re
[contributing]: https://github.com/fetchai/agents-aea/blob/main/CONTRIBUTING.md
[developing]: https://github.com/fetchai/agents-aea/blob/main/DEVELOPING.md
[coc]: https://github.com/fetchai/agents-aea/blob/main/CODE_OF_CONDUCT.md
-[security]: https://github.com/fetchai/agents-aea/blob/main/SECURITY.md
[discussion]: https://github.com/fetchai/agents-aea/discussions
[issues]: https://github.com/fetchai/agents-aea/issues
[github]: https://github.com/fetchai/agents-aea
diff --git a/SECURITY.md b/SECURITY.md
index d347def524..fa7f245661 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -7,7 +7,7 @@ This document outlines security procedures and general policies for the `aea` pr
The following table shows which versions of `aea` are currently being supported with security updates.
| Version | Supported |
-| --------- | ------------------ |
+|-----------|--------------------|
| `1.2.x` | :white_check_mark: |
| `< 1.2.0` | :x: |
diff --git a/aea/connections/scaffold/connection.yaml b/aea/connections/scaffold/connection.yaml
index fe4d8b6687..2b76d893f6 100644
--- a/aea/connections/scaffold/connection.yaml
+++ b/aea/connections/scaffold/connection.yaml
@@ -9,7 +9,7 @@ aea_version: '>=1.0.0, <2.0.0'
fingerprint:
__init__.py: QmaA7o9G1hT3fHtPDq6UYUyS5KY51uDkwMUGUc96odzSCX
connection.py: QmWUqCmfD65KqnGjk2XWLDN3KAU32nC6uYgGCvjmSLWi7D
- readme.md: Qmdt71SaCCwAG1c24VktXDm4pxgUBiPMg4bWfUTiqorypf
+ readme.md: QmRFgpKrtPPTJSAEaXoNNKcYFiTAhVFKuiNZdjrjmAw8d1
fingerprint_ignore_patterns: []
connections: []
protocols: []
diff --git a/aea/connections/scaffold/readme.md b/aea/connections/scaffold/readme.md
index 6359d72d7b..5b373b777f 100644
--- a/aea/connections/scaffold/readme.md
+++ b/aea/connections/scaffold/readme.md
@@ -1,5 +1,7 @@
# Scaffold connection
+
The scaffold connection acts as a boilerplate for a newly created connection.
## Usage
+
Create a scaffold connection with the `aea scaffold connection {NAME}` command and implement your own connection.
diff --git a/benchmark/README.md b/benchmark/README.md
index 5cc76cbeb3..a08b684163 100644
--- a/benchmark/README.md
+++ b/benchmark/README.md
@@ -1,15 +1,19 @@
+# Benchmarks
-# Running a benchmark locally:
+## Running a benchmark locally
First, set permissions:
+
``` bash
chmod +x /benchmark/checks/run_benchmark.sh
```
Then, run:
+
``` bash
./benchmark/checks/run_benchmark.sh
```
+
or to save to file:
``` bash
@@ -18,10 +22,10 @@ or to save to file:
The benchmark will use the locally installed AEA version!
-
-# Deploying a benchmark run and serving results:
+## Deploying a benchmark run and serving results
First remove any old configuration maps and create a new one:
+
``` bash
kubectl delete configmap run-benchmark
kubectl create configmap run-benchmark --from-file=run_from_branch.sh
@@ -51,11 +55,14 @@ List pods:
kubectl get pod -o wide
```
-To access NGINX (wait for status: ` `):
+To access NGINX (wait for status: ``):
+
``` bash
kubectl port-forward NODE_NAME 8000:80
```
+
then
+
``` bash
curl localhost:8000 | tee results.txt
-```
\ No newline at end of file
+```
diff --git a/deploy-image/README.md b/deploy-image/README.md
index 896e85a69f..7c6de6d899 100644
--- a/deploy-image/README.md
+++ b/deploy-image/README.md
@@ -23,14 +23,14 @@ Second, review the `entrypoint.sh` script to make sure you supply the agent with
Importantly, do not add any private keys during the build step!
Third, create a local `.env` file with the relevant environment variables:
-```
+
+``` bash
AGENT_PRIV_KEY=hex_key_here
CONNECTION_PRIV_KEY=hex_key_here
```
Finally, if required, modify the `Dockerfile` to expose any ports needed by the AEA. (The default example does not require this.)
-
### Build the image
``` bash
@@ -47,5 +47,4 @@ To stop, use `docker ps` to find the container id and then `docker stop CONTAINE
## Advanced usage and comments
-- The above approach implies that key files remain in the container. To avoid this, a static volume can be mounted with the key files in it (https://docs.docker.com/get-started/06_bind_mounts/).
-
+- The above approach implies that key files remain in the container. To avoid this, a static volume can be mounted with the key files in it ().
diff --git a/develop-image/README.md b/develop-image/README.md
index 8c2b571355..f562b3f1a1 100644
--- a/develop-image/README.md
+++ b/develop-image/README.md
@@ -4,110 +4,132 @@ All the commands must be executed from the parent directory, if not stated other
## Build
- ./develop-image/scripts/docker-build-img.sh -t fetchai/aea-deploy:latest --
-
+``` bash
+./develop-image/scripts/docker-build-img.sh -t fetchai/aea-deploy:latest --
+```
To pass immediate parameters to the `docker build` command:
- ./develop-image/scripts/docker-build-img.sh arg1 arg2 --
+``` bash
+./develop-image/scripts/docker-build-img.sh arg1 arg2 --
+```
E.g.:
- ./develop-image/scripts/docker-build-img.sh --squash --cpus 4 --compress --
-
+``` bash
+./develop-image/scripts/docker-build-img.sh --squash --cpus 4 --compress --
+```
## Run
- ./develop-image/scripts/docker-run.sh -- /bin/bash
-
-As before, to pass parameters to the `docker run` command:
+``` bash
+./develop-image/scripts/docker-run.sh -- /bin/bash
+```
- ./develop-image/scripts/docker-run.sh -p 8080:80 -- /bin/bash
+As before, to pass parameters to the `docker run` command:
+``` bash
+./develop-image/scripts/docker-run.sh -p 8080:80 -- /bin/bash
+```
## Publish
-First, be sure you tagged the image with the `latest` tag:
+First, be sure you tagged the image with the `latest` tag:
- docker tag fetchai/aea-develop: fetchai/aea-develop:latest
+``` bash
+docker tag fetchai/aea-develop: fetchai/aea-develop:latest
+```
Then, publish the images. First, the `fetchai/aea-develop:`
- ./develop-image/scripts/docker-publish-img.sh
+``` bash
+./develop-image/scripts/docker-publish-img.sh
+```
And then, the `fetchai/aea-develop:latest` image:
- In `docker-env.sh`, uncomment `DOCKER_IMAGE_TAG=fetchai/aea-develop:latest`
-- Run the publish command again:
+- Run the publish command again:
- ./develop-image/scripts/docker-publish-img.sh
+ ``` bash
+ ./develop-image/scripts/docker-publish-img.sh
+ ```
-# Publish to k8s
+### Publish to k8s
Switch the context:
-``` bash
+
+``` shell
kubectx sandbox
```
List pods in cluster:
-``` bash
+
+``` shell
kubectl get pods
```
Optionally, create new namespace:
-``` bash
+
+``` shell
kubectl create namespace aea-research
```
Ensure right namespace is used:
-``` bash
+
+``` shell
kubens aea-research
```
+
Choose namespace in cluster:
-``` bash
+
+``` shell
kubens aea-research
```
+
To enter selected namespace:
-``` bash
+
+``` shell
kubens
```
From the `develop-image` folder run:
-``` bash
+
+``` shell
skaffold run -p sandbox
```
SSH into a new image:
-``` bash
+
+``` shell
kubectl run --generator=run-pod/v1 -it debian --image=debian -- bash
```
-# Dedicated node pool for benchmarking agents
+## Dedicated node pool for benchmarking agents
-
-## Setup and tear down
+### Setup and tear down
To create the node pool
-``` bash
+
+``` shell
gcloud container node-pools create agent-test-pool --cluster sandbox --project fetch-ai-sandbox --node-taints dedicated=agent:NoSchedule --machine-type=n1-standard-4 --num-nodes=1 --enable-autoscaling --node-labels=type=agent-test --max-nodes=1 --min-nodes=0
```
-To remove the node pool
-``` bash
+
+To remove the node pool
+
+``` shell
gcloud container node-pools delete agent-test-pool --cluster sandbox --project fetch-ai-sandbox
```
-## Usage
+### Usage
List pods
-``` bash
+``` shell
kubectl get pod -o wide
```
-``` bash
+``` shell
kubectl exec -it NAME -- bash
```
-
-
-
diff --git a/docs/12-factor.md b/docs/12-factor.md
index cf86a51074..c9f20a30b2 100644
--- a/docs/12-factor.md
+++ b/docs/12-factor.md
@@ -1,228 +1,131 @@
-# Relationship with the Twelve-Factor App methodology.
+# Relationship with the Twelve-Factor App Methodology
-The Twelve-Factor App is
-a set of best practices to build modern
-web applications, or *software-as-a-service*.
+The Twelve-Factor App is a set of best practices to build modern web applications, or *software-as-a-service*.
-In this section, we will see how the AEA framework
-facilitates the achievement of those
-in the development, release and deployment
-phases of an AEA project.
+In this section, we will see how the AEA framework facilitates the achievement of those in the development, release and deployment phases of an AEA project.
-Note that an AEA instance, as a software agent,
-can be seen as a more general case of a web app,
-as it not only shows reactive behaviour,
-but it is also *proactive*, depending
-on the goals assigned to it.
+Note that an AEA instance, as a software agent, can be seen as a more general case of a web app, as it not only shows reactive behaviour, but it is also *proactive*, depending on the goals assigned to it.
## Codebase
-> One codebase tracked in revision control, many deploys
+!!! info "Factor 1"
+ One codebase tracked in revision control, many deploys
Support: Excellent
-The framework does not impose any particular requirement
-or convention on the type of version control
-software to be used to store an AEA project.
+The framework does not impose any particular requirement or convention on the type of version control software to be used to store an AEA project.
## Dependencies
-> Explicitly declare and isolate dependencies
+!!! info "Factor 2"
+ Explicitly declare and isolate dependencies
Support: Good
-The framework allows an AEA project to explicitly declare
-the AEA package dependencies, and the PyPI dependencies
-needed to proper working.
-
-However, it does not provide built-in support
-for checking platform-specific dependencies,
-e.g. specific Python version, or needed system-wide available libraries.
-Nevertheless, this can be indirectly achieved
-by means of build scripts called on `aea build`,
-which can do the checks manually according to the specific
-requirements of the project.
+The framework allows an AEA project to explicitly declare the AEA package dependencies, and the PyPI dependencies needed to proper working.
+However, it does not provide built-in support for checking platform-specific dependencies, e.g. specific Python version, or needed system-wide available libraries. Nevertheless, this can be indirectly achieved by means of build scripts called on `aea build`, which can do the checks manually according to the specific requirements of the project.
## Configuration
-> Store configuration in the environment
+!!! info "Factor 3"
+ Store configuration in the environment
Support: Good
-An AEA project can specify an environment configuration
-file `.env`, stored in the project root,
-that the framework will use to update
-environment variables before the execution of the AEA instance.
+An AEA project can specify an environment configuration file `.env`, stored in the project root, that the framework will use to update environment variables before the execution of the AEA instance.
-The CLI tool command `aea run` accepts the option `--env PATH`
-to change the default configuration file.
-However, the framework does not
-automatically switch between, nor allows to add,
-different types of configuration files, one for each
-deployment step (e.g. development, staging, production),
-without using the `--env` option.
+The CLI tool command `aea run` accepts the option `--env PATH` to change the default configuration file. However, the framework does not automatically switch between, nor allows to add, different types of configuration files, one for each deployment step (e.g. development, staging, production), without using the `--env` option.
-## Backing services
+## Backing Services
-> Treat backing services as attached resources
+!!! info "Factor 4"
+ Treat backing services as attached resources
Support: Good
-A persistent storage of an AEA can be seen
-as an attached resource in the 12-factor terminology.
-The default storage is SQLite, but the interface
-`AbstractStorageBacked` allows to implement
-specific wrappers to other backing services,
-without changing the AEA project code.
-The support for integrating
-different storage back-end implementations in an AEA project
-by using a plug-in mechanism is currently missing.
-
-Moreover, new adapters to backing services
-can be implemented as custom connections, which
-can connect to attached resources.
-This does not usually requires a change
-in the skill code, especially
-in the case when a custom protocol
-can abstract the details of the interaction with
-the specific resource.
+A persistent storage of an AEA can be seen as an attached resource in the 12-factor terminology. The default storage is SQLite, but the interface `AbstractStorageBacked` allows to implement specific wrappers to other backing services, without changing the AEA project code. The support for integrating different storage back-end implementations in an AEA project by using a plug-in mechanism is currently missing.
+Moreover, new adapters to backing services can be implemented as custom connections, which can connect to attached resources. This does not usually require a change in the skill code, especially in the case when a custom protocol can abstract the details of the interaction with the specific resource.
-## Build, release, run
+## Build, Release, Run
-> Strictly separate build and run stages
+!!! info "Factor 5"
+ Strictly separate build and run stages
Support: Excellent
-The phases of build, release and run
-of an AEA project are neatly separated,
-both for programmatic usage
-and through the usage of the CLI tool,
-as each of them corresponds to different subcommands.
+The phases of build, release and run of an AEA project are neatly separated, both for programmatic usage and through the usage of the CLI tool, as each of them corresponds to different subcommands.
## Processes
-> Execute the app as one or more stateless processes
+!!! info "Factor 6"
+ Execute the app as one or more stateless processes
Support: Excellent
-Whether the process is stateless depends on the specific AEA.
-No strict enforcement is applied by the framework.
-Moreover, dialogue histories can be stored
-with persistent storage, if enabled by the developer.
+Whether the process is stateless depends on the specific AEA. No strict enforcement is applied by the framework. Moreover, dialogue histories can be stored with persistent storage, if enabled by the developer.
-## Port binding
+## Port Binding
-> Export services via port binding
+!!! info "Factor 7"
+ Export services via port binding
Support: Excellent
-An AEA project may not need to expose services via HTTP.
-This property depends on the specific choices of
-the project developer, and the framework does not
-impose any restriction.
+An AEA project may not need to expose services via HTTP. This property depends on the specific choices of the project developer, and the framework does not impose any restriction.
-One of the provided package, the "HTTP server" connection,
-relies on `aiohttp`, which makes the connection completely
-self-contained—therefore, it satisfies the requirement.
+One of the provided package, the "HTTP server" connection, relies on `aiohttp`, which makes the connection completely self-contained—therefore, it satisfies the requirement.
-Another relevant example is the ACN node, which
-exposes its service to the Libp2p AEA connection
+Another relevant example is the ACN node, which exposes its service to the Libp2p AEA connection
## Concurrency
-> Scale out via the process model
+!!! info "Factor 8"
+ Scale out via the process model
Support: Not Supported
-The framework does not easily allow to scale up an
-AEA instance with multiple processes,
-as it is bound to a process.
-However, note that its attached services
-can live in a different process, which could
-give better scalability.
+The framework does not easily allow to scale up an AEA instance with multiple processes, as it is bound to a process. However, note that its attached services can live in a different process, which could give better scalability.
## Disposability
-> Maximize robustness with fast startup and graceful shutdown
+!!! info "Factor 9"
+ Maximize robustness with fast startup and graceful shutdown
Support: Good
-Disposability of an AEA instance
-depends, in general, on the AEA itself;
-whether the connections can be quickly
-connected and disconnected,
-whether skills can be easily torn
-down or not, whether other resources
-can be detached successfully like
-the persistent storage,
-just to name a few examples.
-
-There has been put some effort into
-reducing startup time, and to ensure
-that a graceful shut down can happen
-when the process receives a SIGTERM
-under normal circumstances,
-but robustness cannot be ensured for individual components,
-as it depends on their implementation.
-
-Additionally,
-the framework does provide some features to
-control some aspects of AEA disposability,
-e.g. the possibility to change
-execution timeout for behaviours or handlers,
-implementation of an effective exception propagation
-from a component code to the main agent loop.
-
-## Dev/prod parity
-
-> Keep development, staging, and production as similar as possible
+Disposability of an AEA instance depends, in general, on the AEA itself; whether the connections can be quickly connected and disconnected, whether skills can be easily torn down or not, whether other resources can be detached successfully like the persistent storage, just to name a few examples.
+
+There has been put some effort into reducing startup time, and to ensure that a graceful shut-down can happen when the process receives a SIGTERM under normal circumstances, but robustness cannot be ensured for individual components, as it depends on their implementation.
+
+Additionally, the framework does provide some features to control some aspects of AEA disposability, e.g. the possibility to change execution timeout for behaviours or handlers, implementation of an effective exception propagation from a component code to the main agent loop.
+
+## Dev/Prod Parity
+
+!!! info "Factor 10"
+ Keep development, staging, and production as similar as possible
Support: Good
-This aspect mostly depends on the specific AEA project,
-and the framework does not impose particular restrictions
-on best deployment practices (e.g. continuous integration,
-same backing services between development
-and production stages).
+This aspect mostly depends on the specific AEA project, and the framework does not impose particular restrictions on best deployment practices (e.g. continuous integration, same backing services between development and production stages).
## Logs
-> Treat logs as event streams
+!!! info "Factor 11"
+ Treat logs as event streams
Support: Excellent
-Thanks to the seamless integration with the
-Python standard library `logging`,
-the developer or the deployer has great control
-on the routing and filtering of log records.
-The behaviour can be changed by providing
-a proper configuration in the AEA project configuration file,
-according to the standard library specification.
-The framework facilitates this
-by creating ad-hoc logger names that can be used
-for finer-grained routing or filtering;
-for example, each AEA instance uses its own
-logging namespace to send logging events.
-Integration with other log handlers
-is delegated to extensions of the standard library,
-hence not necessarily coupled with the AEA framework.
-
-## Admin processes
-
-> Run admin/management tasks as one-off processes
+Thanks to the seamless integration with the Python standard library `logging`, the developer or the deployer has great control on the routing and filtering of log records. The behaviour can be changed by providing a proper configuration in the AEA project configuration file, according to the standard library specification. The framework facilitates this by creating ad-hoc logger names that can be used for finer-grained routing or filtering; for example, each AEA instance uses its own logging namespace to send logging events. Integration with other log handlers is delegated to extensions of the standard library, hence not necessarily coupled with the AEA framework.
+
+## Admin Processes
+
+!!! info "Factor 12"
+ Run admin/management tasks as one-off processes
Support: Good
-The CLI tool provides commands to
-manage private keys and ledger related operations, and
-it is possible to extend it with a plugin to manage databases of AEA's persistent storage
-for maintenance operations.
-
-Moreover, the Python programming language
-makes it easy to run one-off scripts or running a console
-(also known as REPL) to do management tasks.
-It follows that it is also easy to ensure
-dependency isolation and same configurations
-of the running AEA instance.
+The CLI tool provides commands to manage private keys and ledger related operations, and it is possible to extend it with a plugin to manage databases of AEA's persistent storage for maintenance operations.
+
+Moreover, the Python programming language makes it easy to run one-off scripts or running a console (also known as REPL) to do management tasks. It follows that it is also easy to ensure dependency isolation and same configurations of the running AEA instance.
diff --git a/docs/acn-internals.md b/docs/acn-internals.md
index 5d16d212d2..41bd744edf 100644
--- a/docs/acn-internals.md
+++ b/docs/acn-internals.md
@@ -1,79 +1,50 @@
+# ACN Internals
-The aim of this document is to describe at a high-level
-the main implementation of the Agent Communication Network (ACN).
+The aim of this document is to describe at a high-level the main implementation of the Agent Communication Network (ACN).
In particular:
-- the `libp2p_node` Golang library;
-- the `p2p_libp2p` AEA connection, written in Python, that implements the _direct connection_ with an ACN peer;
-- the `p2p_libp2p_client` AEA connection, written in Python, which implements the _delegate connection_ with an ACN peer.
+- the `libp2p_node` Golang library;
+- the `p2p_libp2p` AEA connection written in Python, that implements the **direct connection** with an ACN peer;
+- the `p2p_libp2p_client` AEA connection written in Python, which implements the **delegate connection** with an ACN peer.
-It is assumed the reader already knows what is the ACN and
-its purposes; if not, we suggest reading this page.
+It is assumed the reader already knows what is the ACN and its purposes; if not, we suggest reading this page.
This documentation page is structured as follows:
-- Firstly, the ACN protocol is described: all the messages
-and data structures involved, as well as some example of interaction
-protocol with these messages;
-- Then, it is explained how a peer can join an existing ACN network,
-and the message exchange involved;
-- It follows the description of the journey of an envelope
- in the ACN network: from the agent connection to its contact
- peer, between ACN peers, and then from the contact peer of the
- destination agent to the target agent;
-- The following section describes the functionalities
- of the AEA connections that allow to communicate through
- the ACN: `fetchai/p2p_libp2p` and `fetchia/p2p_libp2p_delegate`;
-- The documentation ends with a section of known issues and limitations
- of the current implementation.
+- Firstly, the ACN protocol is described: all the messages and data structures involved, as well as some example of interaction protocol with these messages;
+- Then, it is explained how a peer can join an existing ACN network, and the message exchange involved;
+- It follows the description of the journey of an envelope in the ACN network: from the agent connection to its contact peer, between ACN peers, and then from the contact peer of the destination agent to the target agent;
+- The following section describes the functionalities of the AEA connections that allow to communicate through the ACN: `fetchai/p2p_libp2p` and `fetchia/p2p_libp2p_delegate`;
+- The documentation ends with a section of known issues and limitations of the current implementation.
## Messages and Data Structures
-At the foundation of the ACN there is the _ACN protocol_.
-The protocol messages and the reply structure are generated from this
-protocol specification,
-using the protocol generator.
-Therefore, it uses Protocol Buffers
-as a serialization format,
-and the definition of the data structures involved is defined in this
-`.proto` file.
+At the foundation of the ACN there is the _ACN protocol_. The protocol messages and the reply structure are generated from this protocol specification, using the protocol generator. Therefore, it uses Protocol Buffers as a serialization format, and the definition of the data structures involved is defined in this `.proto` file.
-To know more about the protocol generator, refer to the relevant
-section of the documentation:
-Protocol Generator.
+To know more about the protocol generator, refer to the relevant section of the documentation: Protocol Generator.
### Agent Record
-An _agent record_ is a data structure containing information about an
-agent and its Proof-of-Representation (PoR) to be used by a peer for other peers.
-This data structure is used as a payload in other ACN messages (see below).
+An _agent record_ is a data structure containing information about an agent and its Proof-of-Representation (PoR) to be used by a peer for other peers. This data structure is used as a payload in other ACN messages (see below).
The `AgentRecord` data structure contains the following fields:
- `service_id`: a string describing the service identifier.
-- `ledger_id`: a string. It is the identifier of the ledger
- this agent record is associated to.
- Currently, the allowed values are:
+- `ledger_id`: a string. It is the identifier of the ledger this agent record is associated to. Currently, the allowed values are:
- `fetchai`, the identifier for the Fetch.AI ledger;
- `ethereum`, the identifier for the Ethereum ledger;
- `cosmos`, the identifier for the Cosmos ledger;
-- `address`: a string. It is the public key of a public-private key pair.
- It is used as an identifier for routing purposes.
+- `address`: a string. It is the public key of a public-private key pair. It is used as an identifier for routing purposes.
- `public_key`: a string. The representative's public key. Used in case of (PoR).
- `peer_public_key`: a string. The public key of the peer.
- `signature`: a string. The signature for PoR.
-- `not_before`: a string. Specify the lower bound for certificate validity.
- If it is a string, it must follow the format: `YYYY-MM-DD`. It will be interpreted as time zone UTC-0
-- `not_after`: a string. Specify the upper bound for certificate validity.
- If it is a string, it must follow the format: `YYYY-MM-DD`. It will be interpreted as time zone UTC-0.
-
+- `not_before`: a string. Specify the lower bound for certificate validity. If it is a string, it must follow the format: `YYYY-MM-DD`. It will be interpreted as time zone UTC-0
+- `not_after`: a string. Specify the upper bound for certificate validity. If it is a string, it must follow the format: `YYYY-MM-DD`. It will be interpreted as time zone UTC-0.
### ACN Message
-Entities in the ACN (i.e. either agents or peers) exchange _ACN messages_.
-An ACN message contains a `payload` field,
-which is the actual content of the message.
+Entities in the ACN (i.e. either agents or peers) exchange _ACN messages_. An ACN message contains a `payload` field, which is the actual content of the message.
There are different types of payloads:
@@ -85,35 +56,24 @@ There are different types of payloads:
### Status
-The `Status` payload is used as a response message to inform
-the sender about the handling of certain requests.
-The payload contains:
+The `Status` payload is used as a response message to inform the sender about the handling of certain requests. The payload contains:
-- the `status_code`, a positive integer among the ones in the
- Protobuf file.
+- the `status_code`, a positive integer among the ones in the Protobuf file.
- a list of error messages (string).
-A status code `0`, identified as `SUCCESS`,
-means that the request has been processed successfully.
-Status codes greater than `0` can be:
+A status code `0`, identified as `SUCCESS`, means that the request has been processed successfully. Status codes greater than `0` can be:
- Generic errors: errors that occur under generic circumstances.
- - `ERROR_UNSUPPORTED_VERSION`, with integer value `1`: the receiver of the message
- does not support the protocol version of the sender;
- - `ERROR_UNEXPECTED_PAYLOAD`, with integer value `2`: the payload could not be
- deserialised on the receiver side;
+ - `ERROR_UNSUPPORTED_VERSION`, with integer value `1`: the receiver of the message does not support the protocol version of the sender;
+ - `ERROR_UNEXPECTED_PAYLOAD`, with integer value `2`: the payload could not be deserialized on the receiver side;
- `ERROR_GENERIC`, with integer value `3`: an internal error;
- - `ERROR_SERIALIZATION`, with integer value `4`: a serialization error occurred
- on the receiving end;
+ - `ERROR_SERIALIZATION`, with integer value `4`: a serialization error occurred on the receiving end;
-- Register errors: errors that occur during agent registration operations in the ACN.
+- Register errors: errors that occur during agent registration operations in the ACN.
- - `ERROR_WRONG_AGENT_ADDRESS`, with integer value `10`:
- the PoR by a peer from another peer does not match the destination address of
- the envelope to be routed by the receiving peer.
- - `ERROR_WRONG_PUBLIC_KEY`, with integer value `11`: the
- representative peer public key does not match the one in the agent record;
+ - `ERROR_WRONG_AGENT_ADDRESS`, with integer value `10`: the PoR by a peer from another peer does not match the destination address of the envelope to be routed by the receiving peer.
+ - `ERROR_WRONG_PUBLIC_KEY`, with integer value `11`: the representative peer public key does not match the one in the agent record;
- `ERROR_INVALID_PROOF`, with integer value `12`: the signature is invalid;
- `ERROR_UNSUPPORTED_LEDGER`, with integer value `13`: the ledger of the PoR is not supported by the peer;
@@ -122,26 +82,21 @@ Status codes greater than `0` can be:
- `ERROR_UNKNOWN_AGENT_ADDRESS`, with integer value `20`: the requested agent address has not been found in the local DHT of the peer;
- `ERROR_AGENT_NOT_READY`, with integer value `21`: the agent is not ready for envelope delivery.
-
### Register
-The `Register` payload is used to request a peer to register an agent among his known ones.
-The payload contains the field `record`, which is an instance of `AgentRecord`.
+The `Register` payload is used to request a peer to register an agent among his known ones. The payload contains the field `record`, which is an instance of `AgentRecord`.
### LookupRequest
-The `LookupRequest` payload is sent between peer to look-up addresses in the Distributed Hash Table (DHT).
-It contains the agent address (a string) that the sender needs to correctly route an envelope.
+The `LookupRequest` payload is sent between peer to look-up addresses in the Distributed Hash Table (DHT). It contains the agent address (a string) that the sender needs to correctly route an envelope.
### LookupResponse
-The `LookupResponse` payload is the response sent by a peer that received a `LookupRequest`.
-It contains the `AgentRecord` associated to the requested address.
+The `LookupResponse` payload is the response sent by a peer that received a `LookupRequest`. It contains the `AgentRecord` associated to the requested address.
### AeaEnvelope
-The `AeaEnvelope` payload contains the envelope sent by an agent and to be delivered to another agent.
-It contains:
+The `AeaEnvelope` payload contains the envelope sent by an agent and to be delivered to another agent. It contains:
- `envelope`: the envelope to be forwarded, in byte representation;
- an `AgentRecord` (see above).
@@ -156,10 +111,9 @@ The ACN protocol specifies three different possible interactions:
### "Registration" Interaction
-The registration interaction is used by delegate agents or relayed peers
-to register themselves to another peer.
+The registration interaction is used by delegate agents or relayed peers to register themselves to another peer.
-
+``` mermaid
sequenceDiagram
participant Agent/RelayedPeer
participant Peer
@@ -176,14 +130,13 @@ to register themselves to another peer.
else unsupported ledger
Peer->>Agent/RelayedPeer: Status(ERROR_UNSUPPORTED_LEDGER)
end
-
+```
### "Look-up" Interaction
-The look-up interaction is used by a peer
-to request information to another peer about an agent address.
+The look-up interaction is used by a peer to request information to another peer about an agent address.
-
+``` mermaid
sequenceDiagram
participant Peer1
participant Peer2
@@ -193,15 +146,13 @@ to request information to another peer about an agent address.
else unknown agent address
Peer2->>Peer1: Status(ERROR_UNKNOWN_AGENT_ADDRESS)
end
-
-
+```
### "Routing" Interaction
-The routing interaction is used by agents
-and peers to route the envelope through the ACN.
+The routing interaction is used by agents and peers to route the envelope through the ACN.
-
+``` mermaid
sequenceDiagram
participant Peer1
participant Peer2
@@ -214,41 +165,24 @@ and peers to route the envelope through the ACN.
else PoR errors
note over Peer1,Peer2: see above
end
-
-
-
+```
## Joining the ACN network
-When an ACN peer wants to join the network, it has
-to start from a list of _bootstrap peers_, i.e.
-a list of ACN peers to connect with (at least one).
+When an ACN peer wants to join the network, it has to start from a list of _bootstrap peers_, i.e. a list of ACN peers to connect with (at least one).
Each node handles four different types of libp2p streams:
-- the _notification stream_, identified by the URI `/aea-notif/`:
- this stream is used by new peers to notify their existence to
-- the _address stream_, identified by the URI `/aea-address/`:
- used to send look-up requests and look-up responses;
-- the _envelope stream_, identified by the URI `/aea/`:
- used to forward and to receive ACN envelopes;
-- the _register relay stream_, identified by the URI `/aea-register/`:
- this is to receive messages from clients that want to register their agents addresses;
- this peer, and then it can register their addresses.
-
-To begin with, the node process initializes
-the transport connections with the bootstrap peers,
-the local copy of the Kademlia Distributed
-Hash Table (DHT),
-the persistent storage for agent records,
-and performs other non-functional operations
-like setting up the Prometheus monitoring system.
-Optionally, can also start listening for relay connections
-and delegate connections.
+- the _notification stream_, identified by the URI `/aea-notif/`: this stream is used by new peers to notify their existence to
+- the _address stream_, identified by the URI `/aea-address/`: used to send look-up requests and look-up responses;
+- the _envelope stream_, identified by the URI `/aea/`: used to forward and to receive ACN envelopes;
+- the _register relay stream_, identified by the URI `/aea-register/`: this is to receive messages from clients that want to register their agents addresses; this peer, and then it can register their addresses.
+
+To begin with, the node process initializes the transport connections with the bootstrap peers, the local copy of the Kademlia Distributed Hash Table (DHT), the persistent storage for agent records, and performs other non-functional operations like setting up the Prometheus monitoring system. Optionally, can also start listening for relay connections and delegate connections.
Then, it sets up the notification stream and notifies the bootstrap peers (if any).
-
+``` mermaid
sequenceDiagram
participant Peer1
participant Peer2
@@ -269,18 +203,15 @@ Then, it sets up the notification stream and notifies the bootstrap peers (if an
Peer1->>Peer3: register address
end
note over Peer1: set up: - address stream - envelope stream - register relay stream
-
+```
-### Relay connections
+### Relay Connections
-If the ACN node is configured to run the relay service,
-it sets up the register relay stream, waiting for registration
-requests.
+If the ACN node is configured to run the relay service, it sets up the register relay stream, waiting for registration requests.
-The following diagram shows an example of the message exchanged
-during a registration request:
+The following diagram shows an example of the message exchanged during a registration request:
-
+``` mermaid
sequenceDiagram
participant Agent
participant Peer
@@ -303,58 +234,33 @@ during a registration request:
Peer->>Agent: Status(SUCCESS)
note over Peer: announce agent address to other peers
end
-
+```
-### Delegate connections
+### Delegate Connections
-If the ACN node is configured to run the delegate service,
-it start listening from a TCP socket at a configurable URI.
+If the ACN node is configured to run the delegate service, it starts listening from a TCP socket at a configurable URI.
-To see a diagram of the message exchanged
-during a registration request read
-this section.
+To see a diagram of the message exchanged during a registration request read this section.
-## ACN transport
+## ACN Transport
-In the following sections, we describe the main three steps of the routing
-of an envelope through the ACN:
+In the following sections, we describe the main three steps of the routing of an envelope through the ACN:
-- _ACN entrance_: when an envelope sent by an agent enters
- the peer-to-peer network via the peer the agent is connected to
- i.e. agent-to-peer communication;
-- _ACN routing_: when an envelope gets routed through the peer-to-peer network,
- i.e. peer-to-peer communication;
-- _ACN exit_: when an envelope gets delivered to the receiving agent
- through its representative peer, i.e. peer-to-agent communication.
+- _ACN entrance_: when an envelope sent by an agent enters the peer-to-peer network via the peer the agent is connected to i.e. agent-to-peer communication;
+- _ACN routing_: when an envelope gets routed through the peer-to-peer network, i.e. peer-to-peer communication;
+- _ACN exit_: when an envelope gets delivered to the receiving agent through its representative peer, i.e. peer-to-agent communication.
-
### ACN Envelope Entrance: Agent -> Peer
-In this section, we will describe the interaction protocols between agents and peers
-for the messages sent by the agent to the ACN network;
-in particular, the communication from the contact peer of an agent to the agent.
+In this section, we will describe the interaction protocols between agents and peers for the messages sent by the agent to the ACN network; in particular, the communication from the contact peer of an agent to the agent.
The following diagram explains the exchange of messages on entering an envelope in the ACN.
-In the case of _direct connection_,
-`Agent` is a Python process, whereas `Peer` is in a separate (Golang) process.
-The logic of the Python Agent client is implemented in
-the AEA connection `fetchai/p2p_libp2p`
-The communication between `Agent` and `Peer` is done through
-an OS pipe for Inter-Process Communication (IPC) between the AEA's process and the libp2p node process;
-then, the message gets enqueued to an output queue by an input coroutine.
-Finally, the envelope ends up in an output queue,
-which is processed by an output coroutine and routed to the next peer.
-
-In the case of _delegate connection_,
-the message exchange is very similar; however, instead of using
-pipes, the communication is done through the network, i.e. TCP,
-with a peer which has the delegate service enabled.
-The logic of the `Agent` client connected with a delegate connection
-is implemented in
-the AEA connection `fetchai/p2p_libp2p_client`
-
-
+In the case of _direct connection_, `Agent` is a Python process, whereas `Peer` is in a separate (Golang) process. The logic of the Python Agent client is implemented in the AEA connection `fetchai/p2p_libp2p` The communication between `Agent` and `Peer` is done through an OS pipe for Inter-Process Communication (IPC) between the AEA's process and the libp2p node process; then, the message gets enqueued to an output queue by an input coroutine. Finally, the envelope ends up in an output queue, which is processed by an output coroutine and routed to the next peer.
+
+In the case of _delegate connection_, the message exchange is very similar; however, instead of using pipes, the communication is done through the network, i.e. TCP, with a peer which has the delegate service enabled. The logic of the `Agent` client connected with a delegate connection is implemented in the AEA connection `fetchai/p2p_libp2p_client`
+
+``` mermaid
sequenceDiagram
participant Agent
participant Peer
@@ -378,82 +284,65 @@ the
- sequenceDiagram
- participant Agent
- participant Peer1
- participant Peer2
- Agent->>Peer1: AeaEnvelope
- alt envelope sender not registered locally
- note over Peer1: stop, log error
- end
-
-
-2) the `target` of the envelope is
- the local agent connected to the peer:
- the envelope is routed to the local agent.
-
-
- sequenceDiagram
- participant Agent
- participant Peer1
- participant Peer2
- Agent->>Peer1: AeaEnvelope
- alt target == peer1.my_agent
- note over Peer1: envelope destinated to local agent, not routing
- loop agent not ready
- note over Peer1: sleep for 100ms
+ ``` mermaid
+ sequenceDiagram
+ participant Agent
+ participant Peer1
+ participant Peer2
+ Agent->>Peer1: AeaEnvelope
+ alt envelope sender not registered locally
+ note over Peer1: stop, log error
end
- Peer1->>Agent: AeaEnvelope
- Agent->>Peer1: Status(Success)
- end
-
-
-3) the `target` is a delegate client.
- Send the envelope via TCP.
-
-
- sequenceDiagram
- participant Delegate
- participant Peer1
- participant Peer2
- Delegate->>Peer1: AeaEnvelope
- alt destination is a delegate
- note over Peer1: send envelope to delegate via TCP
- Peer1->>Delegate: AeaEnvelope
- Delegate->>Peer1: Status(Success)
- end
-
+ ```
+
+2. the `target` of the envelope is the local agent connected to the peer: the envelope is routed to the local agent.
+
+ ``` mermaid
+ sequenceDiagram
+ participant Agent
+ participant Peer1
+ participant Peer2
+ Agent->>Peer1: AeaEnvelope
+ alt target == peer1.my_agent
+ note over Peer1: envelope destinated to local agent, not routing
+ loop agent not ready
+ note over Peer1: sleep for 100ms
+ end
+ Peer1->>Agent: AeaEnvelope
+ Agent->>Peer1: Status(Success)
+ end
+ ```
+
+3. the `target` is a delegate client. Send the envelope via TCP.
+
+ ``` mermaid
+ sequenceDiagram
+ participant Delegate
+ participant Peer1
+ participant Peer2
+ Delegate->>Peer1: AeaEnvelope
+ alt destination is a delegate
+ note over Peer1: send envelope to delegate via TCP
+ Peer1->>Delegate: AeaEnvelope
+ Delegate->>Peer1: Status(Success)
+ end
+ ```
-4) Otherwise, look up the local DHT.
- If an entry is found, use it;
- otherwise, send a look-up request
- to connected peers.
+4. Otherwise, look up the local DHT. If an entry is found, use it; otherwise, send a look-up request to connected peers.
-
+``` mermaid
sequenceDiagram
participant Agent
participant Peer1
@@ -474,13 +363,11 @@ we may have different scenario:
end
end
note over Peer1,Peer2: Now Peer1 knows the contact peer is PeerX
-
-
-In particular, when a peer receives a LookupRequest message,
-it does the following:
+```
+In particular, when a peer receives a LookupRequest message, it does the following:
-
+``` mermaid
sequenceDiagram
participant Peer1
participant Peer2
@@ -500,13 +387,11 @@ it does the following:
Peer2->>Peer1:Status(UNKNOWN_AGENT_ADDRESS)
end
end
-
+```
-Let `Peer3` the contact peer of the recipient of the envelope.
-The following diagram shows how the contact peer of the
-envelope recipient handles the incoming envelope:
+Let `Peer3` the contact peer of the recipient of the envelope. The following diagram shows how the contact peer of the envelope recipient handles the incoming envelope:
-
+``` mermaid
sequenceDiagram
participant Peer1
participant Peer3
@@ -542,20 +427,15 @@ envelope recipient handles the incoming envelope:
Peer3->>Peer1: Status(ERROR_UNKNOWN_AGENT_ADDRESS)
end
end
-
+```
### ACN Envelope Exit: Peer -> Agent
-The following diagram explains the exchange of messages on exiting an envelope in the ACN.
-That is, the communication from the contact peer of an agent to the agent.
+The following diagram explains the exchange of messages on exiting an envelope in the ACN. That is, the communication from the contact peer of an agent to the agent.
-The same message exchange is done
-both in the case of direct connection and
-delegate connection,
-similarly for what has been described for the envelope entrance
-(see above).
+The same message exchange is done both in the case of direct connection and delegate connection, similarly for what has been described for the envelope entrance (see above).
-
+``` mermaid
sequenceDiagram
participant Agent
participant Peer
@@ -571,43 +451,26 @@ similarly for what has been described for the envelope entrance
else wrong payload
Agent->>Peer: Status(GENERIC_ERROR)
end
-
+```
## Connect your AEA to the ACN
-To connect the AEA to the ACN network,
-there are two AEA connections available:
+To connect the AEA to the ACN network, there are two AEA connections available:
-- the `fetchai/p2p_libp2p`, that implements
- a direct connection, and
-- the `fetchai/p2p_libp2p_delegate` connection,
- that implements the delegate connection.
+- the `fetchai/p2p_libp2p`, that implements a direct connection, and
+- the `fetchai/p2p_libp2p_delegate` connection, that implements the delegate connection.
-For more information on the AEA connection package type,
-refer to this guide.
+For more information on the AEA connection package type, refer to this guide.
-### The `fetchai/p2p_libp2p` connection
+### The `fetchai/p2p_libp2p` Connection
-The source code of the `fetchai/p2p_libp2p` connection
-can be downloaded from
-the AEA Registry,
-or from the main AEA framework repository.
+The source code of the `fetchai/p2p_libp2p` connection can be downloaded from the AEA Registry, or from the main AEA framework repository.
-The package provides the connection class `P2PLibp2pConnection`,
-which implements the `Connection` interface and
-therefore can be used by the Multiplexer as any other connection.
+The package provides the connection class `P2PLibp2pConnection`, which implements the `Connection` interface and therefore can be used by the Multiplexer as any other connection.
-- The `connect` method of this connection spawns a new instance
- of the `libp2p_node` program
-(i.e. an ACN peer node) and connects to it through OS pipes.
-Then, it sets up the _message receiving loop_,
- which enqueues messages in the input queue to be read by `read` method calls,
- and the _message sending loop_,
- which dequeues messages from the output queue and forwards them to the Libp2p node.
-The loops are run concurrently in the Multiplexer thread,
- using the Python asynchronous programming library `asyncio`.
+- The `connect` method of this connection spawns a new instance of the `libp2p_node` program (i.e. an ACN peer node) and connects to it through OS pipes. Then, it sets up the _message receiving loop_, which enqueues messages in the input queue to be read by `read` method calls, and the _message sending loop_, which dequeues messages from the output queue and forwards them to the Libp2p node. The loops are run concurrently in the Multiplexer thread, using the Python asynchronous programming library `asyncio`.
-
+``` mermaid
sequenceDiagram
participant Libp2p Connection
participant sending loop
@@ -624,13 +487,11 @@ The loops are run concurrently in the Multiplexer thread,
deactivate Libp2p Node
deactivate sending loop
deactivate receiving loop
-
+```
-- The `send` method enqueues a message in the output queue.
-The message is then dequeued by the sending loop,
-and then sent to the Libp2p node.
+- The `send` method enqueues a message in the output queue. The message is then dequeued by the sending loop, and then sent to the Libp2p node.
-
+``` mermaid
sequenceDiagram
participant Libp2p Connection
participant sending loop
@@ -656,13 +517,11 @@ and then sent to the Libp2p node.
else envelope decoding error
Libp2p Node->>sending loop: Status(ERROR_SERIALIZATION)
end
-
+```
-- The `receive` method dequeues a message from the input queue.
-The queue is populated by the receiving loop,
-which receives messages from the Libp2p node.
+- The `receive` method dequeues a message from the input queue. The queue is populated by the receiving loop, which receives messages from the Libp2p node.
-
+``` mermaid
sequenceDiagram
participant Libp2p Connection
participant receiving loop
@@ -688,34 +547,19 @@ which receives messages from the Libp2p node.
end
Libp2p Connection->>receiving loop: read message from output queue
note over Libp2p Connection: return message to Multiplexer
-
-
-- the `disconnect` method stops both the receiving loop and the sending loop,
- and stops the Libp2p node.
-
-### The `fetchai/p2p_libp2p_delegate` connection
-
-The source code of the `fetchai/p2p_libp2p_delegate` connection
-can be downloaded from
-the main AEA framework repository.
-or from the main AEA framework repository.
-
-The package provides the connection class `P2PLibp2pClientConnection`,
-which implements the `Connection` interface and
-therefore can be used by the Multiplexer as any other connection.
-
-- The `connect` method of this connection will set up a TCP
- connection to the URI of the delegate peer. Then, it will
- send a `Register` request to register the agent among the peer's
- client connections.
- On registration success, it sets up the _message receiving loop_,
- which enqueues messages in the input queue to be read by read method calls,
- and the _message sending loop_, which dequeues messages from the output queue
- and forwards them to the Libp2p node.
- The loops are run concurrently in the Multiplexer thread,
- using the Python asynchronous programming library `asyncio`.
-
-
+```
+
+- the `disconnect` method stops both the receiving loop and the sending loop, and stops the Libp2p node.
+
+### The `fetchai/p2p_libp2p_delegate` Connection
+
+The source code of the `fetchai/p2p_libp2p_delegate` connection can be downloaded from the main AEA framework repository. or from the main AEA framework repository.
+
+The package provides the connection class `P2PLibp2pClientConnection`, which implements the `Connection` interface and therefore can be used by the Multiplexer as any other connection.
+
+- The `connect` method of this connection will set up a TCP connection to the URI of the delegate peer. Then, it will send a `Register` request to register the agent among the peer's client connections. On registration success, it sets up the _message receiving loop_, which enqueues messages in the input queue to be read by read method calls, and the _message sending loop_, which dequeues messages from the output queue and forwards them to the Libp2p node. The loops are run concurrently in the Multiplexer thread, using the Python asynchronous programming library `asyncio`.
+
+``` mermaid
sequenceDiagram
participant Libp2p Client Connection
participant Libp2p Node
@@ -744,34 +588,24 @@ therefore can be used by the Multiplexer as any other connection.
activate Libp2p Node
deactivate Libp2p Node
end
-
+```
-- The `send` method and the `receive` methods behave similarly to
- the `send` and `receive` methods of the
- `p2p_libp2p connection`,
- in terms of message exchange;
- however, the communication is done via TCP rather than pipes.
+- The `send` method and the `receive` methods behave similarly to the `send` and `receive` methods of the `p2p_libp2p connection`, in terms of message exchange; however, the communication is done via TCP rather than pipes.
-- The `disconnect` method interrupts the connection with the delegate peer,
- without explicitly unregistering.
+- The `disconnect` method interrupts the connection with the delegate peer, without explicitly unregistering.
-## Known issues and limitations
+## Known Issues and Limitations
-In this section, we provide a list of known issues
-and limitations of the current implementation
-of the ACN, considering both the ACN nodes (written in Golang)
-and the AEA connections, for the Python AEA framework, to interact with them.
+In this section, we provide a list of known issues and limitations of the current implementation of the ACN, considering both the ACN nodes (written in Golang) and the AEA connections, for the Python AEA framework, to interact with them.
-### Delegate client on client disconnection/reconnection
+### Delegate Client on Client Disconnection/Reconnection
-In case of disconnection/reconnection, delegate client record will be removed.
-This can cause two problems: either the delegate client is not found,
-or connection is closed during the send operation.
+In case of disconnection/reconnection, delegate client record will be removed. This can cause two problems: either the delegate client is not found, or connection is closed during the send operation.
Possible solutions:
- Create more complicated structure for clients storage;
-- Keep the delegate client record for longer;
+- Keep the delegate client record for longer;
- Clean up the record by timeout, per client queues.
Code references:
@@ -779,33 +613,20 @@ Code references:
- record removed: https://github.com/fetchai/agents-aea/blob/1db1720081969bcec1be5a2000ca176475d2b487/libs/go/libp2p_node/dht/dhtpeer/dhtpeer.go#L864
- send code: https://github.com/fetchai/agents-aea/blob/1db1720081969bcec1be5a2000ca176475d2b487/libs/go/libp2p_node/dht/dhtpeer/dhtpeer.go#L955
+### Golang Node <> Python Client `libp2p` Connection
-### Golang Node <> Python Client `libp2p` connection
-
-In case of connection between the Golang side (i.e. ACN node)
-and the Python side (i.e. the `libp2p` AEA connection) is broken,
-there is no reconnection attempt.
-The Golang side connect to the Python server opened,
-but if the connection is broken Golang can try to reconnect;
-however, the Python side does not know about this and will restart
-the node completely.
+In case of connection between the Golang side (i.e. ACN node) and the Python side (i.e. the `libp2p` AEA connection) is broken, there is no reconnection attempt. The Golang side connect to the Python server opened, but if the connection is broken Golang can try to reconnect; however, the Python side does not know about this and will restart the node completely.
-Possible solutions: the problem requires updates on both sides and assume possible timeouts on broken connection.
-If connection is broken, the Python side awaits for reconnection from Golang side,
-and restart node completely after timeout.
+Possible solutions: the problem requires updates on both sides and assume possible timeouts on broken connection. If connection is broken, the Python side awaits for reconnection from Golang side, and restart node completely after timeout.
-### What a peer should do if it receives an acknowledgement with an error?
+### What a Peer Should Do if it Receives an Acknowledgement with an Error?
-If an ACN response is the `Status` with error code different from `SUCCESS`,
-the forwarding to other peers is not repeated.
+If an ACN response is the `Status` with error code different from `SUCCESS`, the forwarding to other peers is not repeated.
-A possible solution is to resend the message; however,
-not clear why it should help in case of healthy connection,
-how many times the sender should retry, and how it would help.
+A possible solution is to resend the message; however, not clear why it should help in case of healthy connection, how many times the sender should retry, and how it would help.
-Discussion on GitHub:
-https://github.com/fetchai/agents-aea/pull/2509#discussion_r642628983
+Discussion on GitHub: https://github.com/fetchai/agents-aea/pull/2509#discussion_r642628983
-### No possibility of switching peers
+### No Possibility of Switching Peers
In case of a peer becoming unavailable, a delegate client or relay client currently has no means to automatically switch the peer. In particular, the DHT should be updated when a client switches peers.
diff --git a/docs/acn.md b/docs/acn.md
index f083aeff33..03a74bdca0 100644
--- a/docs/acn.md
+++ b/docs/acn.md
@@ -1,17 +1,19 @@
+# Agent Communication Network
The agent communication network (ACN) provides a system for agents to find each other and communicate, solely based on their wallet addresses. It addresses the message delivery problem.
-## Message delivery problem
+## Message Delivery Problem
+
Agents need to contact each others. Given the wallet address of a target agent, how can the originator agent deliver a message to it whilst guaranteeing certain properties?
-The properties we would like to have are:
+The properties we would like to have, are:
- Reliability: with guarantees on message reception
- Authentication: to prevent impersonation
- Confidentiality: to prevent exposing sensitive information within the message
- Availability: some guarantees about the liveness of the service (tampering detection)
-The problem statement and the agents framework context impose a number of design constraints:
+The problem statement and the agent framework context impose a number of design constraints:
- Distributed environment: no assumption are placed about the location of the agent, they can be anywhere in the publicly reachable internet
- Decentralized environment: no trusted central authority
@@ -23,31 +25,27 @@ The ACN solves the above problem whilst providing the above guarantees and satis
The ACN is maintained by peers. Peers are not to be equated with agents. They are processes (usually distributed and decentralized) that together maintain the service. To use the service, agents need to associate themselves with peers. Thanks to digital signatures, the association between a given peer and agent can be verified by any participant in the system.
-## Distributed hash table
+## Distributed Hash Table
At its core, the ACN implements a distributed hash table (DHT). A DHT is similar to a regular hash table in that it stores key-value pairs. However, storage is distributed across the participating machines (peers) with an efficient lookup operation. This is enabled by:
- Consistent hashing: decide responsibility for assignment of the DHT key-value storage
-- Structured overlays: organize the participating peers in a well defined topology for efficient routing
+- Structured overlays: organize the participating peers in a well-defined topology for efficient routing
For the ACN, we use the DHT to store and maintain association between an agent address and the (network) location of its peer.
-
-## N-tier architecture
+## N-Tier Architecture
To satisfy different resource constraints and flexible deployment the ACN is implemented as a multi-tier architecture. As such, it provides an extension of the client-server model. The agent framework exploits this by implementing different tiers as different `Connections`:
-
-
Note
-
The p2p_libp2p_mailbox connection is not available yet.
-
-
+!!! note
+ The `p2p_libp2p_mailbox` connection is not available yet.
-## Trust and security
+## Trust and Security
An agent can choose which connection to use depending on the resource and trust requirements:
@@ -57,6 +55,3 @@ An agent can choose which connection to use depending on the resource and trust
All communication protocols use public cryptography to ensure security (authentication, confidentiality, and availability) using TLS handshakes with pre-shared public keys.
-
-
-
diff --git a/docs/aea-vs-mvc.md b/docs/aea-vs-mvc.md
index 5b34cbd149..787299479d 100644
--- a/docs/aea-vs-mvc.md
+++ b/docs/aea-vs-mvc.md
@@ -1,3 +1,5 @@
+# AEA and Web Frameworks
+
The AEA framework borrows several concepts from popular web frameworks like Django and Ruby on Rails.
## MVC
@@ -8,7 +10,7 @@ Both aforementioned web frameworks use the asynchronous messaging and other agent-oriented development assumptions. Hence, there is not a direct one-to-one relationship between MVC based architectures and the AEA framework. Nevertheless, there are some parallels which can help a developer familiar with MVC make quick progress in the AEA framework, in particular the development of `Skills`:
@@ -21,10 +23,8 @@ The AEA framework is based on Build a skill for an AEA
-
-
diff --git a/docs/agent-oriented-development.md b/docs/agent-oriented-development.md
index 86085e4883..22d507215e 100644
--- a/docs/agent-oriented-development.md
+++ b/docs/agent-oriented-development.md
@@ -1,69 +1,67 @@
-# Agent-oriented development
+# Agent-Oriented Development
In this section, we discuss some of the most fundamental characteristics of an agent-oriented approach to solution development, which might be different from existing paradigms and methodologies that you may be used to. We hope that with this, we can guide you towards the right mindset when designing your own agent-based solutions to real world problems.
## Decentralisation
-Multi-Agent Systems (**MAS**) are inherently decentralised. The vision is of an environment in which every agent is able to directly connect with everyone else and interact with them without having to rely on a third party acting as an intermediary or match-maker. This is in direct contrast to centralised systems in which a single entity is the central point of authority, through which all interactions happen. The conventional client-server model is an example of a centralized architecture where clients interact with one another regarding specific services (e.g. communication, commerce) only through a server.
+Multi-Agent Systems (**MAS**) are inherently decentralized. The vision is of an environment in which every agent is able to directly connect with everyone else and interact with them without having to rely on a third party acting as an intermediary or match-maker. This is in direct contrast to centralized systems in which a single entity is the central point of authority, through which all interactions happen. The conventional client-server model is an example of a centralized architecture where clients interact with one another regarding specific services (e.g. communication, commerce) only through a server.
This is not to say that facilitators and middlemen have no place in a multi-agent system; rather it is the '_commanding reliance on middlemen_' that MAS rejects.
-**Division of responsibilities:** In a decentralised system, every agent is equally privileged, and (in principle) should be able to interact with any other agent. The idea is very much aligned with the peer-to-peer paradigm, in which it is the voluntary participation and contribution of the peers that create the infrastructure. Therefore, in a decentralised system, there is no central 'enforcer'. This means all the work that would typically fall under the responsibilities of a central entity must be performed by individual parties in a decentralised system. Blockchain-based cryptocurrencies are a good example of this. A notable characteristic of cryptocurrencies is the absence of central trusted entities (e.g. banks). But this in turn means that most security precautions related to the handling of digital assets and the execution of transactions are the responsibility of individuals.
+**Division of responsibilities:** In a decentralized system, every agent is equally privileged, and (in principle) should be able to interact with any other agent. The idea is very much aligned with the peer-to-peer paradigm, in which it is the voluntary participation and contribution of the peers that create the infrastructure. Therefore, in a decentralized system, there is no central 'enforcer'. This means all the work that would typically fall under the responsibilities of a central entity must be performed by individual parties in a decentralized system. Blockchain-based cryptocurrencies are a good example of this. A notable characteristic of cryptocurrencies is the absence of central trusted entities (e.g. banks). But this in turn means that most security precautions related to the handling of digital assets and the execution of transactions are the responsibility of individuals.
-
+
-**Decentralisation vs distribution:** It is important to emphasise that by decentralisation we do not mean distribution; although multi-agent systems typically do also tend to be distributed. A distributed system is one whose components are physically located in different places and connected over a network. A fully centralised system, owned and operated by a single entity, may in fact be highly distributed. Google or Microsoft's cloud infrastructure are examples of this, where their components are distributed across the globe yet designed to work together harmoniously and function in unison. Decentralisation on the other hand refers to a system whose components may be owned, operated, and managed by different stakeholders, each with their own personal objectives, interests, and preferences which may not necessarily be aligned with one another or the system itself. Therefore, distribution refers to the physical placement of a system's components, whereas decentralisation refers to **a)** the diversity of ownership and control over a system's constituents, and **b)** the absence of central authorities between them.
+**Decentralisation vs distribution:** It is important to emphasise that by decentralisation we do not mean distribution; although multi-agent systems typically do also tend to be distributed. A distributed system is one whose components are physically located in different places and connected over a network. A fully centralized system, owned and operated by a single entity, may in fact be highly distributed. Google or Microsoft's cloud infrastructure are examples of this, where their components are distributed across the globe yet designed to work together harmoniously and function in unison. Decentralisation on the other hand refers to a system whose components may be owned, operated, and managed by different stakeholders, each with their own personal objectives, interests, and preferences which may not necessarily be aligned with one another or the system itself. Therefore, distribution refers to the physical placement of a system's components, whereas decentralisation refers to **a.** the diversity of ownership and control over a system's constituents, and **b.** the absence of central authorities between them.
-**Example:** To better illustrate the distinction between centralised and decentralised systems, consider another example: search and discoverability in a commerce environment. In a centralised system (say Amazon), there is a single search service -- provided, owned and run by the commerce company itself -- which takes care of all search-related functionality for every product within their domain. So to be discoverable in this system, all sellers must register their products with this particular service. However in a decentralised system, there may not necessarily be a single search service provider. There may be multiple such services, run by different, perhaps competing entities. Each seller has the freedom to register with (i.e. make themselves known to) one or a handful of services. On the buyers side, the more services they contact and query, the higher their chances of finding the product they are looking for.
+**Example:** To better illustrate the distinction between centralized and decentralized systems, consider another example: search and discoverability in a commerce environment. In a centralized system (say Amazon), there is a single search service -- provided, owned and run by the commerce company itself -- which takes care of all search-related functionality for every product within their domain. So to be discoverable in this system, all sellers must register their products with this particular service. However, in a decentralized system, there may not necessarily be a single search service provider. There may be multiple such services, run by different, perhaps competing entities. Each seller has the freedom to register with (i.e. make themselves known to) one or a handful of services. On the buyers side, the more services they contact and query, the higher their chances of finding the product they are looking for.
## Conflicting Environment
-As discussed above, the notion of decentralisation extends as far as ownership and control. Therefore, the different components that make up a decentralised system may each be owned by a different entity, designed according to very different principles and standards, with heterogeneous software and hardware, and each with internal objectives that may be fundamentally inconsistent, worse yet contradictory, with those of others.
+As discussed above, the notion of decentralisation extends as far as ownership and control. Therefore, the different components that make up a decentralized system may each be owned by a different entity, designed according to very different principles and standards, with heterogeneous software and hardware, and each with internal objectives that may be fundamentally inconsistent, worse yet contradictory, with those of others.
As such, a distinctive characteristic of a multi-agent environment, is that it is inhabited by more than one agent (as the name suggests), where each agent may be owned potentially by a different stakeholder (individual, company, government). Since by design, each agent represents and looks after the interests of its owner(s), and because different stakeholders may have unaligned, conflicting, or contradictory interests, it is very common to have multi-agent systems in which the agents' objectives, values and preferences are unaligned, conflicting, or contradictory.
**In practice:** There are practical implications that follow from the above when it comes to designing an agent. For example, it is not rational for an agent to automatically rely on the information it receives from other agents. The information could be:
-* Incomplete: what is unrevealed may have been deemed private for strategic reasons.
-* Uncertain: it may be the result of an inaccurate prediction.
-* Incorrect: it could be an outright lie, due to the adversarial nature of the environment.
+- Incomplete: what is unrevealed may have been deemed private for strategic reasons.
+- Uncertain: it may be the result of an inaccurate prediction.
+- Incorrect: it could be an outright lie, due to the adversarial nature of the environment.
-Therefore one can argue, that there is a degree of uncertainty attached to almost all information an agent receives or infers in a multi-agent system. It wouldn't then be illogical for an agent to take a sceptical approach: treating everything as uncertain, unless proved otherwise.
+Therefore, one can argue that there is a degree of uncertainty attached to almost all information an agent receives or infers in a multi-agent system. It wouldn't then be illogical for an agent to take a sceptical approach: treating everything as uncertain, unless proved otherwise.
-## Asynchronisation
+## Asynchronization
-The conflicting nature of multi-agent systems, consisting of self-interested autonomous agents, points to _asynchronisation_ as the preferred method of designing and managing processes and interactions.
+The conflicting nature of multi-agent systems, consisting of self-interested autonomous agents, points to _asynchronization_ as the preferred method of designing and managing processes and interactions.
-**Synchronisation vs asynchronisation:** In general, asynchronisation refers to the decoupling of events that do interact with one another but do not occur at predetermined intervals, not necessarily relying on each other's existence to function. This is in contrast with _synchronous_ systems in which processes are aware of one another, where one's execution depends in some way on the other.
+**Synchronisation vs asynchronization:** In general, asynchronization refers to the decoupling of events that do interact with one another but do not occur at predetermined intervals, not necessarily relying on each other's existence to function. This is in contrast with _synchronous_ systems in which processes are aware of one another, where one's execution depends in some way on the other.
-**Asynchronisation in MAS:** In the context of multi-agent systems, the decentralised and potentially conflicting nature of the environment creates uncertainty over the behaviour of the whole system, in particular of other agents. For example, suppose an agent `i` sends a message requesting some resources from an agent `j`. Since MAS often tends to be distributed, there is the usual uncertainties with communication over a network: `j` may never receive `i`'s request, or may receive it after a long delay. Furthermore, `j` could receive the request in time and respond immediately, but as mentioned in the last section, its answer might be incomplete (gives only some of the requested resources), uncertain (promises to give the resources, but cannot be fully trusted), or incorrect (sends a wrong resource). In addition, since agents are self-interested, `j` may _decide_ to reply much later, to the point that the resource is no longer useful to agent `i`, or `j` may simply decide not to respond at all. There might be a myriad of reasons why it may choose to do that; it could be because `j` assigns a low priority to answering `i` over its other tasks. But that's beside the point. The take away is that agents' autonomy strongly influences what can be expected of them, and of an environment inhabited by them. As such, developing for a system whose constituents are autonomous, e.g. agents in a multi-agent system, is fundamentally different from one whose constituents aren't, e.g. objects in an object-oriented system.
+**Asynchronization in MAS:** In the context of multi-agent systems, the decentralized and potentially conflicting nature of the environment creates uncertainty over the behaviour of the whole system, in particular of other agents. For example, suppose an agent `i` sends a message requesting some resources from an agent `j`. Since MAS often tends to be distributed, there is the usual uncertainties with communication over a network: `j` may never receive `i`'s request, or may receive it after a long delay. Furthermore, `j` could receive the request in time and respond immediately, but as mentioned in the last section, its answer might be incomplete (gives only some of the requested resources), uncertain (promises to give the resources, but cannot be fully trusted), or incorrect (sends a wrong resource). In addition, since agents are self-interested, `j` may _decide_ to reply much later, to the point that the resource is no longer useful to agent `i`, or `j` may simply decide not to respond at all. There might be a myriad of reasons why it may choose to do that; it could be because `j` assigns a low priority to answering `i` over its other tasks. But that's beside the point. The takeaway is that agents' autonomy strongly influences what can be expected of them, and of an environment inhabited by them. As such, developing for a system whose constituents are autonomous, e.g. agents in a multi-agent system, is fundamentally different from one whose constituents aren't, e.g. objects in an object-oriented system.
-**Objects vs agents:** In object-oriented systems, objects are entities that encapsulate state and perform actions, i.e. call methods, on this state. In object-oriented languages, like C++ and Java, it is common practice to declare methods as public, so they can be invoked by other objects in the system whenever they wish. This implies that an object does not control its own behaviour. If an object’s method is public, the object has no control over whether or not that method is executed.
+**Objects vs agents:** In object-oriented systems, objects are entities that encapsulate state and perform actions, i.e. call methods, on this state. In object-oriented languages, like C++ and Java, it is common practice to declare methods as public, so they can be invoked by other objects in the system whenever they wish. This implies that an object does not control its own behaviour. If an object’s method is public, the object has no control over whether that method is executed.
We cannot take for granted that an agent `j` will execute an action (the equivalent of a method in object-oriented systems) just because another agent `i` wants it to; this action may not be in the best interests of agent `j`. So we do not think of agents as invoking methods on one another, rather as _requesting_ actions. If `i` requests `j` to perform an action, then `j` may or may not perform the action. It may choose to do it later or do it in exchange for something. The locus of control is therefore different in object-oriented and agent-oriented systems. In the former, the decision lies with the object invoking the method, whereas in the latter, the decision lies with the agent receiving the request. This distinction could be summarised by the following slogan (from An Introduction to MultiAgent Systems by Michael Wooldridge):
>objects do it for free; agents do it because they want to.
-All of this makes asynchronisation the preferred method for designing agent processes and interactions. An agent's interactions should be independent of each other, as much as possible, and of the agent's decision making processes and actions. This means the success or failure of, or delay in any single interaction does not block the agent's other tasks.
+All of this makes asynchronization the preferred method for designing agent processes and interactions. An agent's interactions should be independent of each other, as much as possible, and of the agent's decision-making processes and actions. This means the success or failure of, or delay in any single interaction does not block the agent's other tasks.
## Time
-Closely related with the discussion of asynchronicity, is the idea that in multi-agent systems, time is not a universally agreed notion. Agents may not necessarily share the same clock and this fact must be taken into account when designing agent-based systems. For example, you cannot necessarily expect agents to synchronise their behaviour according to time (e.g. perform a certain task at a time `X`).
+Closely related with the discussion of asynchronicity, is the idea that in multi-agent systems, time is not a universally agreed notion. Agents may not necessarily share the same clock and this fact must be taken into account when designing agent-based systems. For example, you cannot necessarily expect agents to synchronise their behaviour according to time (e.g. perform a certain task at a time `X`).
-Another related issue, is that unlike some agent-based simulation (ABS) systems where there is a global tick rate for all agents, in AEA-based systems tick rates may be different for different agents. This is due to the fundamental difference that ABS systems control some aspects of all of their agents' executions while in AEA-based systems, agents are truly decoupled from one another - most likely distributed and running on different machines and networks - and there is absolutely no central unit that moderates any aspect of their behaviour.
+Another related issue, is that unlike some agent-based simulation (ABS) systems where there is a global tick rate for all agents, in AEA-based systems tick rates may be different for different agents. This is due to the fundamental difference that ABS systems control some aspects of all of their agents' executions while in AEA-based systems, agents are truly decoupled from one another - most likely distributed and running on different machines and networks - and there is absolutely no central unit that moderates any aspect of their behaviour.
## Complex, Incomplete, Inconsistent and Uncertain
The fourth characteristic(s) relate to the environment in which agents are expected to operate in, and these have been mentioned a number of times in the previous sections.
-The environment agents are suited for typically tend to be complex, to the point that it is usually impossible for any single agent to perceive the whole of the environment on its own. This means that at any point in time, any agent has a limited knowledge about the state of the environment. In other words, the agents;' information tend to be incomplete due to the complexity and sophistication of the world in which they reside.
+The environment agents are suited for typically tend to be complex, to the point that it is usually impossible for any single agent to perceive the whole of the environment on its own. This means that at any point in time, any agent has a limited knowledge about the state of the environment. In other words, the agents;' information tend to be incomplete due to the complexity and sophistication of the world in which they reside.
-Consider an agent which represents a driverless vehicle. The complexity of the problem of driving on the road makes it impossible for a single vehicle to have an accurate and up-to-date knowledge of the overall state of the world . This means that an agent's model of the world is at best uncertain. For instance, the vehicle, through its sensor may detect green light at a junction, and by being aware of what it means, it may infer that it is safe to cross a junction. However, that simply may not be true as another car in the opposite direction may still cross the junction violating their red light. Therefore, there is uncertainty associated with the knowledge "it is safe to cross the road because the light is green", and the agent must recognise that.
+Consider an agent which represents a driver-less vehicle. The complexity of the problem of driving on the road makes it impossible for a single vehicle to have an accurate and up-to-date knowledge of the overall state of the world . This means that an agent's model of the world is at best uncertain. For instance, the vehicle, through its sensor may detect green light at a junction, and by being aware of what it means, it may infer that it is safe to cross a junction. However, that simply may not be true as another car in the opposite direction may still cross the junction violating their red light. Therefore, there is uncertainty associated with the knowledge "it is safe to cross the road because the light is green", and the agent must recognise that.
-Furthermore, the often conflicting nature of the environment means information obtained from multiple sources (agents) may be inconsistent. Again, this must be taken into consideration when designing an agent which is expected to operate successfully in a potentially conflicting environment.
+Furthermore, the often conflicting nature of the environment means information obtained from multiple sources (agents) may be inconsistent. Again, this must be taken into consideration when designing an agent which is expected to operate successfully in a potentially conflicting environment.
## Further Reading
-* Wooldridge, M. (2009). _An Introduction to MultiAgent Systems_. Wiley, Second edition.
-* Shoham, Y. and Leyton-Brown, K. (2008). _Multiagent Systems: Algorithmic, Game-Theoretic, and Logical Foundations_. Cambridge University Press
-
-
+- Wooldridge, M. (2009). _An Introduction to MultiAgent Systems_. Wiley, Second edition.
+- Shoham, Y. and Leyton-Brown, K. (2008). _Multiagent Systems: Algorithmic, Game-Theoretic, and Logical Foundations_. Cambridge University Press
diff --git a/docs/agent-vs-aea.md b/docs/agent-vs-aea.md
index 16a90e5d37..aaa951ebf3 100644
--- a/docs/agent-vs-aea.md
+++ b/docs/agent-vs-aea.md
@@ -1,12 +1,15 @@
+# AEAs vs Agents
+
AEAs are more than just agents.
-In this guide we show some of the differences in terms of code.
+In this guide, we show some of the differences in terms of code.
The Build an AEA programmatically guide shows how to programmatically build an AEA. We can build an agent of the `Agent` class programmatically as well.
-First, import the python and application specific libraries. (Get the packages directory from the AEA repository `svn export https://github.com/fetchai/agents-aea.git/trunk/packages`.)
+First, import the python and application specific libraries. (Get the `packages` directory from the AEA repository `svn export https://github.com/fetchai/agents-aea.git/trunk/packages`.)
+
``` python
import os
import time
@@ -27,12 +30,12 @@ from packages.fetchai.protocols.default.message import DefaultMessage
Unlike an `AEA`, an `Agent` does not require a `Wallet`, `LedgerApis` or `Resources` module.
However, we need to implement 4 abstract methods:
+
- `setup()`
- `act()`
- `handle_envelope()`
- `teardown()`
-
When we run an agent, `start()` calls `setup()` and then the main agent loop. The main agent loop calls `act()`, `react()` and `update()` on each tick. When the agent is stopped via `stop()` then `teardown()` is called.
Such a lightweight agent can be used to implement simple logic.
@@ -40,6 +43,7 @@ Such a lightweight agent can be used to implement simple logic.
## Code an `Agent`
We define our `Agent` which simply receives envelopes, prints the sender address and `protocol_id` and returns it unopened.
+
``` python
INPUT_FILE = "input_file"
OUTPUT_FILE = "output_file"
@@ -118,8 +122,10 @@ class MyAgent(Agent):
my_agent = MyAgent(identity, [stub_connection])
```
-## Start the agent
+## Start the Agent
+
We run the agent from a different thread so that we can still use the main thread to pass it messages.
+
``` python
# Set the agent running in a different thread
try:
@@ -130,8 +136,10 @@ We run the agent from a different thread so that we can still use the main threa
time.sleep(3)
```
-## Send and receive an envelope
+## Send and Receive an Envelope
+
We use the input and output text files to send an envelope to our agent and receive a response
+
``` python
# Create a message inside an envelope and get the stub connection to pass it into the agent
message_text = b"my_agent,other_agent,fetchai/default:1.0.0,\x12\r\x08\x01*\t*\x07\n\x05hello,"
@@ -148,7 +156,9 @@ We use the input and output text files to send an envelope to our agent and rece
```
## Shutdown
-Finally stop our agent and wait for it to finish
+
+Finally, stop our agent and wait for it to finish
+
``` python
finally:
# Shut down the agent
@@ -156,139 +166,135 @@ Finally stop our agent and wait for it to finish
t.join()
```
-## Your turn
+## Your Turn
Now it is your turn to develop a simple agent with the `Agent` class.
-## Entire code listing
-If you just want to copy and paste the entire script in you can find it here:
-
-Click here to see full listing
-
-
-``` python
-import os
-import time
-from threading import Thread
-from typing import List
-
-from aea.agent import Agent
-from aea.configurations.base import ConnectionConfig
-from aea.connections.base import Connection
-from aea.helpers.file_io import write_with_lock
-from aea.identity.base import Identity
-from aea.mail.base import Envelope
-
-from packages.fetchai.connections.stub.connection import StubConnection
-from packages.fetchai.protocols.default.message import DefaultMessage
-
-
-INPUT_FILE = "input_file"
-OUTPUT_FILE = "output_file"
-
+## Entire Code Listing
-class MyAgent(Agent):
- """A simple agent."""
-
- def __init__(self, identity: Identity, connections: List[Connection]):
- """Initialise the agent."""
- super().__init__(identity, connections)
-
- def setup(self):
- """Setup the agent."""
-
- def act(self):
- """Act implementation."""
- print("Act called for tick {}".format(self.tick))
-
- def handle_envelope(self, envelope: Envelope) -> None:
- """
- Handle envelope.
+If you just want to copy and paste the entire script in you can find it here:
- :param envelope: the envelope received
- :return: None
- """
- print("React called for tick {}".format(self.tick))
- if (
- envelope is not None
- and envelope.protocol_specification_id
- == DefaultMessage.protocol_specification_id
- ):
- sender = envelope.sender
- receiver = envelope.to
- envelope.to = sender
- envelope.sender = receiver
- envelope.message = DefaultMessage.serializer.decode(envelope.message_bytes)
- envelope.message.sender = receiver
- envelope.message.to = sender
- print(
- "Received envelope from {} with protocol_specification_id={}".format(
- sender, envelope.protocol_specification_id
+??? note "Click here to see full listing"
+
+ ``` python
+ import os
+ import time
+ from threading import Thread
+ from typing import List
+
+ from aea.agent import Agent
+ from aea.configurations.base import ConnectionConfig
+ from aea.connections.base import Connection
+ from aea.helpers.file_io import write_with_lock
+ from aea.identity.base import Identity
+ from aea.mail.base import Envelope
+
+ from packages.fetchai.connections.stub.connection import StubConnection
+ from packages.fetchai.protocols.default.message import DefaultMessage
+
+
+ INPUT_FILE = "input_file"
+ OUTPUT_FILE = "output_file"
+
+
+ class MyAgent(Agent):
+ """A simple agent."""
+
+ def __init__(self, identity: Identity, connections: List[Connection]):
+ """Initialise the agent."""
+ super().__init__(identity, connections)
+
+ def setup(self):
+ """Setup the agent."""
+
+ def act(self):
+ """Act implementation."""
+ print("Act called for tick {}".format(self.tick))
+
+ def handle_envelope(self, envelope: Envelope) -> None:
+ """
+ Handle envelope.
+
+ :param envelope: the envelope received
+ :return: None
+ """
+ print("React called for tick {}".format(self.tick))
+ if (
+ envelope is not None
+ and envelope.protocol_specification_id
+ == DefaultMessage.protocol_specification_id
+ ):
+ sender = envelope.sender
+ receiver = envelope.to
+ envelope.to = sender
+ envelope.sender = receiver
+ envelope.message = DefaultMessage.serializer.decode(envelope.message_bytes)
+ envelope.message.sender = receiver
+ envelope.message.to = sender
+ print(
+ "Received envelope from {} with protocol_specification_id={}".format(
+ sender, envelope.protocol_specification_id
+ )
)
- )
- self.outbox.put(envelope)
-
- def teardown(self):
- """Teardown the agent."""
-
-
-def run():
- """Run demo."""
-
- # Ensure the input and output files do not exist initially
- if os.path.isfile(INPUT_FILE):
- os.remove(INPUT_FILE)
- if os.path.isfile(OUTPUT_FILE):
- os.remove(OUTPUT_FILE)
-
- # Create an addresses identity:
- identity = Identity(
- name="my_agent", address="some_address", public_key="public_key"
- )
-
- # Set up the stub connection
- configuration = ConnectionConfig(
- input_file_path=INPUT_FILE,
- output_file_path=OUTPUT_FILE,
- connection_id=StubConnection.connection_id,
- )
- stub_connection = StubConnection(
- configuration=configuration, data_dir=".", identity=identity
- )
-
- # Create our Agent
- my_agent = MyAgent(identity, [stub_connection])
-
- # Set the agent running in a different thread
- try:
- t = Thread(target=my_agent.start)
- t.start()
-
- # Wait for everything to start up
- time.sleep(3)
-
- # Create a message inside an envelope and get the stub connection to pass it into the agent
- message_text = b"my_agent,other_agent,fetchai/default:1.0.0,\x12\r\x08\x01*\t*\x07\n\x05hello,"
-
- with open(INPUT_FILE, "wb") as f:
- write_with_lock(f, message_text)
-
- # Wait for the envelope to get processed
- time.sleep(2)
-
- # Read the output envelope generated by the agent
- with open(OUTPUT_FILE, "rb") as f:
- print("output message: " + f.readline().decode("utf-8"))
- finally:
- # Shut down the agent
- my_agent.stop()
- t.join()
-
-
-if __name__ == "__main__":
- run()
-```
-
-
-
-
+ self.outbox.put(envelope)
+
+ def teardown(self):
+ """Teardown the agent."""
+
+
+ def run():
+ """Run demo."""
+
+ # Ensure the input and output files do not exist initially
+ if os.path.isfile(INPUT_FILE):
+ os.remove(INPUT_FILE)
+ if os.path.isfile(OUTPUT_FILE):
+ os.remove(OUTPUT_FILE)
+
+ # Create an addresses identity:
+ identity = Identity(
+ name="my_agent", address="some_address", public_key="public_key"
+ )
+
+ # Set up the stub connection
+ configuration = ConnectionConfig(
+ input_file_path=INPUT_FILE,
+ output_file_path=OUTPUT_FILE,
+ connection_id=StubConnection.connection_id,
+ )
+ stub_connection = StubConnection(
+ configuration=configuration, data_dir=".", identity=identity
+ )
+
+ # Create our Agent
+ my_agent = MyAgent(identity, [stub_connection])
+
+ # Set the agent running in a different thread
+ try:
+ t = Thread(target=my_agent.start)
+ t.start()
+
+ # Wait for everything to start up
+ time.sleep(3)
+
+ # Create a message inside an envelope and get the stub connection to pass it into the agent
+ message_text = b"my_agent,other_agent,fetchai/default:1.0.0,\x12\r\x08\x01*\t*\x07\n\x05hello,"
+
+ with open(INPUT_FILE, "wb") as f:
+ write_with_lock(f, message_text)
+
+ # Wait for the envelope to get processed
+ time.sleep(2)
+
+ # Read the output envelope generated by the agent
+ with open(OUTPUT_FILE, "rb") as f:
+ print("output message: " + f.readline().decode("utf-8"))
+ finally:
+ # Shut down the agent
+ my_agent.stop()
+ t.join()
+
+
+ if __name__ == "__main__":
+ run()
+ ```
diff --git a/docs/aggregation-demo.md b/docs/aggregation-demo.md
index c5bf1c7b58..873fd9f8c9 100644
--- a/docs/aggregation-demo.md
+++ b/docs/aggregation-demo.md
@@ -1,11 +1,13 @@
+# Aggregation Skill
+
This demo shows how AEAs can aggregate values over the peer-to-peer network.
## Discussion
This demonstration shows how to set up a simple aggregation network in which several AEAs take an average of values fetched from different sources for the same real-world quantity. For this particular example, we take an average of Bitcoin prices from four public APIs.
-## Preparation instructions
-
+## Preparation Instructions
+
### Dependencies
Follow the Preliminaries and Installation sections from the AEA quick start.
@@ -17,6 +19,7 @@ Follow the Preliminaries and Alternatively, create from scratch.
-
-
+ The following steps create **Alice_AEA** from scratch:
+
+ ``` bash
+ aea create aries_alice
+ cd aries_alice
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/http_client:0.24.5
+ aea add connection fetchai/webhook:0.20.5
+ aea add skill fetchai/aries_alice:0.26.5
+ ```
-#### Configure the `aries_alice` skill:
+#### Configure the `aries_alice` Skill
-(configuration file: `alice/vendor/fetchai/skills/aries_alice/skill.yaml`)
+(configuration file: `alice/vendor/fetchai/skills/aries_alice/skill.yaml`)
Ensure `admin_host` and `admin_port` values match with the values you noted above for **Alice_ACA**. You can use the framework's handy `config` CLI command to set these values:
``` bash
aea config set vendor.fetchai.skills.aries_alice.models.strategy.args.admin_host 127.0.0.1
```
+
``` bash
aea config set --type int vendor.fetchai.skills.aries_alice.models.strategy.args.admin_port 8031
```
-#### Configure the `webhook` connection:
+#### Configure the `webhook` Connection
(configuration file: `alice/vendor/fetchai/connections/webhook/connection.yaml`).
@@ -229,7 +228,7 @@ Next, make sure the value of `webhook_url_path` is `/webhooks/topic/{topic}/`.
aea config set vendor.fetchai.connections.webhook.config.webhook_url_path /webhooks/topic/{topic}/
```
-#### Configure the `p2p_libp2p` connection:
+#### Configure the `p2p_libp2p` Connection
``` bash
aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
@@ -242,7 +241,7 @@ aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
}'
```
-### Install the Dependencies and Run Alice_AEA:
+### Install the Dependencies and Run Alice_AEA
Now install all the dependencies:
@@ -251,7 +250,7 @@ aea install
aea build
```
-Finally run **Alice_AEA**:
+Finally, run **Alice_AEA**:
``` bash
aea run
@@ -268,23 +267,20 @@ aea fetch fetchai/aries_faber:0.32.4
cd aries_faber
```
-Alternatively, create from scratch.
-
+??? note "Alternatively, create from scratch:"
+ The following steps create **Faber_AEA** from scratch:
-The following steps create Faber_AEA from scratch:
-``` bash
-aea create aries_faber
-cd aries_faber
-aea add connection fetchai/p2p_libp2p:0.27.4
-aea add connection fetchai/soef:0.27.5
-aea add connection fetchai/http_client:0.24.5
-aea add connection fetchai/webhook:0.20.5
-aea add skill fetchai/aries_faber:0.24.4
-```
-
-
+ ``` bash
+ aea create aries_faber
+ cd aries_faber
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/http_client:0.24.5
+ aea add connection fetchai/webhook:0.20.5
+ aea add skill fetchai/aries_faber:0.24.4
+ ```
-#### Configure the `aries_faber` skill:
+#### Configure the `aries_faber` Skill
(configuration file: `faber/vendor/fetchai/skills/aries_alice/skill.yaml`)
@@ -298,7 +294,7 @@ aea config set vendor.fetchai.skills.aries_faber.models.strategy.args.admin_host
aea config set --type int vendor.fetchai.skills.aries_faber.models.strategy.args.admin_port 8021
```
-#### Configure the `webhook` connection:
+#### Configure the `webhook` Connection
(configuration file: `faber/vendor/fetchai/connections/webhook/connection.yaml`).
@@ -314,7 +310,7 @@ Next, make sure the value of `webhook_url_path` is `/webhooks/topic/{topic}/`.
aea config set vendor.fetchai.connections.webhook.config.webhook_url_path /webhooks/topic/{topic}/
```
-#### Configure the `p2p_libp2p` connection:
+#### Configure the `p2p_libp2p` Connection
``` bash
aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
@@ -329,7 +325,7 @@ aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
where `SOME_ADDRESS` is **Alice_AEA's P2P address** as displayed in the third terminal.
-### Install the Dependencies and Run Faber_AEA:
+### Install the Dependencies and Run Faber_AEA
Now install all the dependencies:
@@ -365,10 +361,10 @@ aea delete aries_faber
aea delete aries_alice
```
-## Further developments
+## Further Developments
In the next update to this demo, the remaining interactions between AEAs and ACAs must be implemented. This means:
-* An instance of Indy ledger must be installed and running. See here for more detail.
-* The commands for running the ACAs need to be adjusted. Additional options relating to a wallet (wallet-name, type, key, storage-type, configuration, credentials) need to be fed to the ACAs as well as the ledger's genesis file so the ACAs can connect to the ledger.
-* The remaining interactions between the AEAs and ACAs as described here need to be implemented.
\ No newline at end of file
+- An instance of Indy ledger must be installed and running. See here for more detail.
+- The commands for running the ACAs need to be adjusted. Additional options relating to a wallet (wallet-name, type, key, storage-type, configuration, credentials) need to be fed to the ACAs as well as the ledger's genesis file so the ACAs can connect to the ledger.
+- The remaining interactions between the AEAs and ACAs as described here need to be implemented.
diff --git a/docs/assets/images/favicon.ico b/docs/assets/images/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..8c5a4d5b3ec784024720f4a537eb5f099ce3b2ad
GIT binary patch
literal 1150
zcmb`H+b)Aq5QVqIog0ZnP-j&|ix4V~Jl+ALyz<%IM>NJy{oGUhVAu}>1mPw8tLv(
z8SC3R(kvN<)=KNl_*ruy-X+EKgv>=-Wv;hPikS(?_IGGpv+N}kvNY7Ac_ArG4$DR&
z{tF{N5SHV7%JSzmzqFW^T&&aLj&d`yGSVw4
-
-``` python
-import os
-import time
-from threading import Thread
-
-from aea_ledger_fetchai import FetchAICrypto
-
-from aea.aea_builder import AEABuilder
-from aea.configurations.base import SkillConfig
-from aea.crypto.helpers import PRIVATE_KEY_PATH_SCHEMA, create_private_key
-from aea.helpers.file_io import write_with_lock
-from aea.skills.base import Skill
-
-
-ROOT_DIR = "./"
-INPUT_FILE = "input_file"
-OUTPUT_FILE = "output_file"
-FETCHAI_PRIVATE_KEY_FILE = PRIVATE_KEY_PATH_SCHEMA.format(FetchAICrypto.identifier)
-
-
-def run():
- """Run demo."""
-
- # Create a private key
- create_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE)
-
- # Ensure the input and output files do not exist initially
- if os.path.isfile(INPUT_FILE):
- os.remove(INPUT_FILE)
- if os.path.isfile(OUTPUT_FILE):
- os.remove(OUTPUT_FILE)
-
- # Instantiate the builder and build the AEA
- # By default, the default protocol, error skill and stub connection are added
- builder = AEABuilder()
-
- builder.set_name("my_aea")
-
- builder.add_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE)
-
- # Add the stub connection (assuming it is present in the local directory 'packages')
- builder.add_connection("./packages/fetchai/connections/stub")
-
- # Add the echo skill (assuming it is present in the local directory 'packages')
- builder.add_skill("./packages/fetchai/skills/echo")
-
- # create skill and handler manually
- from aea.protocols.base import Message
- from aea.skills.base import Handler
-
- from packages.fetchai.protocols.default.message import DefaultMessage
-
- class DummyHandler(Handler):
- """Dummy handler to handle messages."""
-
- SUPPORTED_PROTOCOL = DefaultMessage.protocol_id
-
- def setup(self) -> None:
- """Noop setup."""
-
- def teardown(self) -> None:
- """Noop teardown."""
-
- def handle(self, message: Message) -> None:
- """Handle incoming message."""
- self.context.logger.info("You got a message: {}".format(str(message)))
-
- config = SkillConfig(name="test_skill", author="fetchai")
- skill = Skill(configuration=config)
- dummy_handler = DummyHandler(
- name="dummy_handler", skill_context=skill.skill_context
- )
- skill.handlers.update({dummy_handler.name: dummy_handler})
- builder.add_component_instance(skill)
-
- # Create our AEA
- my_aea = builder.build()
-
- # Set the AEA running in a different thread
- try:
- t = Thread(target=my_aea.start)
- t.start()
-
- # Wait for everything to start up
- time.sleep(4)
-
- # Create a message inside an envelope and get the stub connection to pass it on to the echo skill
- message_text = b"my_aea,other_agent,fetchai/default:1.0.0,\x12\x10\x08\x01\x12\x011*\t*\x07\n\x05hello,"
- with open(INPUT_FILE, "wb") as f:
- write_with_lock(f, message_text)
- print(b"input message: " + message_text)
-
- # Wait for the envelope to get processed
- time.sleep(4)
-
- # Read the output envelope generated by the echo skill
- with open(OUTPUT_FILE, "rb") as f:
- print(b"output message: " + f.readline())
- finally:
- # Shut down the AEA
- my_aea.stop()
- t.join()
- t = None
-
-
-if __name__ == "__main__":
- run()
-```
-
-
-
-
+??? note "Click here to see full listing:"
+
+ ``` python
+ import os
+ import time
+ from threading import Thread
+
+ from aea_ledger_fetchai import FetchAICrypto
+
+ from aea.aea_builder import AEABuilder
+ from aea.configurations.base import SkillConfig
+ from aea.crypto.helpers import PRIVATE_KEY_PATH_SCHEMA, create_private_key
+ from aea.helpers.file_io import write_with_lock
+ from aea.skills.base import Skill
+
+
+ ROOT_DIR = "./"
+ INPUT_FILE = "input_file"
+ OUTPUT_FILE = "output_file"
+ FETCHAI_PRIVATE_KEY_FILE = PRIVATE_KEY_PATH_SCHEMA.format(FetchAICrypto.identifier)
+
+
+ def run():
+ """Run demo."""
+
+ # Create a private key
+ create_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE)
+
+ # Ensure the input and output files do not exist initially
+ if os.path.isfile(INPUT_FILE):
+ os.remove(INPUT_FILE)
+ if os.path.isfile(OUTPUT_FILE):
+ os.remove(OUTPUT_FILE)
+
+ # Instantiate the builder and build the AEA
+ # By default, the default protocol, error skill and stub connection are added
+ builder = AEABuilder()
+
+ builder.set_name("my_aea")
+
+ builder.add_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE)
+
+ # Add the stub connection (assuming it is present in the local directory 'packages')
+ builder.add_connection("./packages/fetchai/connections/stub")
+
+ # Add the echo skill (assuming it is present in the local directory 'packages')
+ builder.add_skill("./packages/fetchai/skills/echo")
+
+ # create skill and handler manually
+ from aea.protocols.base import Message
+ from aea.skills.base import Handler
+
+ from packages.fetchai.protocols.default.message import DefaultMessage
+
+ class DummyHandler(Handler):
+ """Dummy handler to handle messages."""
+
+ SUPPORTED_PROTOCOL = DefaultMessage.protocol_id
+
+ def setup(self) -> None:
+ """Noop setup."""
+
+ def teardown(self) -> None:
+ """Noop teardown."""
+
+ def handle(self, message: Message) -> None:
+ """Handle incoming message."""
+ self.context.logger.info("You got a message: {}".format(str(message)))
+
+ config = SkillConfig(name="test_skill", author="fetchai")
+ skill = Skill(configuration=config)
+ dummy_handler = DummyHandler(
+ name="dummy_handler", skill_context=skill.skill_context
+ )
+ skill.handlers.update({dummy_handler.name: dummy_handler})
+ builder.add_component_instance(skill)
+
+ # Create our AEA
+ my_aea = builder.build()
+
+ # Set the AEA running in a different thread
+ try:
+ t = Thread(target=my_aea.start)
+ t.start()
+
+ # Wait for everything to start up
+ time.sleep(4)
+
+ # Create a message inside an envelope and get the stub connection to pass it on to the echo skill
+ message_text = b"my_aea,other_agent,fetchai/default:1.0.0,\x12\x10\x08\x01\x12\x011*\t*\x07\n\x05hello,"
+ with open(INPUT_FILE, "wb") as f:
+ write_with_lock(f, message_text)
+ print(b"input message: " + message_text)
+
+ # Wait for the envelope to get processed
+ time.sleep(4)
+
+ # Read the output envelope generated by the echo skill
+ with open(OUTPUT_FILE, "rb") as f:
+ print(b"output message: " + f.readline())
+ finally:
+ # Shut down the AEA
+ my_aea.stop()
+ t.join()
+ t = None
+
+
+ if __name__ == "__main__":
+ run()
+ ```
diff --git a/docs/build-aea-step-by-step.md b/docs/build-aea-step-by-step.md
index 52c18d83b2..d7adfe851a 100644
--- a/docs/build-aea-step-by-step.md
+++ b/docs/build-aea-step-by-step.md
@@ -1,14 +1,13 @@
+# Build an AEA with the CLI
Building an AEA step by step (ensure you have followed the Preliminaries and Installation sections from the AEA quick start first):
-
-
Set up your AEA project with the CLI: `aea create my_aea && cd my_aea`
-
Look at, then add the right connections for your use case:
- `aea search connections`, then `aea add connection [public_id]`
-
-
Look for, then add or generate the protocols you require: `aea search protocols`, then `aea add protocol [public_id]` or `aea generate protocol [path_to_specification]`
-
Look for, then add or code the skills you need: `aea search skills`, then `aea add skill [public_id]`. This guide shows you step by step how to develop a skill.
Now, run your AEA: `aea run --connections [public_id]`
-
-See information on the CLI tool here for all the available commands.
+1. Set up your AEA project with the CLI: `aea create my_aea && cd my_aea`
+1. Look at, then add the right connections for your use case:
+ `aea search connections`, then `aea add connection [public_id]`
+1. Look for, then add or generate the protocols you require: `aea search protocols`, then `aea add protocol [public_id]` or `aea generate protocol [path_to_specification]`
+1. Look for, then add or code the skills you need: `aea search skills`, then `aea add skill [public_id]`. This guide shows you step by step how to develop a skill.
+1. Where required, scaffold any of the above resources with the scaffolding tool or generate a protocol with the protocol generator.
+1. Now, run your AEA: `aea run --connections [public_id]`
+
+See information on the CLI tool here for all the available commands.
diff --git a/docs/car-park-skills.md b/docs/car-park-skills.md
index 7fffa1bb1f..8217a90505 100644
--- a/docs/car-park-skills.md
+++ b/docs/car-park-skills.md
@@ -1,8 +1,9 @@
+# Car park skills
The AEA car-park skills demonstrate an interaction between two AEAs.
-* The `carpark_detection` AEA provides information on the number of car parking spaces available in a given vicinity.
-* The `carpark_client` AEA is interested in purchasing information on available car parking spaces in the same vicinity.
+- The `carpark_detection` AEA provides information on the number of car parking spaces available in a given vicinity.
+- The `carpark_client` AEA is interested in purchasing information on available car parking spaces in the same vicinity.
## Discussion
@@ -12,9 +13,10 @@ This demo allows you to test the AEA functionality of the car park AEA demo with
It demonstrates how the AEAs trade car park information.
## Communication
-This diagram shows the communication between the various entities as data is successfully sold by the car park AEA to the client.
-
+This diagram shows the communication between the various entities as data is successfully sold by the car park AEA to the client.
+
+``` mermaid
sequenceDiagram
participant Search
participant Car_Data_Buyer_AEA
@@ -42,18 +44,17 @@ This diagram shows the communication between the various entities as data is suc
deactivate Car_Data_Buyer_AEA
deactivate Car_Park_AEA
deactivate Blockchain
-
-
+```
-## Option 1: AEA Manager approach
+## Option 1: AEA Manager Approach
-Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.
+Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.
-### Preparation instructions
+### Preparation Instructions
Install the AEA Manager.
-### Demo instructions
+### Demo Instructions
The following steps assume you have launched the AEA Manager Desktop app.
@@ -66,151 +67,152 @@ The following steps assume you have launched the AEA Manager Desktop app.
4. Run the `car_detector` AEA. Navigate to its logs and copy the multiaddress displayed.
5. Navigate to the settings of the `car_data_buyer` and under `components > connection >` `fetchai/p2p_libp2p:0.22.0` update as follows (make sure to replace the placeholder with the multiaddress):
-``` bash
-{
- "delegate_uri": "127.0.0.1:11001",
- "entry_peers": ["REPLACE_WITH_MULTI_ADDRESS_HERE"],
- "local_uri": "127.0.0.1:9001",
- "log_file": "libp2p_node.log",
- "public_uri": "127.0.0.1:9001"
-}
-```
+
+ ``` bash
+ {
+ "delegate_uri": "127.0.0.1:11001",
+ "entry_peers": ["REPLACE_WITH_MULTI_ADDRESS_HERE"],
+ "local_uri": "127.0.0.1:9001",
+ "log_file": "libp2p_node.log",
+ "public_uri": "127.0.0.1:9001"
+ }
+ ```
6. Run the `car_data_buyer`.
In the AEA's logs, you should see the agent trading successfully.
-
-## Option 2: CLI approach
+## Option 2: CLI Approach
Follow this approach when using the `aea` CLI.
-### Preparation instructions
+### Preparation Instructions
#### Dependencies
Follow the Preliminaries and Installation sections from the AEA quick start.
-### Demo instructions
+### Demo Instructions
-#### Create car detector AEA
+#### Create Car Detector AEA
First, fetch the car detector AEA:
-``` bash
-aea fetch fetchai/car_detector:0.32.4
-cd car_detector
-aea install
-aea build
-```
-Alternatively, create from scratch.
-
-
-The following steps create the car detector from scratch:
``` bash
-aea create car_detector
+aea fetch fetchai/car_detector:0.32.4
cd car_detector
-aea add connection fetchai/p2p_libp2p:0.27.4
-aea add connection fetchai/soef:0.27.5
-aea add connection fetchai/ledger:0.21.4
-aea add skill fetchai/carpark_detection:0.27.5
-aea config set --type dict agent.dependencies \
-'{
- "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"}
-}'
-aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
-aea config set --type dict agent.default_routing \
-'{
- "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
- "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
-}'
aea install
aea build
```
-
-
-
-#### Create car data buyer AEA
+??? note "Alternatively, create from scratch:"
+
+ The following steps create the car detector from scratch:
+
+ ``` bash
+ aea create car_detector
+ cd car_detector
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/carpark_detection:0.27.5
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
+ }'
+ aea install
+ aea build
+ ```
+
+#### Create Car Data Buyer AEA
Then, fetch the car data client AEA:
-``` bash
-aea fetch fetchai/car_data_buyer:0.33.4
-cd car_data_buyer
-aea install
-aea build
-```
-
-Alternatively, create from scratch.
-
-The following steps create the car data client from scratch:
``` bash
-aea create car_data_buyer
+aea fetch fetchai/car_data_buyer:0.33.4
cd car_data_buyer
-aea add connection fetchai/p2p_libp2p:0.27.4
-aea add connection fetchai/soef:0.27.5
-aea add connection fetchai/ledger:0.21.4
-aea add skill fetchai/carpark_client:0.27.5
-aea config set --type dict agent.dependencies \
-'{
- "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"}
-}'
-aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
-aea config set --type dict agent.default_routing \
-'{
- "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
- "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
-}'
aea install
aea build
```
-
-
-
-
-#### Add keys for the car data seller AEA
+??? note "Alternatively, create from scratch:"
+ The following steps create the car data client from scratch:
+
+ ``` bash
+ aea create car_data_buyer
+ cd car_data_buyer
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/carpark_client:0.27.5
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
+ }'
+ aea install
+ aea build
+ ```
+
+#### Add Keys for the Car Data Seller AEA
First, create the private key for the car data seller AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai `Dorado` use:
+
``` bash
aea generate-key fetchai
aea add-key fetchai fetchai_private_key.txt
```
Next, create a private key used to secure the AEA's communications:
+
``` bash
aea generate-key fetchai fetchai_connection_private_key.txt
aea add-key fetchai fetchai_connection_private_key.txt --connection
```
Finally, certify the key for use by the connections that request that:
+
``` bash
aea issue-certificates
```
-#### Add keys and generate wealth for the car data buyer AEA
+#### Add Keys and Generate Wealth for the Car Data Buyer AEA
The buyer needs to have some wealth to purchase the service from the seller.
First, create the private key for the car data buyer AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai `Dorado` use:
+
``` bash
aea generate-key fetchai
aea add-key fetchai fetchai_private_key.txt
```
Then, create some wealth for your car data buyer based on the network you want to transact with. On the Fetch.ai `Dorado` network:
+
``` bash
aea generate-wealth fetchai
```
Next, create a private key used to secure the AEA's communications:
+
``` bash
aea generate-key fetchai fetchai_connection_private_key.txt
aea add-key fetchai fetchai_connection_private_key.txt --connection
```
Finally, certify the key for use by the connections that request that:
+
``` bash
aea issue-certificates
```
@@ -229,6 +231,7 @@ Once you see a message of the form `To join its network use multiaddr 'SOME_ADDR
This is the entry peer address for the local agent communication network created by the car data seller.
Then, in the car data buyer, run this command (replace `SOME_ADDRESS` with the correct value as described above):
+
``` bash
aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
'{
@@ -239,9 +242,11 @@ aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
"public_uri": "127.0.0.1:9001"
}'
```
+
This allows the car data buyer to connect to the same local agent communication network as the car data seller.
Then run the buyer AEA:
+
``` bash
aea run
```
@@ -251,10 +256,9 @@ You will see that the AEAs negotiate and then transact using the Fetch.ai testne
#### Cleaning up
When you're finished, delete your AEAs:
+
``` bash
cd ..
aea delete car_detector
aea delete car_data_buyer
```
-
-
\ No newline at end of file
diff --git a/docs/cli-commands.md b/docs/cli-commands.md
index 7a698bb936..900968d891 100644
--- a/docs/cli-commands.md
+++ b/docs/cli-commands.md
@@ -1,47 +1,47 @@
-# CLI commands
+# CLI Commands
-| Command | Description |
-| ------------------------------------------- | ---------------------------------------------------------------------------- |
-| `add [package_type] [public_id]` | Add a `package_type` connection, contract, protocol, or skill, with `[public_id]`, to the AEA. `add --local` to add from local `packages` directory. |
-| `add-key [ledger_id] file [--connection]` | Add a private key from a file for `ledger_id`. |
-| `build` | Build the agent and its components. |
-| `config get [path]` | Reads the configuration specified in `path` and prints its target. |
-| `config set [path] [--type TYPE]` | Sets a new value for the target of the `path`. Optionally cast to type. |
-| `create [name]` | Create a new AEA project called `name`. |
-| `delete [name]` | Delete an AEA project. See below for disabling a resource. |
-| `eject [package_type] [public_id]` | Move a package of `package_type` and `package_id` from vendor to project working directory. |
-| `fetch [public_id]` | Fetch an AEA project with `public_id`. `fetch --local` to fetch from local `packages` directory. |
-| `fingerprint [package_type] [public_id]` | Fingerprint connection, contract, protocol, or skill, with `public_id`. |
-| `freeze` | Get all the dependencies needed for the AEA project and its components. |
-| `generate protocol [protocol_spec_path]` | Generate a protocol from the specification. |
-| `generate-key [ledger_id]` | Generate private keys. The AEA uses a private key to derive the associated public key and address. |
-| `generate-wealth [ledger_id]` | Generate wealth for address on test network. |
-| `get-address [ledger_id]` | Get the address associated with the private key. |
-| `get-multiaddress [ledger_id]...` | Get the multiaddress associated with a private key or connection. |
-| `get-public-key [ledger_id]...` | Get the public key associated with a private key of the agent. |
-| `get-wealth [ledger_id]` | Get the wealth associated with the private key. |
-| `init` | Initialize your AEA configurations. (With `--author` to define author.) |
-| `install [-r ]` | Install the dependencies. (With `--install-deps` to install dependencies.) |
-| `interact` | Interact with a running AEA via the stub connection. |
-| `ipfs` | IPFS Commands |
-| `issue-certificates` | Issue the connection certificates. |
-| `launch [path_to_agent_project]...` | Launch many agents at the same time. |
-| `list [package_type]` | List the installed resources. |
-| `local-registry-sync` | Upgrade the local package registry. |
-| `login USERNAME [--password password]` | Login to a registry account with credentials. |
-| `logout` | Logout from registry account. |
-| `publish` | Publish the AEA to registry. Needs to be executed from an AEA project.`publish --local` to publish to local `packages` directory. |
-| `push [package_type] [public_id]` | Push connection, protocol, or skill with `public_id` to registry. `push --local` to push to local `packages` directory. |
-| `register` | Create a new registry account.
-| `remove [package_type] [name]` | Remove connection, protocol, or skill, called `name`, from AEA. |
-| `remove-key [ledger_id] [name]` | Remove a private key registered with id `ledger_id`. |
-| `reset_password EMAIL` | Reset the password of the registry account. |
-| `run {using [connections, ...]}` | Run the AEA on the Fetch.ai network with default or specified connections. |
-| `scaffold [package_type] [name]` | Scaffold a new connection, protocol, or skill called `name`. |
-| `search [package_type]` | Search for components in the registry. `search --local [package_type] [--query searching_query]` to search in local `packages` directory. |
-| `transfer [type] [address] [amount]` | Transfer wealth associated with a private key of the agent to another account. |
-| `upgrade [package_type] [public_id]` | Upgrade the packages of the agent. |
-| `-v DEBUG run` | Run with debugging. |
+| Command | Description |
+|-------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `add [package_type] [public_id]` | Add a `package_type` connection, contract, protocol, or skill, with `[public_id]`, to the AEA. `add --local` to add from local `packages` directory. |
+| `add-key [ledger_id] file [--connection]` | Add a private key from a file for `ledger_id`. |
+| `build` | Build the agent and its components. |
+| `config get [path]` | Reads the configuration specified in `path` and prints its target. |
+| `config set [path] [--type TYPE]` | Sets a new value for the target of the `path`. Optionally cast to type. |
+| `create [name]` | Create a new AEA project called `name`. |
+| `delete [name]` | Delete an AEA project. See below for disabling a resource. |
+| `eject [package_type] [public_id]` | Move a package of `package_type` and `package_id` from vendor to project working directory. |
+| `fetch [public_id]` | Fetch an AEA project with `public_id`. `fetch --local` to fetch from local `packages` directory. |
+| `fingerprint [package_type] [public_id]` | Fingerprint connection, contract, protocol, or skill, with `public_id`. |
+| `freeze` | Get all the dependencies needed for the AEA project and its components. |
+| `generate protocol [protocol_spec_path]` | Generate a protocol from the specification. |
+| `generate-key [ledger_id]` | Generate private keys. The AEA uses a private key to derive the associated public key and address. |
+| `generate-wealth [ledger_id]` | Generate wealth for address on test network. |
+| `get-address [ledger_id]` | Get the address associated with the private key. |
+| `get-multiaddress [ledger_id]...` | Get the multiaddress associated with a private key or connection. |
+| `get-public-key [ledger_id]...` | Get the public key associated with a private key of the agent. |
+| `get-wealth [ledger_id]` | Get the wealth associated with the private key. |
+| `init` | Initialize your AEA configurations. (With `--author` to define author.) |
+| `install [-r ]` | Install the dependencies. (With `--install-deps` to install dependencies.) |
+| `interact` | Interact with a running AEA via the stub connection. |
+| `ipfs` | IPFS Commands |
+| `issue-certificates` | Issue the connection certificates. |
+| `launch [path_to_agent_project]...` | Launch many agents at the same time. |
+| `list [package_type]` | List the installed resources. |
+| `local-registry-sync` | Upgrade the local package registry. |
+| `login USERNAME [--password password]` | Login to a registry account with credentials. |
+| `logout` | Logout from registry account. |
+| `publish` | Publish the AEA to registry. Needs to be executed from an AEA project.`publish --local` to publish to local `packages` directory. |
+| `push [package_type] [public_id]` | Push connection, protocol, or skill with `public_id` to registry. `push --local` to push to local `packages` directory. |
+| `register` | Create a new registry account. |
+| `remove [package_type] [name]` | Remove connection, protocol, or skill, called `name`, from AEA. |
+| `remove-key [ledger_id] [name]` | Remove a private key registered with id `ledger_id`. |
+| `reset_password EMAIL` | Reset the password of the registry account. |
+| `run {using [connections, ...]}` | Run the AEA on the Fetch.ai network with default or specified connections. |
+| `scaffold [package_type] [name]` | Scaffold a new connection, protocol, or skill called `name`. |
+| `search [package_type]` | Search for components in the registry. `search --local [package_type] [--query searching_query]` to search in local `packages` directory. |
+| `transfer [type] [address] [amount]` | Transfer wealth associated with a private key of the agent to another account. |
+| `upgrade [package_type] [public_id]` | Upgrade the packages of the agent. |
+| `-v DEBUG run` | Run with debugging. |
-
-
Tip
-
You can also disable a resource without deleting it by removing the entry from the configuration but leaving the package in the skills namespace.
-
+!!! tip
+ You can also disable a resource without deleting it by removing the entry from the configuration but leaving the package in the `skills` namespace.
-
-
Tip
-
You can skip the consistency checks on the AEA project by using the flag --skip-consistency-check. E.g. aea --skip-consistency-check run will bypass the fingerprint checks.
-
-
-
+!!! tip
+ You can skip the consistency checks on the AEA project by using the flag `--skip-consistency-check`. E.g. `aea --skip-consistency-check run` will bypass the fingerprint checks.
diff --git a/docs/cli-how-to.md b/docs/cli-how-to.md
index ea990b74ac..5d19bcd798 100644
--- a/docs/cli-how-to.md
+++ b/docs/cli-how-to.md
@@ -1,3 +1,5 @@
+# How to Use the Command Line Interface
+
The command line interface is the easiest way to build an AEA.
## Installation
@@ -14,28 +16,35 @@ The following installs the entire AEA package including the CLI.
pip install aea[all]
```
-If you are using `zsh` rather than `bash` type
+If you are using `zsh` rather than `bash` type
+
``` zsh
pip install 'aea[cli]'
```
+
and
+
``` zsh
pip install 'aea[all]'
```
+
respectively.
Be sure that the `bin` folder of your Python environment
is in the `PATH` variable. If so, you can execute the CLI tool as:
+
``` bash
aea
```
You might find useful the execution of the `aea.cli` package
as a script:
+
``` bash
python -m aea.cli
```
-which is just an alternative entry-point to the CLI tool.
+
+which is just an alternative entry-point to the CLI tool.
## Troubleshooting
@@ -46,8 +55,7 @@ pip install aea[all] --force --no-cache-dir
```
And for `zsh` run:
+
``` zsh
pip install 'aea[all]' --force --no-cache-dir
```
-
-
diff --git a/docs/cli-vs-programmatic-aeas.md b/docs/cli-vs-programmatic-aeas.md
index 1779944f73..3b23cd4416 100644
--- a/docs/cli-vs-programmatic-aeas.md
+++ b/docs/cli-vs-programmatic-aeas.md
@@ -1,3 +1,5 @@
+# CLI vs Programmatic AEAs
+
The AEA framework enables us to create agents either from the CLI tool or programmatically.
The following demo demonstrates an interaction between two AEAs.
@@ -11,24 +13,25 @@ The scope of the specific demo is to demonstrate how a CLI based AEA can interac
to achieve this we are going to use the weather station skills.
This demo does not utilize a smart contract or a ledger interaction.
-## Get required packages
+## Get Required Packages
-Copy the packages directory into your local working directory:
+Copy the `packages` directory into your local working directory:
``` bash
svn export https://github.com/fetchai/agents-aea.git/trunk/packages
```
Also, install `aea-ledger-fetchai` plug-in:
-```bash
+
+``` bash
pip install aea-ledger-fetchai
```
-## Demo instructions
+## Demo Instructions
If you want to create the weather station AEA step by step you can follow this guide here
-### Create the weather station AEA
+### Create the Weather Station AEA
Fetch the weather station AEA with the following command :
@@ -39,285 +42,291 @@ aea install
aea build
```
-### Update the AEA configurations
+### Update the AEA Configurations
In the terminal change the configuration:
+
``` bash
aea config set vendor.fetchai.skills.weather_station.models.strategy.args.is_ledger_tx False --type bool
```
+
The `is_ledger_tx` will prevent the AEA to communicate with a ledger.
-### Add keys
+### Add Keys
Add a private key for the weather station.
+
``` bash
aea generate-key fetchai
aea add-key fetchai fetchai_private_key.txt
```
Next, create a private key used to secure the AEA's communications:
+
``` bash
aea generate-key fetchai fetchai_connection_private_key.txt
aea add-key fetchai fetchai_connection_private_key.txt --connection
```
Finally, certify the key for use by the connections that request that:
+
``` bash
aea issue-certificates
```
-### Run the weather station AEA
+### Run the Weather Station AEA
+
``` bash
aea run
```
Once you see a message of the form `To join its network use multiaddr: ['SOME_ADDRESS']` take note of the address.
-### Create the weather client AEA
+### Create the Weather Client AEA
Since we want to show the interaction between a programmatically created AEA with a CLI based AEA we are going to write some code for the client.
Create a new python file and name it `weather_client.py` and add the following code
-Weather client full code.
-
-``` python
-import logging
-import os
-import sys
-from typing import cast
-
-from aea_ledger_fetchai import FetchAICrypto
-
-from aea.aea import AEA
-from aea.aea_builder import AEABuilder
-from aea.configurations.base import ConnectionConfig
-from aea.crypto.helpers import (
- PRIVATE_KEY_PATH_SCHEMA,
- create_private_key,
- make_certificate,
-)
-from aea.crypto.wallet import Wallet
-from aea.helpers.base import CertRequest
-from aea.identity.base import Identity
-from aea.protocols.base import Protocol
-from aea.registries.resources import Resources
-from aea.skills.base import Skill
-
-import packages.fetchai.connections.p2p_libp2p.connection
-from packages.fetchai.connections.ledger.connection import LedgerConnection
-from packages.fetchai.connections.p2p_libp2p.connection import P2PLibp2pConnection
-from packages.fetchai.connections.soef.connection import SOEFConnection
-from packages.fetchai.protocols.ledger_api.message import LedgerApiMessage
-from packages.fetchai.protocols.oef_search.message import OefSearchMessage
-from packages.fetchai.skills.weather_client.strategy import Strategy
-
-
-API_KEY = "TwiCIriSl0mLahw17pyqoA"
-SOEF_ADDR = "s-oef.fetch.ai"
-SOEF_PORT = 443
-ENTRY_PEER_ADDRESS = (
- "/dns4/127.0.0.1/tcp/9000/p2p/16Uiu2HAmLBCAqHL8SuFosyDhAKYsLKXBZBWXBsB9oFw2qU4Kckun"
-)
-FETCHAI_PRIVATE_KEY_FILE = PRIVATE_KEY_PATH_SCHEMA.format(FetchAICrypto.identifier)
-FETCHAI_PRIVATE_KEY_FILE_CONNECTION = PRIVATE_KEY_PATH_SCHEMA.format(
- "fetchai_connection"
-)
-ROOT_DIR = os.getcwd()
-
-logger = logging.getLogger("aea")
-logging.basicConfig(stream=sys.stdout, level=logging.INFO)
-
-
-def run():
- """Run demo."""
-
- # Create a private key
- create_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE)
- create_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE_CONNECTION)
-
- # Set up the wallet, identity and (empty) resources
- wallet = Wallet(
- private_key_paths={FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE},
- connection_private_key_paths={
- FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_CONNECTION
- },
+??? note "Weather client full code:"
+
+ ``` python
+ import logging
+ import os
+ import sys
+ from typing import cast
+
+ from aea_ledger_fetchai import FetchAICrypto
+
+ from aea.aea import AEA
+ from aea.aea_builder import AEABuilder
+ from aea.configurations.base import ConnectionConfig
+ from aea.crypto.helpers import (
+ PRIVATE_KEY_PATH_SCHEMA,
+ create_private_key,
+ make_certificate,
)
- identity = Identity(
- "my_aea",
- address=wallet.addresses.get(FetchAICrypto.identifier),
- public_key=wallet.public_keys.get(FetchAICrypto.identifier),
+ from aea.crypto.wallet import Wallet
+ from aea.helpers.base import CertRequest
+ from aea.identity.base import Identity
+ from aea.protocols.base import Protocol
+ from aea.registries.resources import Resources
+ from aea.skills.base import Skill
+
+ import packages.fetchai.connections.p2p_libp2p.connection
+ from packages.fetchai.connections.ledger.connection import LedgerConnection
+ from packages.fetchai.connections.p2p_libp2p.connection import P2PLibp2pConnection
+ from packages.fetchai.connections.soef.connection import SOEFConnection
+ from packages.fetchai.protocols.ledger_api.message import LedgerApiMessage
+ from packages.fetchai.protocols.oef_search.message import OefSearchMessage
+ from packages.fetchai.skills.weather_client.strategy import Strategy
+
+
+ API_KEY = "TwiCIriSl0mLahw17pyqoA"
+ SOEF_ADDR = "s-oef.fetch.ai"
+ SOEF_PORT = 443
+ ENTRY_PEER_ADDRESS = (
+ "/dns4/127.0.0.1/tcp/9000/p2p/16Uiu2HAmLBCAqHL8SuFosyDhAKYsLKXBZBWXBsB9oFw2qU4Kckun"
)
- resources = Resources()
- data_dir = os.getcwd()
-
- # specify the default routing for some protocols
- default_routing = {
- LedgerApiMessage.protocol_id: LedgerConnection.connection_id,
- OefSearchMessage.protocol_id: SOEFConnection.connection_id,
- }
- default_connection = P2PLibp2pConnection.connection_id
-
- state_update_protocol = Protocol.from_dir(
- os.path.join(os.getcwd(), "packages", "fetchai", "protocols", "state_update")
- )
- resources.add_protocol(state_update_protocol)
-
- # Add the default protocol (which is part of the AEA distribution)
- default_protocol = Protocol.from_dir(
- os.path.join(os.getcwd(), "packages", "fetchai", "protocols", "default")
+ FETCHAI_PRIVATE_KEY_FILE = PRIVATE_KEY_PATH_SCHEMA.format(FetchAICrypto.identifier)
+ FETCHAI_PRIVATE_KEY_FILE_CONNECTION = PRIVATE_KEY_PATH_SCHEMA.format(
+ "fetchai_connection"
)
- resources.add_protocol(default_protocol)
-
- # Add the signing protocol (which is part of the AEA distribution)
- signing_protocol = Protocol.from_dir(
- os.path.join(os.getcwd(), "packages", "fetchai", "protocols", "signing")
- )
- resources.add_protocol(signing_protocol)
-
- # Add the ledger_api protocol
- ledger_api_protocol = Protocol.from_dir(
- os.path.join(
- os.getcwd(),
- "packages",
- "fetchai",
- "protocols",
- "ledger_api",
+ ROOT_DIR = os.getcwd()
+
+ logger = logging.getLogger("aea")
+ logging.basicConfig(stream=sys.stdout, level=logging.INFO)
+
+
+ def run():
+ """Run demo."""
+
+ # Create a private key
+ create_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE)
+ create_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE_CONNECTION)
+
+ # Set up the wallet, identity and (empty) resources
+ wallet = Wallet(
+ private_key_paths={FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE},
+ connection_private_key_paths={
+ FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_CONNECTION
+ },
)
- )
- resources.add_protocol(ledger_api_protocol)
-
- # Add the oef_search protocol
- oef_protocol = Protocol.from_dir(
- os.path.join(
- os.getcwd(),
- "packages",
- "fetchai",
- "protocols",
- "oef_search",
+ identity = Identity(
+ "my_aea",
+ address=wallet.addresses.get(FetchAICrypto.identifier),
+ public_key=wallet.public_keys.get(FetchAICrypto.identifier),
)
- )
- resources.add_protocol(oef_protocol)
-
- # Add the fipa protocol
- fipa_protocol = Protocol.from_dir(
- os.path.join(
- os.getcwd(),
- "packages",
- "fetchai",
- "protocols",
- "fipa",
+ resources = Resources()
+ data_dir = os.getcwd()
+
+ # specify the default routing for some protocols
+ default_routing = {
+ LedgerApiMessage.protocol_id: LedgerConnection.connection_id,
+ OefSearchMessage.protocol_id: SOEFConnection.connection_id,
+ }
+ default_connection = P2PLibp2pConnection.connection_id
+
+ state_update_protocol = Protocol.from_dir(
+ os.path.join(os.getcwd(), "packages", "fetchai", "protocols", "state_update")
)
- )
- resources.add_protocol(fipa_protocol)
-
- # Add the LedgerAPI connection
- configuration = ConnectionConfig(connection_id=LedgerConnection.connection_id)
- ledger_api_connection = LedgerConnection(
- configuration=configuration, data_dir=data_dir, identity=identity
- )
- resources.add_connection(ledger_api_connection)
-
- # Add the P2P connection
- cert_path = ".certs/conn_cert.txt"
- cert_request = CertRequest(
- identifier="acn",
- ledger_id=FetchAICrypto.identifier,
- not_after="2022-01-01",
- not_before="2021-01-01",
- public_key="fetchai",
- message_format="{public_key}",
- save_path=cert_path,
- )
- public_key = wallet.connection_cryptos.public_keys.get(FetchAICrypto.identifier)
- message = cert_request.get_message(public_key)
- make_certificate(
- FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE, message, cert_path
- )
- configuration = ConnectionConfig(
- connection_id=P2PLibp2pConnection.connection_id,
- delegate_uri="127.0.0.1:11001",
- entry_peers=[ENTRY_PEER_ADDRESS],
- local_uri="127.0.0.1:9001",
- log_file="libp2p_node.log",
- public_uri="127.0.0.1:9001",
- build_directory=os.getcwd(),
- build_entrypoint="check_dependencies.py",
- cert_requests=[cert_request],
- )
- configuration.directory = os.path.dirname(
- packages.fetchai.connections.p2p_libp2p.connection.__file__
- )
-
- AEABuilder.run_build_for_component_configuration(configuration)
-
- p2p_connection = P2PLibp2pConnection(
- configuration=configuration,
- data_dir=data_dir,
- identity=identity,
- crypto_store=wallet.connection_cryptos,
- )
- resources.add_connection(p2p_connection)
-
- # Add the SOEF connection
- configuration = ConnectionConfig(
- api_key=API_KEY,
- soef_addr=SOEF_ADDR,
- soef_port=SOEF_PORT,
- restricted_to_protocols={OefSearchMessage.protocol_id},
- connection_id=SOEFConnection.connection_id,
- )
- soef_connection = SOEFConnection(
- configuration=configuration, data_dir=data_dir, identity=identity
- )
- resources.add_connection(soef_connection)
-
- # create the AEA
- my_aea = AEA(
- identity,
- wallet,
- resources,
- data_dir,
- default_connection=default_connection,
- default_routing=default_routing,
- )
- # Add the error and weather_client skills
- error_skill = Skill.from_dir(
- os.path.join(ROOT_DIR, "packages", "fetchai", "skills", "error"),
- agent_context=my_aea.context,
- )
- weather_skill = Skill.from_dir(
- os.path.join(ROOT_DIR, "packages", "fetchai", "skills", "weather_client"),
- agent_context=my_aea.context,
- )
-
- strategy = cast(Strategy, weather_skill.models.get("strategy"))
- strategy._is_ledger_tx = False
-
- for skill in [error_skill, weather_skill]:
- resources.add_skill(skill)
-
- # Run the AEA
- try:
- logger.info("STARTING AEA NOW!")
- my_aea.start()
- except KeyboardInterrupt:
- logger.info("STOPPING AEA NOW!")
- my_aea.stop()
-
-
-if __name__ == "__main__":
- run()
-```
-
+ resources.add_protocol(state_update_protocol)
+
+ # Add the default protocol (which is part of the AEA distribution)
+ default_protocol = Protocol.from_dir(
+ os.path.join(os.getcwd(), "packages", "fetchai", "protocols", "default")
+ )
+ resources.add_protocol(default_protocol)
+
+ # Add the signing protocol (which is part of the AEA distribution)
+ signing_protocol = Protocol.from_dir(
+ os.path.join(os.getcwd(), "packages", "fetchai", "protocols", "signing")
+ )
+ resources.add_protocol(signing_protocol)
+
+ # Add the ledger_api protocol
+ ledger_api_protocol = Protocol.from_dir(
+ os.path.join(
+ os.getcwd(),
+ "packages",
+ "fetchai",
+ "protocols",
+ "ledger_api",
+ )
+ )
+ resources.add_protocol(ledger_api_protocol)
+
+ # Add the oef_search protocol
+ oef_protocol = Protocol.from_dir(
+ os.path.join(
+ os.getcwd(),
+ "packages",
+ "fetchai",
+ "protocols",
+ "oef_search",
+ )
+ )
+ resources.add_protocol(oef_protocol)
+
+ # Add the fipa protocol
+ fipa_protocol = Protocol.from_dir(
+ os.path.join(
+ os.getcwd(),
+ "packages",
+ "fetchai",
+ "protocols",
+ "fipa",
+ )
+ )
+ resources.add_protocol(fipa_protocol)
+
+ # Add the LedgerAPI connection
+ configuration = ConnectionConfig(connection_id=LedgerConnection.connection_id)
+ ledger_api_connection = LedgerConnection(
+ configuration=configuration, data_dir=data_dir, identity=identity
+ )
+ resources.add_connection(ledger_api_connection)
+
+ # Add the P2P connection
+ cert_path = ".certs/conn_cert.txt"
+ cert_request = CertRequest(
+ identifier="acn",
+ ledger_id=FetchAICrypto.identifier,
+ not_after="2022-01-01",
+ not_before="2021-01-01",
+ public_key="fetchai",
+ message_format="{public_key}",
+ save_path=cert_path,
+ )
+ public_key = wallet.connection_cryptos.public_keys.get(FetchAICrypto.identifier)
+ message = cert_request.get_message(public_key)
+ make_certificate(
+ FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE, message, cert_path
+ )
+ configuration = ConnectionConfig(
+ connection_id=P2PLibp2pConnection.connection_id,
+ delegate_uri="127.0.0.1:11001",
+ entry_peers=[ENTRY_PEER_ADDRESS],
+ local_uri="127.0.0.1:9001",
+ log_file="libp2p_node.log",
+ public_uri="127.0.0.1:9001",
+ build_directory=os.getcwd(),
+ build_entrypoint="check_dependencies.py",
+ cert_requests=[cert_request],
+ )
+ configuration.directory = os.path.dirname(
+ packages.fetchai.connections.p2p_libp2p.connection.__file__
+ )
+
+ AEABuilder.run_build_for_component_configuration(configuration)
+
+ p2p_connection = P2PLibp2pConnection(
+ configuration=configuration,
+ data_dir=data_dir,
+ identity=identity,
+ crypto_store=wallet.connection_cryptos,
+ )
+ resources.add_connection(p2p_connection)
+
+ # Add the SOEF connection
+ configuration = ConnectionConfig(
+ api_key=API_KEY,
+ soef_addr=SOEF_ADDR,
+ soef_port=SOEF_PORT,
+ restricted_to_protocols={OefSearchMessage.protocol_id},
+ connection_id=SOEFConnection.connection_id,
+ )
+ soef_connection = SOEFConnection(
+ configuration=configuration, data_dir=data_dir, identity=identity
+ )
+ resources.add_connection(soef_connection)
+
+ # create the AEA
+ my_aea = AEA(
+ identity,
+ wallet,
+ resources,
+ data_dir,
+ default_connection=default_connection,
+ default_routing=default_routing,
+ )
+ # Add the error and weather_client skills
+ error_skill = Skill.from_dir(
+ os.path.join(ROOT_DIR, "packages", "fetchai", "skills", "error"),
+ agent_context=my_aea.context,
+ )
+ weather_skill = Skill.from_dir(
+ os.path.join(ROOT_DIR, "packages", "fetchai", "skills", "weather_client"),
+ agent_context=my_aea.context,
+ )
+
+ strategy = cast(Strategy, weather_skill.models.get("strategy"))
+ strategy._is_ledger_tx = False
+
+ for skill in [error_skill, weather_skill]:
+ resources.add_skill(skill)
+
+ # Run the AEA
+ try:
+ logger.info("STARTING AEA NOW!")
+ my_aea.start()
+ except KeyboardInterrupt:
+ logger.info("STOPPING AEA NOW!")
+ my_aea.stop()
+
+
+ if __name__ == "__main__":
+ run()
+ ```
Now replace `ENTRY_PEER_ADDRESS` with the peer address (`SOME_ADDRESS`) noted above.
For more details on how to create an agent programmatically follow this guide here.
-### Run the weather station AEA
+### Run the Weather Client AEA
In a new terminal window, navigate to the folder that you created the script and run:
+
``` bash
python weather_client.py
```
diff --git a/docs/config.md b/docs/config.md
index 6b53437969..294ee1abe8 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -1,8 +1,11 @@
+# Configurations
+
This document describes the configuration files of the different packages.
-## AEA configuration YAML
+## AEA Configuration YAML
The following provides a list of the relevant regex used:
+
``` yaml
PACKAGE_REGEX: "[a-zA-Z_][a-zA-Z0-9_]*"
AUTHOR_REGEX: "[a-zA-Z_][a-zA-Z0-9_]*"
@@ -11,6 +14,7 @@ LEDGER_ID_REGEX: "^[^\\d\\W]\\w*\\Z"
```
The `aea-config.yaml` defines the AEA project. The compulsory components are listed below:
+
``` yaml
agent_name: my_agent # Name of the AEA project (must satisfy PACKAGE_REGEX)
author: fetchai # Author handle of the project's author (must satisfy AUTHOR_REGEX)
@@ -42,6 +46,7 @@ dependencies: {} # The python dependencies the AE
```
The `aea-config.yaml` can be extended with a number of optional fields:
+
``` yaml
period: 0.05 # The period to call agent's act
execution_timeout: 0 # The execution time limit on each call to `react` and `act` (0 disables the feature)
@@ -60,6 +65,7 @@ data_dir: None # The path to the directory for
The `aea-config.yaml` can further be extended with component configuration overrides.
For custom connection configurations:
+
``` yaml
public_id: some_author/some_package:0.1.0 # The public id of the connection (must satisfy PUBLIC_ID_REGEX).
type: connection # for connections, this must be "connection".
@@ -67,6 +73,7 @@ config: ... # a dictionary to overwrite the
```
For custom skill configurations:
+
``` yaml
public_id: some_author/some_package:0.1.0 # The public id of the connection (must satisfy PUBLIC_ID_REGEX).
type: skill # for skills, this must be "skill".
@@ -84,10 +91,10 @@ models: # override configurations for mo
foo: bar
```
-
-## Connection configuration YAML
+## Connection Configuration YAML
The `connection.yaml`, which is present in each connection package, has the following required fields:
+
``` yaml
name: scaffold # Name of the package (must satisfy PACKAGE_REGEX)
author: fetchai # Author handle of the package's author (must satisfy AUTHOR_REGEX)
@@ -111,9 +118,10 @@ dependencies: {} # The python dependencies the pa
is_abstract: false # An optional boolean that if `true` makes the connection
```
-## Contract configuration YAML
+## Contract Configuration YAML
The `contract.yaml`, which is present in each contract package, has the following required fields:
+
``` yaml
name: scaffold # Name of the package (must satisfy PACKAGE_REGEX)
author: fetchai # Author handle of the package's author (must satisfy AUTHOR_REGEX)
@@ -133,9 +141,10 @@ config: # A dictionary containing the kw
dependencies: {} # The python dependencies the package relies on. They will be installed when `aea install` is run.
```
-## Protocol configuration YAML
+## Protocol Configuration YAML
The `protocol.yaml`, which is present in each protocol package, has the following required fields:
+
``` yaml
name: scaffold # Name of the package (must satisfy PACKAGE_REGEX)
author: fetchai # Author handle of the package's author (must satisfy AUTHOR_REGEX)
@@ -152,9 +161,10 @@ fingerprint_ignore_patterns: [] # Ignore pattern for the fingerp
dependencies: {} # The python dependencies the package relies on. They will be installed when `aea install` is run.
```
-## Skill configuration YAML
+## Skill Configuration YAML
The `skill.yaml`, which is present in each protocol package, has the following required fields:
+
``` yaml
name: scaffold # Name of the package (must satisfy PACKAGE_REGEX)
author: fetchai # Author handle of the package's author (must satisfy AUTHOR_REGEX)
diff --git a/docs/connect-a-frontend.md b/docs/connect-a-frontend.md
index 8f151af39f..e0a7778a7b 100644
--- a/docs/connect-a-frontend.md
+++ b/docs/connect-a-frontend.md
@@ -1,9 +1,13 @@
+# Front-End Integration
+
This page lays out two options for connecting a front-end to an AEA. The following diagram illustrates these two options.
## Case 1
+
The first option is to create a `HTTP Server` connection that handles incoming requests from a REST API. In this scenario, the REST API communicates with the AEA and requests are handled by the `HTTP Server` connection package. The REST API should send CRUD requests to the `HTTP Server` connection (`fetchai/http_server:0.23.5`) which translates these into Envelopes to be consumed by the correct skill.
## Case 2
-The second option is to create a front-end comprising a stand-alone `Multiplexer` with a `P2P` connection (`fetchai/p2p_libp2p:0.27.4`). In this scenario the Agent Communication Network can be used to send Envelopes from the AEA to the front-end.
\ No newline at end of file
+
+The second option is to create a front-end comprising a stand-alone `Multiplexer` with a `P2P` connection (`fetchai/p2p_libp2p:0.27.4`). In this scenario the Agent Communication Network can be used to send Envelopes from the AEA to the front-end.
diff --git a/docs/connection.md b/docs/connection.md
index 3d612afacb..e81f17f59d 100644
--- a/docs/connection.md
+++ b/docs/connection.md
@@ -1,3 +1,5 @@
+# Connections
+
A `Connection` provides an interface for the agent to connect with entities in the outside world. Connections wrap SDKs or APIs and provide interfaces to networks, ledgers and other services. As such, a connection is concerned with I/O bound and continuously connected operations. Where necessary, a connection is responsible for translating between the framework specific protocol (an `Envelope` with its contained `Message`) and the external service or third-party protocol (e.g. `HTTP`). Hence, there are two roles for connections: wrapper and transport connection. The transport connection is responsible to delivering AEA envelopes.
The messages constructed or received by a connection are eventually processed by one or several skills which deal with handling and generating messages related to a specific business objective.
@@ -8,7 +10,7 @@ An `AEA` can interact with multiple connections at the same time via the `InBox` and `OutBox`, which are, respectively, queues for incoming and outgoing envelopes and their contained messages.
-## Developing your connection
+## Developing your Connection
The easiest way to get started developing your own connection is by using the scaffold command:
@@ -18,13 +20,13 @@ aea scaffold connection my_new_connection
This will scaffold a connection package called `my_new_connection` with three files:
-* `__init__.py`
-* `connection.py` containing the scaffolded connection class
-* `connection.yaml` containing the scaffolded configuration file
+- `__init__.py`
+- `connection.py` containing the scaffolded connection class
+- `connection.yaml` containing the scaffolded configuration file
As a developer you have the choice between implementing a sync or asynchronous interface. The scaffolded `connection.py` file contains two classes: the `MyScaffoldAsyncConnection` inherited from the `Connection` base class and the `MyScaffoldSyncConnection` inherited from the `BaseSyncConnection`. Remove the unused class.
-### Primary methods to develop - asynchronous connection interface
+### Primary Methods to Develop - Asynchronous Connection Interface
The developer needs to implement four public coroutines:
@@ -38,14 +40,15 @@ The developer needs to implement four public coroutines:
The framework provides a demo `stub` connection which implements an I/O reader and writer to send and receive messages between the agent and a local file. To gain inspiration and become familiar with the structure of connection packages, you may find it useful to check out `fetchai/stub:0.21.2`, `fetchai/http_server:0.23.5` or `fetchai/http_client:0.24.5` connections. The latter two connections are for external clients to connect with an agent, and for the agent to connect with external servers, respectively.
-### Primary methods to develop - sync connection interface
+### Primary Methods to Develop - Sync Connection Interface
The `BaseSyncConnection` uses executors to execute synchronous code from the asynchronous context of the `Multiplexer` in executors/threads, which are limited by the amount of configured workers.
The asynchronous methods `connect`, `disconnect` and `send` are converted to callbacks which the developer implements:
-* `on_connect`
-* `on_disconnect`
-* `on_send`
+
+- `on_connect`
+- `on_disconnect`
+- `on_send`
All of these methods will be executed in the executor pool.
@@ -57,7 +60,7 @@ The `receive` coroutine has no direct equivalent. Instead, the developer impleme
Every connection must have a configuration file in `connection.yaml`, containing meta-information about the connection as well as all the required configuration details. For more details, have a look here.
-### Configuration options
+### Configuration Options
The `connection.yaml` file contains a number of fields that must be edited by the developer of the connection:
@@ -83,5 +86,3 @@ cert_requests: []
- `dependencies` lists any Python dependencies of the connection package
- `is_abstract` specifies whether this connection is only used as an abstract base class
- `cert_requests` lists certification requests of the connection (see proof of representation for details)
-
-
diff --git a/docs/contract.md b/docs/contract.md
index 327941a55d..d4858fba1e 100644
--- a/docs/contract.md
+++ b/docs/contract.md
@@ -1,3 +1,5 @@
+# Contracts
+
`Contracts` wrap smart contracts for Fetch.ai and third-party decentralized ledgers. In particular, they provide wrappers around the API or ABI of a smart contract and its byte code. They implement a translation between framework messages (in the `fetchai/contract_api:1.0.0` protocol) and the implementation specifics of the ABI.
Contracts usually implement four types of methods:
@@ -9,10 +11,9 @@ Contracts usually implement four types of methods:
Contracts can be added as packages which means they become reusable across AEA projects.
-The smart contract wrapped in a AEA contract package might be a third-party smart contract or your own smart contract potentially interacting with a third-party contract on-chain.
-
+The smart contract wrapped in an AEA contract package might be a third-party smart contract or your own smart contract potentially interacting with a third-party contract on-chain.
-## Interacting with contracts from skills
+## Interacting with Contracts from Skills
Interacting with contracts in almost all cases requires network access. Therefore, the framework executes contract related logic in a Connection.
@@ -20,7 +21,7 @@ Interacting with contracts in almost all cases requires network access. Therefor
In particular, the `fetchai/ledger:0.21.4` connection can be used to execute contract related logic. The skills communicate with the `fetchai/ledger:0.21.4` connection via the `fetchai/contract_api:1.0.0` protocol. This protocol implements a request-response pattern to serve the four types of methods listed above:
-- the `get_deploy_transaction` message is used to request a deploy transaction for a specific contract. For instance, to request a deploy transaction for the deployment of the smart contract wrapped in the `fetchai/erc1155:0.23.2` package, we send the following message to the `fetchai/ledger:0.21.4`:
+- the `get_deploy_transaction` message is used to request a `deploy` transaction for a specific contract. For instance, to request a `deploy` transaction for the deployment of the smart contract wrapped in the `fetchai/erc1155:0.23.2` package, we send the following message to the `fetchai/ledger:0.21.4`:
``` python
contract_api_msg = ContractApiMessage(
@@ -39,11 +40,13 @@ Any additional arguments needed by the contract's constructor method should be a
This message will be handled by the `fetchai/ledger:0.21.4` connection and then a `raw_transaction` message will be returned with the matching raw transaction. To send this transaction to the ledger for processing, we first sign the message with the decision maker and then send the signed transaction to the `fetchai/ledger:0.21.4` connection using the `fetchai/ledger_api:1.0.0` protocol. For details on how to implement the message handling, see the handlers in the `erc1155_deploy` skill.
-
-
CosmWasm based smart contract deployments
-
When using CosmWasm based smart contracts two types of deployment transactions exist. The first transaction stores the code on the chain. The second transaction initialises the code. This way, the same contract code can be initialised many times. Both the store and init messages use the ContractApiMessage.Performative.GET_DEPLOY_TRANSACTION performative. The ledger API automatically detects the type of transactions based on the provided keyword arguments. In particular, an init transaction requires the keyword arguments code_id (integer), label (string), amount (integer) and init_msg (JSON). For an example look at the fetchai/erc1155:0.23.2 package.
-
-
+!!! note "CosmWasm based smart contract deployments"
+
+ When using CosmWasm based smart contracts two types of deployment transactions exist. The first transaction stores the code on the chain. The second transaction initialises the code. This way, the same contract code can be initialised many times.
+
+ Both the store and init messages use the ContractApiMessage.Performative.GET_DEPLOY_TRANSACTION performative. The ledger API automatically detects the type of transactions based on the provided keyword arguments. In particular, an init transaction requires the keyword arguments code_id (integer), label (string), amount (integer) and init_msg (JSON).
+
+ For an example look at the fetchai/erc1155:0.23.2 package.
- the `get_raw_transaction` message is used to request any transaction for a specific contract which changes state in the contract. For instance, to request a transaction for the creation of token in the deployed `erc1155` smart contract wrapped in the `fetchai/erc1155:0.23.2` package, we send the following message to the `fetchai/ledger:0.21.4`:
@@ -64,7 +67,7 @@ contract_api_msg = ContractApiMessage(
)
```
-This message will be handled by the `fetchai/ledger:0.21.4` connection and then a `raw_transaction` message will be returned with the matching raw transaction. For this to be executed correctly, the `fetchai/erc1155:0.23.2` contract package needs to implement the `get_create_batch_transaction` method with the specified key word arguments (see example in *Deploy your own*, below). Similarly to above, to send this transaction to the ledger for processing, we first sign the message with the decision maker and then send the signed transaction to the `fetchai/ledger:0.21.4` connection using the `fetchai/ledger_api:1.0.0` protocol.
+This message will be handled by the `fetchai/ledger:0.21.4` connection and then a `raw_transaction` message will be returned with the matching raw transaction. For this to be executed correctly, the `fetchai/erc1155:0.23.2` contract package needs to implement the `get_create_batch_transaction` method with the specified key word arguments (see example in *Deploy your own*, below). Similar to the above, to send this transaction to the ledger for processing, we first sign the message with the decision maker and then send the signed transaction to the `fetchai/ledger:0.21.4` connection using the `fetchai/ledger_api:1.0.0` protocol.
- the `get_raw_message` message is used to request any contract method call for a specific contract which does not change state in the contract. For instance, to request a call to get a hash from some input data in the deployed `erc1155` smart contract wrapped in the `fetchai/erc1155:0.23.2` package, we send the following message to the `fetchai/ledger:0.21.4`:
@@ -89,8 +92,8 @@ contract_api_msg = ContractApiMessage(
),
)
```
-This message will be handled by the `fetchai/ledger:0.21.4` connection and then a `raw_message` message will be returned with the matching raw message. For this to be executed correctly, the `fetchai/erc1155:0.23.2` contract package needs to implement the `get_hash_single` method with the specified key word arguments. We can then send the raw message to the `fetchai/ledger:0.21.4` connection using the `fetchai/ledger_api:1.0.0` protocol. In this case, signing is not required.
+This message will be handled by the `fetchai/ledger:0.21.4` connection and then a `raw_message` message will be returned with the matching raw message. For this to be executed correctly, the `fetchai/erc1155:0.23.2` contract package needs to implement the `get_hash_single` method with the specified key word arguments. We can then send the raw message to the `fetchai/ledger:0.21.4` connection using the `fetchai/ledger_api:1.0.0` protocol. In this case, signing is not required.
- the `get_state` message is used to request any contract method call to query state in the deployed contract. For instance, to request a call to get the balances in the deployed `erc1155` smart contract wrapped in the `fetchai/erc1155:0.23.2` package, we send the following message to the `fetchai/ledger:0.21.4`:
@@ -107,8 +110,8 @@ contract_api_msg = ContractApiMessage(
),
)
```
-This message will be handled by the `fetchai/ledger:0.21.4` connection and then a `state` message will be returned with the matching state. For this to be executed correctly, the `fetchai/erc1155:0.23.2` contract package needs to implement the `get_balance` method with the specified key word arguments. We can then send the raw message to the `fetchai/ledger:0.21.4` connection using the `fetchai/ledger_api:1.0.0` protocol. In this case, signing is not required.
+This message will be handled by the `fetchai/ledger:0.21.4` connection and then a `state` message will be returned with the matching state. For this to be executed correctly, the `fetchai/erc1155:0.23.2` contract package needs to implement the `get_balance` method with the specified key word arguments. We can then send the raw message to the `fetchai/ledger:0.21.4` connection using the `fetchai/ledger_api:1.0.0` protocol. In this case, signing is not required.
## Developing your own
@@ -120,10 +123,9 @@ aea scaffold contract my_new_contract
This will scaffold a contract package called `my_new_contract` with three files:
-* `__init__.py`
-* `contract.py`, containing the scaffolded contract class
-* `contract.yaml` containing the scaffolded configuration file
-
+- `__init__.py`
+- `contract.py`, containing the scaffolded contract class
+- `contract.yaml` containing the scaffolded configuration file
Once your scaffold is in place, you can create a `build` folder in the package and copy the smart contract interface (e.g. bytes code and ABI) to it. Then, specify the path to the interfaces in the `contract.yaml`. For instance, if you use Ethereum, then you might specify the following:
@@ -131,8 +133,8 @@ Once your scaffold is in place, you can create a `build` folder in the package a
contract_interface_paths:
ethereum: build/my_contract.json
```
-where `ethereum` is the ledger id and `my_contract.json` is the file containing the byte code and ABI.
+where `ethereum` is the ledger id and `my_contract.json` is the file containing the byte code and ABI.
Finally, you will want to implement the part of the contract interface you need in `contract.py`:
@@ -180,6 +182,7 @@ class MyContract(Contract):
tx = cls._try_estimate_gas(ledger_api, tx)
return tx
```
+
Above, we implement a method to create a transaction, in this case a transaction to create a batch of tokens. The method will be called by the framework, specifically the `fetchai/ledger:0.21.4` connection once it receives a message (see bullet point 2 above). The method first gets the latest transaction nonce of the `deployer_address`, then constructs the contract instance, then uses the instance to build the transaction and finally updates the gas on the transaction.
It helps to look at existing contract packages, like `fetchai/erc1155:0.23.2`, and skills using them, like `fetchai/erc1155_client:0.11.0` and `fetchai/erc1155_deploy:0.31.5`, for inspiration and guidance.
diff --git a/docs/core-components-1.md b/docs/core-components-1.md
index 2ded1b513d..995f18b932 100644
--- a/docs/core-components-1.md
+++ b/docs/core-components-1.md
@@ -1,8 +1,10 @@
+# Core Components - Part 1
+
The AEA framework consists of several core components, some required to run an AEA and others optional.
The following sections discuss the inner workings of the AEA framework and how it calls the code in custom packages (see inversion of control and a helpful comparison here). Whilst it is in principle possible to use parts of the framework as a library, we do not recommend it.
-## The elements each AEA uses
+## The Elements Each AEA Uses
### Envelope
@@ -12,15 +14,11 @@ The following sections discuss the inner workings of the AEA framework and how i
An `Envelope` is the core object with which agents communicate. It is a vehicle for `Messages` with five attributes:
-* `to`: defines the destination address.
-
-* `sender`: defines the sender address.
-
-* `protocol_id`: defines the id of the `Protocol`.
-
-* `message`: is a bytes field which holds the `Message` in serialized form.
-
-* `Optional[context]`: an optional field to specify routing information in a URI.
+- `to`: defines the destination address.
+- `sender`: defines the sender address.
+- `protocol_id`: defines the id of the `Protocol`.
+- `message`: is a bytes field which holds the `Message` in serialized form.
+- `Optional[context]`: an optional field to specify routing information in a URI.
`Messages` must adhere to a `Protocol`.
@@ -28,11 +26,9 @@ An `Envelope` is the core object
`Protocols` define agent-to-agent as well as component-to-component interactions within AEAs. As such, they include:
-* `Messages` defining the syntax of messages;
-
-* `Serialization` defining how a `Message` is encoded for transport; and, optionally
-
-* `Dialogues`, which define rules over `Message` sequences.
+- `Messages` defining the syntax of messages;
+- `Serialization` defining how a `Message` is encoded for transport; and, optionally
+- `Dialogues`, which define rules over `Message` sequences.
The framework provides one default `Protocol`, called `default` (current version `fetchai/default:1.1.6`). This `Protocol` provides a bare-bones implementation for an AEA `Protocol` which includes a `DefaultMessage` class and associated `DefaultSerializer` and `DefaultDialogue` classes.
@@ -66,12 +62,12 @@ It maintains an `InBox` and `Handler`: each `Skill` has zero, one or more `Handler` objects. There is a one-to-one correspondence between `Handlers` and the protocols in an AEA (also known as the _registered protocols_). Handlers implement AEAs' **reactive** behaviour. If an AEA understands a `Protocol` referenced in a received `Envelope` (i.e. the protocol is registered in this AEA), this envelope is sent to the corresponding `Handler` which executes the AEA's reaction to this `Message`.
-* `Behaviour`: a `skill` can have zero, one or more `Behaviours`, each encapsulating actions which further the AEAs goal and are initiated by internals of the AEA rather than external events. Behaviours implement AEAs' **pro-activeness**. The framework provides a number of abstract base classes implementing different types of simple and composite behaviours (e.g. cyclic, one-shot, finite-state-machine, etc), and these define how often and in what order a behaviour and its sub-behaviours must be executed.
-* `Model`: zero, one or more `Models` that inherit from the `Model` abstract base class and are accessible via the `SkillContext`.
-* `Task`: zero, one or more `Tasks` encapsulate background work internal to the AEA. `Task` differs from the other three in that it is not a part of `Skills`, but `Tasks` are declared in or from `Skills` if a packaging approach for AEA creation is used.
+- `Handler`: each `Skill` has zero, one or more `Handler` objects. There is a one-to-one correspondence between `Handlers` and the protocols in an AEA (also known as the _registered protocols_). Handlers implement AEAs' **reactive** behaviour. If an AEA understands a `Protocol` referenced in a received `Envelope` (i.e. the protocol is registered in this AEA), this envelope is sent to the corresponding `Handler` which executes the AEA's reaction to this `Message`.
+- `Behaviour`: a `skill` can have zero, one or more `Behaviours`, each encapsulating actions which further the AEAs goal and are initiated by internals of the AEA rather than external events. Behaviours implement AEAs' **pro-activeness**. The framework provides a number of abstract base classes implementing different types of simple and composite behaviours (e.g. cyclic, one-shot, finite-state-machine, etc), and these define how often and in what order a behaviour and its sub-behaviours must be executed.
+- `Model`: zero, one or more `Models` that inherit from the `Model` abstract base class and are accessible via the `SkillContext`.
+- `Task`: zero, one or more `Tasks` encapsulate background work internal to the AEA. `Task` differs from the other three in that it is not a part of `Skills`, but `Tasks` are declared in or from `Skills` if a packaging approach for AEA creation is used.
-A `Skill` can read (parts of) an AEA's state (as summarised in the `AgentContext`), and suggests actions to the AEA according to its specific logic. As such, more than one `Skill` could exist per `Protocol`, competing with each other in suggesting to the AEA the best course of actions to take. In technical terms, this means `Skills` are horizontally arranged.
+A `Skill` can read (parts of) an AEA's state (as summarised in the `AgentContext`), and propose actions to the AEA according to its specific logic. As such, more than one `Skill` could exist per `Protocol`, competing with each other in suggesting to the AEA the best course of actions to take. In technical terms, this means `Skills` are horizontally arranged.
For instance, an AEA which is trading goods, could subscribe to more than one `Skill`, where each corresponds to a different trading strategy.
@@ -79,25 +75,25 @@ The framework places no limits on the complexity of `Skills`. They can implement
The framework provides one default `Skill`, called `error`. Additional `Skills` can be added as packages. For more details on `Skills` head over to the `Skill` guide .
-### Agent loop
+### Agent Loop
The `AgentLoop` performs a series of activities while the `AEA` state is not `stopped`.
-* it calls the `act()` function of all active registered `Behaviours` at their respective tick rate.
-* it grabs all Envelopes waiting in the `InBox` queue and calls the `handle()` function for the `Handlers` currently registered against the `Protocol` of the `Envelope`.
-* it dispatches the internal `Messages` from the decision maker (described below) to the handler in the relevant `Skill`.
+- it calls the `act()` function of all active registered `Behaviours` at their respective tick rate.
+- it grabs all Envelopes waiting in the `InBox` queue and calls the `handle()` function for the `Handlers` currently registered against the `Protocol` of the `Envelope`.
+- it dispatches the internal `Messages` from the decision maker (described below) to the handler in the relevant `Skill`.
The `AgentLoop` and `Multiplexer` are decoupled via the `InBox` and `OutBox`, and both are maintained by the `Runtime`.
-## Next steps
+## Next Steps
-### Recommended
+### Recommended
We recommend you continue with the next step in the 'Getting Started' series:
- AEA and web frameworks
-### Relevant deep-dives
+### Relevant Deep-Dives
Most AEA development focuses on developing the `Skills` and `Protocols` necessary for an AEA to deliver against its economic objectives.
@@ -112,6 +108,3 @@ Most of an AEA developer's time is spent on `Skill` development. `Skills` are th
In most cases, one of the available `Connection` packages can be used. Occasionally, you might develop your own `Connection`:
- Connections
-
-
-
diff --git a/docs/core-components-2.md b/docs/core-components-2.md
index 2c63f56977..00700bfcb0 100644
--- a/docs/core-components-2.md
+++ b/docs/core-components-2.md
@@ -1,8 +1,10 @@
+# Core components - Part 2
+
The AEA framework consists of several core components, some required to run an AEA and others optional.
-In Core Components - Part 1 we described the common components each AEA uses. In this page, we will look at more advanced components.
+In Core Components - Part 1 we described the common components each AEA uses. In this page, we will look at more advanced components.
-## Required components used by AEAs
+## Required Components Used by AEAs
### Decision Maker
@@ -10,7 +12,7 @@ In Core Components - Part 1 we described the
The `DecisionMaker` can be thought of as a `Wallet` manager plus "economic brain" of the AEA. It is responsible for the AEA's crypto-economic security and goal management, and it contains the preference and ownership representation of the AEA. The decision maker is the only component with access to the `Wallet`'s private keys.
-You can learn more about the decision maker here. In its simplest form, the decision maker acts like a `Wallet` with `Handler` to react to messages it receives from the skills.
+You can learn more about the decision maker here. In its simplest form, the decision maker acts like a `Wallet` with `Handler` that reacts to the messages it receives from the skills.
### Wallet
@@ -21,9 +23,9 @@ The agent has two sets of private keys, as configured in the `aea-config.yaml`:
- `private_key_paths`: This is a dictionary mapping identifiers to the file paths of private keys used in the AEA. For each identifier, e.g. `fetchai`, the AEA can have one private key. The private keys listed here are available in the `Decision Maker` and the associated public keys and addresses are available in all skills. The AEA uses these keys to sign transactions and messages. These keys usually hold the AEAs funds.
- `connection_private_key_paths`: This is a dictionary mapping identifiers to the file paths of private keys used in connections. For each identifier, e.g. `fetchai`, the `Multiplexer` can have one private key. The private keys listed here are available in the connections. The connections use these keys to secure message transport, for instance.
-It is the responsibility of the AEA's user to safe-guard the keys used and ensure that keys are only used in a single AEA. Using the same key across different AEAs will lead to various failure modes.
+It is the responsibility of the AEA's user to safeguard the keys used and ensure that keys are only used in a single AEA. Using the same key across different AEAs will lead to various failure modes.
-Private keys can be encrypted at rest. The CLI commands used for interacting with the wallet allow specifying a password for encryption/decryption.
+Private keys can be encrypted at rest. The CLI commands used for interacting with the wallet allow specifying a password for encryption/decryption.
### Identity
@@ -31,7 +33,7 @@ The `Identity` is an abstrac
The identity can be accessed in a `Skill` via the `AgentContext`.
-## Optional components used by AEAs
+## Optional Components Used by AEAs
### Contracts
@@ -43,30 +45,26 @@ The identity can be accessed in a `Skill` via the here.
-## Putting it together
+## Putting it Together
Taken together, the core components from this section and the first part provide the following simplified illustration of an AEA:
-## Next steps
+## Next Steps
-### Recommended
+### Recommended
We recommend you continue with the next step in the 'Getting Started' series:
- How AEAs talk to each other - Interaction protocols
-### Relevant deep-dives
+### Relevant Deep-Dives
Understanding the decision maker is vital to developing a goal oriented and crypto-economically safe AEA. You can learn more about the `DecisionMaker` in the following section:
- Decision Maker
-
Understanding `Contracts` is important when developing AEAs that make commitments or use smart contracts for other purposes. You can learn more about the `Contracts` agents use in the following section:
- Contracts
-
-
-
diff --git a/docs/css/my-styles.css b/docs/css/my-styles.css
index 0799b02f96..92508981d5 100644
--- a/docs/css/my-styles.css
+++ b/docs/css/my-styles.css
@@ -1,37 +1,75 @@
-pre {
- background-color: #f8f8f7;
+/* overriding colours */
+[data-md-color-scheme="default"] {
+ --md-primary-fg-color: #4051b5;
+ --md-typeset-color: #525252;
+ --md-a-color:#1d6ff5;
+}
+[data-md-color-scheme="slate"] {
+ --md-primary-fg-color: #fff;
+ --md-typeset-color: #d0d6fc;
+ --md-a-color:#5997fd;
+}
+
+/*overriding the header*/
+.md-header-nav__button.md-logo img {
+ width: auto!important;
}
-code {
- background-color: #0083fb;
+.md-nav__button .md-logo{
+ width: auto!important;
}
-/* this doesn't work now
-.md-nav__link {
- text-transform: uppercase;
- color: #0083fb;
+.md-header__button.md-logo img {
+ width: auto !important;
}
-*/
-/* Katharine's css additions */
-.md-header,
-.md-tabs,
-.md-footer-meta,
-.md-footer-nav,
-.md-footer-nav__inner {
- background-color: #172b6e;
+
+@media screen and (max-width: 76.1875em) {
+ .md-nav--primary .md-nav__title[for="__drawer"] {
+ background-color: #202943 !important;
+ }
+
+ .md-nav--primary .md-nav__title[for=__drawer] img{
+ width: 180px;
+ height: auto;
+ }
}
-.md-nav__title {
- color: #172b6e;
+.md-header{
+ background-color:#202943!important;
+ padding: 10px;
+ height: auto;
}
-.md-icon {
- ./assets/images/favicon.ico;
+/* Overriding font header, link, paragraph styling */
+.md-typeset h3,
+.md-typeset h1,
+.md-typeset h2 {
+ margin-bottom: 0.4em;
+ color: var(--md-primary-fg-color);
+ font-weight: 400;
}
-/* Needed so that Mermaid UML diagrams don't end up being massively tall */
-svg{
- height: auto;
+.md-typeset a {
+ color: var(--md-a-color);
+}
-}
\ No newline at end of file
+.black-link{
+ color: black!important;
+}
+
+.md-nav__item .md-nav__link--active {
+ padding: 4px 8px;
+ position: relative;
+ left: -8px;
+ background: #E9EBFC;
+ border-radius: 16px;
+ width: fit-content;
+ color: rgb(64, 81, 181)
+}
+
+@media screen and (max-width: 76.1875em) {
+ .md-nav__item .md-nav__link--active {
+ left: 0;
+ }
+}
diff --git a/docs/debug.md b/docs/debug.md
index 1a75cb0271..4917568702 100644
--- a/docs/debug.md
+++ b/docs/debug.md
@@ -1,3 +1,5 @@
+# Debugging
+
There are multiple ways in which to configure your AEA for debugging during development. We focus on the standard Python approach here.
## Using `pdb` stdlib
@@ -16,7 +18,7 @@ aea -s run
For more guidance on how to use `pdb` check out the documentation.
-## Using an IDE:
+## Using an IDE
- For VSCode modify the `launch.json` to include the following information:
@@ -36,4 +38,5 @@ For more guidance on how to use `pdb` check out the stand-alone transaction demo. The main difference is that now we are going to use the decision-maker to sign the transaction.
-First, import the libraries and the set the constant values. (Get the packages directory from the AEA repository `svn export https://github.com/fetchai/agents-aea.git/trunk/packages`.)
+First, import the libraries and the set the constant values. (Get the `packages` directory from the AEA repository `svn export https://github.com/fetchai/agents-aea.git/trunk/packages`.)
``` python
import logging
@@ -37,9 +39,9 @@ FETCHAI_PRIVATE_KEY_FILE_1 = "fetchai_private_key_1.txt"
FETCHAI_PRIVATE_KEY_FILE_2 = "fetchai_private_key_2.txt"
```
-## Create a private key and an AEA
+## Create a Private Key and an AEA
-To have access to the decision-maker, which is responsible for signing transactions, we need to create an AEA. We can create a an AEA with the builder, providing it with a private key we generate first.
+To have access to the decision-maker, which is responsible for signing transactions, we need to create an AEA. We can create an AEA with the builder, providing it with a private key we generate first.
``` python
# Create a private key
@@ -59,7 +61,7 @@ To have access to the decision-maker, which is responsible for signing transacti
my_aea = builder.build()
```
-## Add a simple skill
+## Add a Simple Skill
Add a simple skill with a signing handler and the signing dialogues.
@@ -85,7 +87,8 @@ Add a simple skill with a signing handler and the signing dialogues.
my_aea.resources.add_skill(simple_skill)
```
-## Create a second identity
+## Create a Second Identity
+
``` python
# create a second identity
create_private_key(
@@ -103,9 +106,10 @@ Add a simple skill with a signing handler and the signing dialogues.
)
```
-## Create the signing message
+## Create the Signing Message
+
+Next, we are creating the signing message and sending it to the decision-maker.
-Next, we are creating the signing message and we send it to the decision-maker.
``` python
# create signing message for decision maker to sign
terms = Terms(
@@ -143,9 +147,10 @@ Next, we are creating the signing message and we send it to the decision-maker.
```
-## Run the agent
+## Run the Agent
+
+Finally, we are running the agent and expect the signed transaction to be printed in the terminal.
-Finally, we are running the agent and we expect the signed transaction to be printed in the terminal.
``` python
# Set the AEA running in a different thread
try:
@@ -164,9 +169,10 @@ Finally, we are running the agent and we expect the signed transaction to be pri
After the completion of the signing, we get the signed transaction.
-## More details
+## More Details
To be able to register a handler that reads the internal messages, we have to create a class at the end of the file which processes the signing messages.
+
``` python
class SigningDialogues(Model, BaseSigningDialogues):
"""Signing dialogues model."""
@@ -298,275 +304,274 @@ class SigningHandler(Handler):
You can find the full code for this example below:
-Transaction via decision-maker full code
-
-``` python
-import logging
-import time
-from threading import Thread
-from typing import Optional, cast
-
-from aea_ledger_fetchai import FetchAICrypto
-
-from aea.aea_builder import AEABuilder
-from aea.configurations.base import PublicId, SkillConfig
-from aea.crypto.helpers import create_private_key
-from aea.crypto.ledger_apis import LedgerApis
-from aea.crypto.wallet import Wallet
-from aea.helpers.transaction.base import RawTransaction, Terms
-from aea.identity.base import Identity
-from aea.protocols.base import Address, Message
-from aea.protocols.dialogue.base import Dialogue
-from aea.skills.base import Handler, Model, Skill, SkillContext
-
-from packages.fetchai.protocols.signing.dialogues import SigningDialogue
-from packages.fetchai.protocols.signing.dialogues import (
- SigningDialogues as BaseSigningDialogues,
-)
-from packages.fetchai.protocols.signing.message import SigningMessage
-
-from tests.conftest import get_wealth_if_needed
-
-
-logger = logging.getLogger("aea")
-logging.basicConfig(level=logging.INFO)
-
-FETCHAI_PRIVATE_KEY_FILE_1 = "fetchai_private_key_1.txt"
-FETCHAI_PRIVATE_KEY_FILE_2 = "fetchai_private_key_2.txt"
-
-
-def run():
- """Run demo."""
-
- # Create a private key
- create_private_key(
- FetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_1
- )
-
- # Instantiate the builder and build the AEA
- # By default, the default protocol, error skill and stub connection are added
- builder = AEABuilder()
-
- builder.set_name("my_aea")
-
- builder.add_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE_1)
-
- # Create our AEA
- my_aea = builder.build()
-
- # add a simple skill with handler
- skill_context = SkillContext(my_aea.context)
- skill_config = SkillConfig(name="simple_skill", author="fetchai", version="0.1.0")
- signing_handler = SigningHandler(
- skill_context=skill_context, name="signing_handler"
- )
- signing_dialogues_model = SigningDialogues(
- skill_context=skill_context,
- name="signing_dialogues",
- self_address=str(skill_config.public_id),
- )
-
- simple_skill = Skill(
- skill_config,
- skill_context,
- handlers={signing_handler.name: signing_handler},
- models={signing_dialogues_model.name: signing_dialogues_model},
- )
- my_aea.resources.add_skill(simple_skill)
-
- # create a second identity
- create_private_key(
- FetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_2
- )
-
- counterparty_wallet = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_2})
- get_wealth_if_needed(counterparty_wallet.addresses["fetchai"])
-
- counterparty_identity = Identity(
- name="counterparty_aea",
- addresses=counterparty_wallet.addresses,
- public_keys=counterparty_wallet.public_keys,
- default_address_key=FetchAICrypto.identifier,
- )
-
- # create signing message for decision maker to sign
- terms = Terms(
- ledger_id=FetchAICrypto.identifier,
- sender_address=my_aea.identity.address,
- counterparty_address=counterparty_identity.address,
- amount_by_currency_id={"FET": -1},
- quantities_by_good_id={"some_service": 1},
- nonce="some_nonce",
- fee_by_currency_id={"FET": 0},
- )
- get_wealth_if_needed(terms.sender_address)
-
- signing_dialogues = cast(SigningDialogues, skill_context.signing_dialogues)
- stub_transaction = LedgerApis.get_transfer_transaction(
- terms.ledger_id,
- terms.sender_address,
- terms.counterparty_address,
- terms.sender_payable_amount,
- terms.sender_fee,
- terms.nonce,
- )
- signing_msg = SigningMessage(
- performative=SigningMessage.Performative.SIGN_TRANSACTION,
- dialogue_reference=signing_dialogues.new_self_initiated_dialogue_reference(),
- raw_transaction=RawTransaction(FetchAICrypto.identifier, stub_transaction),
- terms=terms,
+??? note "Transaction via decision-maker full code:"
+
+ ``` python
+ import logging
+ import time
+ from threading import Thread
+ from typing import Optional, cast
+
+ from aea_ledger_fetchai import FetchAICrypto
+
+ from aea.aea_builder import AEABuilder
+ from aea.configurations.base import PublicId, SkillConfig
+ from aea.crypto.helpers import create_private_key
+ from aea.crypto.ledger_apis import LedgerApis
+ from aea.crypto.wallet import Wallet
+ from aea.helpers.transaction.base import RawTransaction, Terms
+ from aea.identity.base import Identity
+ from aea.protocols.base import Address, Message
+ from aea.protocols.dialogue.base import Dialogue
+ from aea.skills.base import Handler, Model, Skill, SkillContext
+
+ from packages.fetchai.protocols.signing.dialogues import SigningDialogue
+ from packages.fetchai.protocols.signing.dialogues import (
+ SigningDialogues as BaseSigningDialogues,
)
- signing_dialogue = cast(
- Optional[SigningDialogue],
- signing_dialogues.create_with_message("decision_maker", signing_msg),
- )
- assert signing_dialogue is not None
- my_aea.context.decision_maker_message_queue.put_nowait(signing_msg)
-
- # Set the AEA running in a different thread
- try:
- logger.info("STARTING AEA NOW!")
- t = Thread(target=my_aea.start)
- t.start()
-
- # Let it run long enough to interact with the decision maker
- time.sleep(1)
- finally:
- # Shut down the AEA
- logger.info("STOPPING AEA NOW!")
- my_aea.stop()
- t.join()
-
-
-class SigningDialogues(Model, BaseSigningDialogues):
- """Signing dialogues model."""
-
- def __init__(self, self_address: Address, **kwargs) -> None:
- """
- Initialize dialogues.
-
- :return: None
- """
- Model.__init__(self, **kwargs)
-
- def role_from_first_message( # pylint: disable=unused-argument
- message: Message, receiver_address: Address
- ) -> Dialogue.Role:
- """Infer the role of the agent from an incoming/outgoing first message
-
- :param message: an incoming/outgoing first message
- :param receiver_address: the address of the receiving agent
- :return: The role of the agent
- """
- return SigningDialogue.Role.SKILL
-
- BaseSigningDialogues.__init__(
- self,
- self_address=self_address,
- role_from_first_message=role_from_first_message,
+ from packages.fetchai.protocols.signing.message import SigningMessage
+
+ from tests.conftest import get_wealth_if_needed
+
+
+ logger = logging.getLogger("aea")
+ logging.basicConfig(level=logging.INFO)
+
+ FETCHAI_PRIVATE_KEY_FILE_1 = "fetchai_private_key_1.txt"
+ FETCHAI_PRIVATE_KEY_FILE_2 = "fetchai_private_key_2.txt"
+
+
+ def run():
+ """Run demo."""
+
+ # Create a private key
+ create_private_key(
+ FetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_1
+ )
+
+ # Instantiate the builder and build the AEA
+ # By default, the default protocol, error skill and stub connection are added
+ builder = AEABuilder()
+
+ builder.set_name("my_aea")
+
+ builder.add_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE_1)
+
+ # Create our AEA
+ my_aea = builder.build()
+
+ # add a simple skill with handler
+ skill_context = SkillContext(my_aea.context)
+ skill_config = SkillConfig(name="simple_skill", author="fetchai", version="0.1.0")
+ signing_handler = SigningHandler(
+ skill_context=skill_context, name="signing_handler"
+ )
+ signing_dialogues_model = SigningDialogues(
+ skill_context=skill_context,
+ name="signing_dialogues",
+ self_address=str(skill_config.public_id),
+ )
+
+ simple_skill = Skill(
+ skill_config,
+ skill_context,
+ handlers={signing_handler.name: signing_handler},
+ models={signing_dialogues_model.name: signing_dialogues_model},
+ )
+ my_aea.resources.add_skill(simple_skill)
+
+ # create a second identity
+ create_private_key(
+ FetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_2
+ )
+
+ counterparty_wallet = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_2})
+ get_wealth_if_needed(counterparty_wallet.addresses["fetchai"])
+
+ counterparty_identity = Identity(
+ name="counterparty_aea",
+ addresses=counterparty_wallet.addresses,
+ public_keys=counterparty_wallet.public_keys,
+ default_address_key=FetchAICrypto.identifier,
+ )
+
+ # create signing message for decision maker to sign
+ terms = Terms(
+ ledger_id=FetchAICrypto.identifier,
+ sender_address=my_aea.identity.address,
+ counterparty_address=counterparty_identity.address,
+ amount_by_currency_id={"FET": -1},
+ quantities_by_good_id={"some_service": 1},
+ nonce="some_nonce",
+ fee_by_currency_id={"FET": 0},
+ )
+ get_wealth_if_needed(terms.sender_address)
+
+ signing_dialogues = cast(SigningDialogues, skill_context.signing_dialogues)
+ stub_transaction = LedgerApis.get_transfer_transaction(
+ terms.ledger_id,
+ terms.sender_address,
+ terms.counterparty_address,
+ terms.sender_payable_amount,
+ terms.sender_fee,
+ terms.nonce,
+ )
+ signing_msg = SigningMessage(
+ performative=SigningMessage.Performative.SIGN_TRANSACTION,
+ dialogue_reference=signing_dialogues.new_self_initiated_dialogue_reference(),
+ raw_transaction=RawTransaction(FetchAICrypto.identifier, stub_transaction),
+ terms=terms,
)
-
-
-class SigningHandler(Handler):
- """Implement the signing handler."""
-
- SUPPORTED_PROTOCOL = SigningMessage.protocol_id # type: Optional[PublicId]
-
- def setup(self) -> None:
- """Implement the setup for the handler."""
-
- def handle(self, message: Message) -> None:
- """
- Implement the reaction to a message.
-
- :param message: the message
- :return: None
- """
- signing_msg = cast(SigningMessage, message)
-
- # recover dialogue
- signing_dialogues = cast(SigningDialogues, self.context.signing_dialogues)
signing_dialogue = cast(
- Optional[SigningDialogue], signing_dialogues.update(signing_msg)
+ Optional[SigningDialogue],
+ signing_dialogues.create_with_message("decision_maker", signing_msg),
)
- if signing_dialogue is None:
- self._handle_unidentified_dialogue(signing_msg)
- return
-
- # handle message
- if signing_msg.performative is SigningMessage.Performative.SIGNED_TRANSACTION:
- self._handle_signed_transaction(signing_msg, signing_dialogue)
- elif signing_msg.performative is SigningMessage.Performative.ERROR:
- self._handle_error(signing_msg, signing_dialogue)
- else:
- self._handle_invalid(signing_msg, signing_dialogue)
-
- def teardown(self) -> None:
- """
- Implement the handler teardown.
-
- :return: None
- """
-
- def _handle_unidentified_dialogue(self, signing_msg: SigningMessage) -> None:
- """
- Handle an unidentified dialogue.
-
- :param msg: the message
- """
- self.context.logger.info(
- "received invalid signing message={}, unidentified dialogue.".format(
- signing_msg
+ assert signing_dialogue is not None
+ my_aea.context.decision_maker_message_queue.put_nowait(signing_msg)
+
+ # Set the AEA running in a different thread
+ try:
+ logger.info("STARTING AEA NOW!")
+ t = Thread(target=my_aea.start)
+ t.start()
+
+ # Let it run long enough to interact with the decision maker
+ time.sleep(1)
+ finally:
+ # Shut down the AEA
+ logger.info("STOPPING AEA NOW!")
+ my_aea.stop()
+ t.join()
+
+
+ class SigningDialogues(Model, BaseSigningDialogues):
+ """Signing dialogues model."""
+
+ def __init__(self, self_address: Address, **kwargs) -> None:
+ """
+ Initialize dialogues.
+
+ :return: None
+ """
+ Model.__init__(self, **kwargs)
+
+ def role_from_first_message( # pylint: disable=unused-argument
+ message: Message, receiver_address: Address
+ ) -> Dialogue.Role:
+ """Infer the role of the agent from an incoming/outgoing first message
+
+ :param message: an incoming/outgoing first message
+ :param receiver_address: the address of the receiving agent
+ :return: The role of the agent
+ """
+ return SigningDialogue.Role.SKILL
+
+ BaseSigningDialogues.__init__(
+ self,
+ self_address=self_address,
+ role_from_first_message=role_from_first_message,
)
- )
-
- def _handle_signed_transaction(
- self, signing_msg: SigningMessage, signing_dialogue: SigningDialogue
- ) -> None:
- """
- Handle a signing message.
-
- :param signing_msg: the signing message
- :param signing_dialogue: the dialogue
- :return: None
- """
- self.context.logger.info("transaction signing was successful.")
- logger.info(signing_msg.signed_transaction)
-
- def _handle_error(
- self, signing_msg: SigningMessage, signing_dialogue: SigningDialogue
- ) -> None:
- """
- Handle an oef search message.
-
- :param signing_msg: the signing message
- :param signing_dialogue: the dialogue
- :return: None
- """
- self.context.logger.info(
- "transaction signing was not successful. Error_code={} in dialogue={}".format(
- signing_msg.error_code, signing_dialogue
+
+
+ class SigningHandler(Handler):
+ """Implement the signing handler."""
+
+ SUPPORTED_PROTOCOL = SigningMessage.protocol_id # type: Optional[PublicId]
+
+ def setup(self) -> None:
+ """Implement the setup for the handler."""
+
+ def handle(self, message: Message) -> None:
+ """
+ Implement the reaction to a message.
+
+ :param message: the message
+ :return: None
+ """
+ signing_msg = cast(SigningMessage, message)
+
+ # recover dialogue
+ signing_dialogues = cast(SigningDialogues, self.context.signing_dialogues)
+ signing_dialogue = cast(
+ Optional[SigningDialogue], signing_dialogues.update(signing_msg)
)
- )
-
- def _handle_invalid(
- self, signing_msg: SigningMessage, signing_dialogue: SigningDialogue
- ) -> None:
- """
- Handle an oef search message.
-
- :param signing_msg: the signing message
- :param signing_dialogue: the dialogue
- :return: None
- """
- self.context.logger.warning(
- "cannot handle signing message of performative={} in dialogue={}.".format(
- signing_msg.performative, signing_dialogue
+ if signing_dialogue is None:
+ self._handle_unidentified_dialogue(signing_msg)
+ return
+
+ # handle message
+ if signing_msg.performative is SigningMessage.Performative.SIGNED_TRANSACTION:
+ self._handle_signed_transaction(signing_msg, signing_dialogue)
+ elif signing_msg.performative is SigningMessage.Performative.ERROR:
+ self._handle_error(signing_msg, signing_dialogue)
+ else:
+ self._handle_invalid(signing_msg, signing_dialogue)
+
+ def teardown(self) -> None:
+ """
+ Implement the handler teardown.
+
+ :return: None
+ """
+
+ def _handle_unidentified_dialogue(self, signing_msg: SigningMessage) -> None:
+ """
+ Handle an unidentified dialogue.
+
+ :param msg: the message
+ """
+ self.context.logger.info(
+ "received invalid signing message={}, unidentified dialogue.".format(
+ signing_msg
+ )
)
- )
-
-
-if __name__ == "__main__":
- run()
-```
-
\ No newline at end of file
+
+ def _handle_signed_transaction(
+ self, signing_msg: SigningMessage, signing_dialogue: SigningDialogue
+ ) -> None:
+ """
+ Handle a signing message.
+
+ :param signing_msg: the signing message
+ :param signing_dialogue: the dialogue
+ :return: None
+ """
+ self.context.logger.info("transaction signing was successful.")
+ logger.info(signing_msg.signed_transaction)
+
+ def _handle_error(
+ self, signing_msg: SigningMessage, signing_dialogue: SigningDialogue
+ ) -> None:
+ """
+ Handle an oef search message.
+
+ :param signing_msg: the signing message
+ :param signing_dialogue: the dialogue
+ :return: None
+ """
+ self.context.logger.info(
+ "transaction signing was not successful. Error_code={} in dialogue={}".format(
+ signing_msg.error_code, signing_dialogue
+ )
+ )
+
+ def _handle_invalid(
+ self, signing_msg: SigningMessage, signing_dialogue: SigningDialogue
+ ) -> None:
+ """
+ Handle an oef search message.
+
+ :param signing_msg: the signing message
+ :param signing_dialogue: the dialogue
+ :return: None
+ """
+ self.context.logger.warning(
+ "cannot handle signing message of performative={} in dialogue={}.".format(
+ signing_msg.performative, signing_dialogue
+ )
+ )
+
+
+ if __name__ == "__main__":
+ run()
+ ```
diff --git a/docs/decision-maker.md b/docs/decision-maker.md
index 2e5bfab0d7..db196496ab 100644
--- a/docs/decision-maker.md
+++ b/docs/decision-maker.md
@@ -1,6 +1,8 @@
+# Decision Maker
+
The `DecisionMaker` can be thought of like a wallet manager plus "economic brain" of the AEA. It is responsible for the AEA's crypto-economic security and goal management, and it contains the preference and ownership representation of the AEA. The decision maker is the only component which has access to the wallet's private keys.
-## Interaction with skills
+## Interaction with Skills
Skills communicate with the decision maker via `Messages`. At present, the decision maker processes messages of two protocols:
@@ -9,7 +11,8 @@ Skills communicate with the decision maker via `StateUpdateMessage`: it is used to initialize the decision maker with preferences and ownership states. It can also be used to update the ownership states in the decision maker if the settlement of transaction takes place.
A message, say `msg`, is sent to the decision maker like so from any skill:
-```
+
+``` python
self.context.decision_maker_message_queue.put_nowait(msg)
```
@@ -20,15 +23,15 @@ To process `Messages` from the decision maker in a given skill you need to creat
``` python
class SigningHandler(Handler):
- protocol_id = SigningMessage.protocol_id
+ protocol_id = SigningMessage.protocol_id
- def handle(self, message: Message):
- """
- Handle a signing message.
+ def handle(self, message: Message):
+ """
+ Handle a signing message.
- :param message: the signing message from the decision maker.
- """
- # code to handle the message
+ :param message: the signing message from the decision maker.
+ """
+ # code to handle the message
```
## Custom `DecisionMaker`
@@ -50,10 +53,7 @@ The easiest way to add a custom decision maker handler is to run the following c
aea scaffold decision-maker-handler
```
-You can then implement your own custom logic to process messages and interact with the `Wallet`.
+You can then implement your own custom logic to process messages and interact with the `Wallet`.
-
-
Note
-
For examples how to use these concepts have a look at the tac_ skills. These functionalities are experimental and subject to change.
-
-
\ No newline at end of file
+!!! note
+ For examples how to use these concepts have a look at the `tac_` skills. These functionalities are experimental and subject to change.
diff --git a/docs/defining-data-models.md b/docs/defining-data-models.md
index 26542c183d..b51b9afdc8 100644
--- a/docs/defining-data-models.md
+++ b/docs/defining-data-models.md
@@ -1,10 +1,11 @@
+# Defining Data Models
+
In this section, we explain how to define _data models_, an important component of the OEF Search & Discovery. It allows agents to describe themselves and to discover the services/resources they are interested in.
In a sentence, a `DataModel` is a set of `attributes`, and a `Description` of a service/resource is an assignment of those attributes.
All you need to specify data models and descriptions (that is, instances of the data model) can be found in the `aea.helpers.search` module.
-
## Attributes
At the lowest level of our data model language, we have the `Attribute`.
@@ -14,11 +15,11 @@ It is identified by a `name`, that must be unique in a given data model (that is
Every attribute has a `type`, that specifies the domain of the property, that is, the possible values that the attribute can assume. At the moment, we support five types of attributes:
-* strings
-* integers
-* booleans
-* floats
-* locations, i.e. instances of `Location` (pairs of (latitude, longitude))
+- strings
+- integers
+- booleans
+- floats
+- locations, i.e. instances of `Location` (pairs of (latitude, longitude))
An attribute can be `optional`, in the sense that instantiation of the attribute is not mandatory by the instances of the data model.
@@ -26,15 +27,15 @@ Finally, every attribute might have a `description` that explains the purpose of
**Example**: suppose we have a bookshop, and we want to describe the books we sell. Presumably, we would like to include: the following properties of our books:
-* The `title`
-* The `author`
-* The `genre` (e.g. science fiction, horror)
-* The `year of publication`
-* The `average rating` (average of the ratings between 0 and 5)
-* The `ISBN` code
-* If it can be sold as an e-book.
+- The `title`
+- The `author`
+- The `genre` (e.g. science fiction, horror)
+- The `year of publication`
+- The `average rating` (average of the ratings between 0 and 5)
+- The `ISBN` code
+- If it can be sold as an e-book.
-For each of this fields, we can define an attribute by using `Attribute`:
+For each of these fields, we can define an attribute by using `Attribute`:
``` python
from aea.helpers.search.models import Attribute, Location
@@ -47,6 +48,7 @@ attr_isbn = Attribute("ISBN", str, True, "The ISBN.")
attr_ebook = Attribute("ebook_available", bool, False, "If the book can be sold as an e-book.")
attr_bookshop = Attribute("bookshop_pos", Location, False, "The location of the bookshop where you can find the book")
```
+
Let's focus on the parameters of the `Attribute` constructor:
1. the first one is the name of the attribute. It is needed to instantiate a data model and to define queries over it.
@@ -56,7 +58,7 @@ Let's focus on the parameters of the `Attribute` constructor:
3. the third one is a boolean that specifies whether the attribute is _always required_ or it _can be omitted_. For example, we might not be able to specify the `ebook_available` attribute, maybe because it's not applicable to some kind of books.
4. the fourth parameter is the description, that is a short description of the purpose of the attribute.
-## Data models
+## Data Models
A _data model_ is just a set of _attributes_. The class that implements the data model is `DataModel`.
@@ -120,7 +122,7 @@ We defined the descriptions for two books, namely `It` and `_1984`, that refers
The attributes are instantiated with a dictionary that has:
-* as keys, the name of the attributes.
-* as values, the values associated with the attributes.
+- as keys, the name of the attributes.
+- as values, the values associated with the attributes.
Notice that in the latter book we omitted the `average_rating` field. We are allowed to do that because of the `average_rating` attribute is not mandatory.
diff --git a/docs/demos.md b/docs/demos.md
index 0c9d0bd737..ea2211f0cc 100644
--- a/docs/demos.md
+++ b/docs/demos.md
@@ -1,5 +1,7 @@
+# Demos
+
We provide demo guides for multiple use-cases, each one involving several AEAs interacting in a different scenario.
These demos serve to highlight the concept of AEAs as well as provide inspiration for developers. Demos should not be taken as production ready software, although every care is taken to fix bugs when reported.
-Demos are alphabetically sorted, we recommend you start with the weather skills demo.
\ No newline at end of file
+Demos are alphabetically sorted, we recommend you start with the weather skills demo.
diff --git a/docs/deployment.md b/docs/deployment.md
index 5817fef348..b73888a7b3 100644
--- a/docs/deployment.md
+++ b/docs/deployment.md
@@ -1,3 +1,4 @@
+# Deployment
The easiest way to run an AEA is using your development environment.
@@ -8,6 +9,7 @@ For deployment, we recommend you use Kubernetes navigate to our TAC deployment example.
diff --git a/docs/design-principles.md b/docs/design-principles.md
index 052fc3132e..7f5c2c8672 100644
--- a/docs/design-principles.md
+++ b/docs/design-principles.md
@@ -1,12 +1,12 @@
-The AEA framework development is guided by the following 8 principles:
+# Design Principles
-* **Accessibility**: ease of use.
-* **Modularity**: encourages module creation, sharing and reuse.
-* **Openness**: easily extensible with third-party libraries.
-* **Conciseness**: conceptually simple.
-* **Value-driven**: drives immediate value.
-* **Low entry barriers**: leverages existing programming languages and web protocols.
-* **Safety**: safe for the user (economically speaking).
-* **Goal-alignment**: seamless facilitation of users' preferences and goals.
+The AEA framework development is guided by the following 8 principles:
-
\ No newline at end of file
+- **Accessibility**: ease of use.
+- **Modularity**: encourages module creation, sharing and reuse.
+- **Openness**: easily extensible with third-party libraries.
+- **Conciseness**: conceptually simple.
+- **Value-driven**: drives immediate value.
+- **Low entry barriers**: leverages existing programming languages and web protocols.
+- **Safety**: safe for the user (economically speaking).
+- **Goal-alignment**: seamless facilitation of users' preferences and goals.
diff --git a/docs/development-setup.md b/docs/development-setup.md
index b9175eaa41..e63f4c7e22 100644
--- a/docs/development-setup.md
+++ b/docs/development-setup.md
@@ -1,4 +1,4 @@
-
+# Development Setup
An AEA consists of packages . When developing, it helps to be able to save packages in a local package registry, rather than pushing them to remote registry. This guide helps you set up a local package registry and configure the working directory for development.
@@ -13,20 +13,13 @@ There are two ways to write code for an AEA:
To prepare a directory (henceforth working directory) for development with the AEA framework you can take a few steps:
- Either, manually:
-
- - Ensure you start with an empty working directory to avoid any unnecessary side effects.
-
- - In your working directory, create an empty folder called `packages`. This folder will act as the local registry for packages.
-
- - In your working directory, create a `.env` file with the constant `PYTHONPATH=$PYTHONPATH:path_to_packages_dir` where `path_to_packages_dir` is the path to the packages folder in your working directory.
-
+ - Ensure you start with an empty working directory to avoid any unnecessary side effects.
+ - In your working directory, create an empty folder called `packages`. This folder will act as the local registry for packages.
+ - In your working directory, create a `.env` file with the constant `PYTHONPATH=$PYTHONPATH:path_to_packages_dir` where `path_to_packages_dir` is the path to the `packages` folder in your working directory.
- Or, automated:
-
- - Fork our template repo for AEA development. Then clone it to your machine.
-
+ - Fork our template repo for AEA development. Then clone it to your machine.
- Depending on your editor, you might take further steps:
-
- - VS Code: The Python Extension in VS Code can be configured to include additional paths in the Python path. The extension has a setting for `python.envFile` which specifies the path to a file containing environment variable definitions. The default is set to `"python.envFile": "${workspaceFolder}/.env"`. Provide the path to the `.env` file in the above settings. In the `.env` file, add the `PYTHONPATH` constant defined above. Then close VS Code and re-open it for the settings to take effect.
+ - VS Code: The Python Extension in VS Code can be configured to include additional paths in the Python path. The extension has a setting for `python.envFile` which specifies the path to a file containing environment variable definitions. The default is set to `"python.envFile": "${workspaceFolder}/.env"`. Provide the path to the `.env` file in the above settings. In the `.env` file, add the `PYTHONPATH` constant defined above. Then close VS Code and re-open it for the settings to take effect.
After developing a package, you can add it to an AEA project in the working directory (e.g. `aea create AGENT_NAME && cd AGENT_NAME && aea add --local PACKAGE_TYPE PUBLIC_ID` will create a new AEA project `AGENT_NAME` and add the package of type `PACKAGE_TYPE` with public id `PUBLIC_ID` to it.)
@@ -35,23 +28,16 @@ After developing a package, you can add it to an AEA project in the working dire
It is also possible to develop directly in an AEA project:
- Prepare a directory (henceforth working directory) for development.
-
- Create a new project `aea create AGENT_NAME && cd AGENT_NAME`
-
- Scaffold a new package `aea scaffold --with-symlinks PACKAGE_TYPE PACKAGE_NAME`. This will create the package scaffold under the directory `{PACKAGE_TYPE}s` and create symlinks to ensure package import paths line up with the folder structure. The symlinks are not needed to run the AEA. They are purely for your IDE.
-
- In your working directory, create a `.env` file with the constant `PYTHONPATH=$PYTHONPATH:path_to_project_dir` where `path_to_project_dir` is the path to the AEA project contained in your working directory.
-
- Depending on your editor, you might take further steps:
+ - VS Code: The Python Extension in VS Code can be configured to include additional paths in the Python path. The extension has a setting for `python.envFile` which specifies the path to a file containing environment variable definitions. The default is set to `"python.envFile": "${workspaceFolder}/.env"`. Provide the path to the `.env` file in the above settings. In the `.env` file, add the `PYTHONPATH` constant defined above. Then close VS Code and re-open it for the settings to take effect.
- - VS Code: The Python Extension in VS Code can be configured to include additional paths in the Python path. The extension has a setting for `python.envFile` which specifies the path to a file containing environment variable definitions. The default is set to `"python.envFile": "${workspaceFolder}/.env"`. Provide the path to the `.env` file in the above settings. In the `.env` file, add the `PYTHONPATH` constant defined above. Then close VS Code and re-open it for the settings to take effect.
-
-## General advice
+## General Advice
This advice partially overlaps with the previous two sections:
- When developing a specific AEA, it might be helpful to publish/push or fetch/add from local registry. From your working directory/AEA project, simply execute the usual AEA CLI commands. The CLI will first search in the `packages` directory, then in the remote AEA registry. You can explicitly point to local registry by providing flag `--local` or `--remote` to only point to remote registry (see here for more details on CLI commands).
-
-- When working on an AEA, it may help to provide a symbolic link to the packages directory, so that the import paths are detected by your editor. Simply create an empty file with `touch packages` in your AEA project, then create a symbolic link to the `packages` directory with `ln -s ../packages packages`.
-
+- When working on an AEA, it may help to provide a symbolic link to the `packages` directory, so that the import paths are detected by your editor. Simply create an empty file with `touch packages` in your AEA project, then create a symbolic link to the `packages` directory with `ln -s ../packages packages`.
- Alternatively, it can help to provide symbolic links within an AEA to align import paths with folder structure. Simply create an empty file with `touch packages` in your AEA project, then create a symbolic link to `ln -s vendor packages`.
diff --git a/docs/diagram.md b/docs/diagram.md
index 6c3c166aba..bf27cdd5de 100644
--- a/docs/diagram.md
+++ b/docs/diagram.md
@@ -1,3 +1,5 @@
+# Architectural Diagram
+
The framework has two distinctive parts.
- A **core** that is developed by the Fetch.ai team as well as external contributors.
@@ -5,7 +7,7 @@ The framework has two distinctive parts.
Currently, the framework supports four types of packages which can be added to the core as modules:
-- Skills encapsulate logic that deliver economic value to the AEA. Skills are the main focus of the framework's extensibility.
+- Skills encapsulate logic that deliver economic value to the AEA. Skills are the main focus of the framework's extensibility.
- Protocols define the structure of agent-to-agent and component-to-component interactions (messages and dialogues) for agents.
- Connections provide interfaces for the agent to connect with the outside world. They wrap SDKs or APIs and provide interfaces to networks, ledgers and other services.
- Contracts wrap smart contracts for Fetch.ai and third-party decentralized ledgers.
@@ -14,30 +16,25 @@ The following figure illustrates the framework's architecture:
-
The execution is broken down in more detail below:
The agent operation breaks down into three parts:
-* **Setup**: calls the `setup()` method of all registered resources
-* **Operation**:
- * Agent loop (Thread 1 - Asynchronous agent loop):
- * `react()`: this function grabs all Envelopes waiting in the `InBox` queue and calls the `handle()` method on the Handler(s) responsible for them.
- * `act()`: this function calls the `act()` method of all registered Behaviours.
- * `update()`: this function enqueues scheduled tasks for execution with the `TaskManager` and executes the decision maker.
- * Task loop (Thread 2- Synchronous): executes available tasks
- * Decision maker loop (Thread 3- Synchronous): processes internal messages
- * Multiplexer (Thread 4 - Asynchronous event loop): processes incoming and outgoing messages across several connections asynchronously.
-* **Teardown**: calls the `teardown()` method of all registered resources
-
+- **Setup**: calls the `setup()` method of all registered resources
+- **Operation**:
+ - Agent loop (Thread 1 - Asynchronous agent loop):
+ - `react()`: this function grabs all Envelopes waiting in the `InBox` queue and calls the `handle()` method on the Handler(s) responsible for them.
+ - `act()`: this function calls the `act()` method of all registered Behaviours.
+ - `update()`: this function enqueues scheduled tasks for execution with the `TaskManager` and executes the decision maker.
+ - Task loop (Thread 2- Synchronous): executes available tasks
+ - Decision maker loop (Thread 3- Synchronous): processes internal messages
+ - Multiplexer (Thread 4 - Asynchronous event loop): processes incoming and outgoing messages across several connections asynchronously.
+- **Teardown**: calls the `teardown()` method of all registered resources
To prevent a developer from blocking the main loop with custom skill code, an execution time limit is applied to every `Behaviour.act` and `Handler.handle` call.
By default, the execution limit is set to `0` seconds, which disables the feature. You can set the limit to a strictly positive value (e.g. `0.1` seconds) to test your AEA for production readiness. If the `act` or `handle` time exceed this limit, the call will be terminated.
An appropriate message is added to the logs in the case of some code execution being terminated.
-
-
-
diff --git a/docs/erc1155-skills.md b/docs/erc1155-skills.md
index 2ccb214906..f2c30d4a8f 100644
--- a/docs/erc1155-skills.md
+++ b/docs/erc1155-skills.md
@@ -1,10 +1,12 @@
+# Contract Deploy and Interact
+
The AEA `erc1155_deploy` and `erc1155_client` skills demonstrate an interaction between two AEAs which use a smart contract.
-* The `erc1155_deploy` skill deploys the smart contract, creates and mints items.
-* The `erc1155_client` skill signs a transaction to complete a trustless trade with its counterparty.
+- The `erc1155_deploy` skill deploys the smart contract, creates and mints items.
+- The `erc1155_client` skill signs a transaction to complete a trustless trade with its counterparty.
+
+## Preparation Instructions
-## Preparation instructions
-
### Dependencies
Follow the Preliminaries and Installation sections from the AEA quick start.
@@ -13,14 +15,12 @@ Follow the Preliminaries and
-
\ No newline at end of file
+```
diff --git a/docs/generic-skills-step-by-step.md b/docs/generic-skills-step-by-step.md
index 0e2eea4fe6..47bd479d56 100644
--- a/docs/generic-skills-step-by-step.md
+++ b/docs/generic-skills-step-by-step.md
@@ -1,3 +1,5 @@
+# Trade between Two AEAs
+
This guide is a step-by-step introduction to building AEAs that advertise their static and dynamic data, find other AEAs with required data, negotiate terms of trade, and carry out trades via ledger transactions.
If you simply want to run the resulting AEAs go here.
@@ -6,7 +8,7 @@ If you simply want to run the resulting AEAs go here
Follow the Preliminaries and Installation sections from the AEA quick start.
-## Reference code (Optional)
+## Reference Code (Optional)
This step-by-step guide goes through the creation of two AEAs which are already developed by Fetch.ai. You can get the finished AEAs, and compare your code against them, by following the next steps:
@@ -24,7 +26,7 @@ aea eject skill fetchai/generic_buyer:0.27.5
cd ..
```
-## Simplification step
+## Simplification Step
To keep file paths consistent with the reference code, we suggest you initialize your local author as `fetchai` for the purpose of this demo only:
@@ -37,12 +39,15 @@ aea init --reset --author fetchai
### Step 1: Create the AEA
Create a new AEA by typing the following command in the terminal:
+
``` bash
aea create my_generic_seller
cd my_generic_seller
aea install
```
+
Our newly created AEA is inside the current working directory. Let’s create our new skill that will handle the sale of data. Type the following command:
+
``` bash
aea scaffold skill generic_seller
```
@@ -55,7 +60,7 @@ The `scaffold skill` command creates the correct structure for a new skill insid
- `my_model.py`
- `skills.yaml`
-### Step 2: Create the behaviour
+### Step 2: Create the Behaviour
A `Behaviour` class contains the business logic specific to actions initiated by the AEA, rather than reactions to other events.
@@ -225,17 +230,17 @@ class GenericServiceRegistrationBehaviour(TickerBehaviour):
self.context.logger.info("unregistering agent from SOEF.")
```
-This `TickerBehaviour` registers (see `setup` method) and deregisters (see `teardown` method) our AEA’s service on the SOEF search node at regular tick intervals (here 60 seconds). By registering, the AEA becomes discoverable to other AEAs.
+This `TickerBehaviour` registers (see `setup` method) and de-registers (see `teardown` method) our AEA’s service on the SOEF search node at regular tick intervals (here 60 seconds). By registering, the AEA becomes discoverable to other AEAs.
In `setup`, prior to registrations, we send a message to the ledger connection to check the account balance for the AEA's address on the configured ledger.
-### Step 3: Create the handler
+### Step 3: Create the Handler
So far, we have tasked the AEA with sending register/unregister requests to the SOEF search node. However at present, the AEA has no way of handling the responses it receives from the search node, or in fact messages sent by any other AEA.
We have to specify the logic to negotiate with another AEA based on the strategy we want our AEA to follow. The following diagram illustrates the negotiation flow that we want this AEA to use, as well as interactions with a search node and the blockchain between a `seller_AEA` and a `buyer_AEA`.
-
+``` mermaid
sequenceDiagram
participant Search
participant Buyer_AEA
@@ -272,8 +277,7 @@ We have to specify the logic to negotiate with another AEA based on the strategy
deactivate Search
deactivate Seller_AEA
deactivate Blockchain
-
-
+```
In our case, `my_generic_seller` is the `Seller_AEA` in the above figure.
@@ -351,6 +355,7 @@ class GenericFipaHandler(Handler):
def teardown(self) -> None:
"""Implement the handler teardown."""
```
+
The code above contains the logic for handling `FipaMessages` received by the `my_generic_seller` AEA. We use `FipaDialogues` (more on this below) to keep track of the progress of the negotiation dialogue between the `my_generic_seller` AEA and the `my_generic_buyer` AEA.
In the above `handle` method, we first check if a received message belongs to an existing dialogue or if we have to create a new dialogue (the `recover dialogue` part). Once this is done, we break down the AEA's response to each type of negotiation message, as indicated by the message's performative (the `handle message` part). Therefore, we implement the AEA's response to each negotiation message type in a different handler function.
@@ -451,6 +456,7 @@ The next code-block handles the decline message we receive from the buyer. Add
FipaDialogue.EndState.DECLINED_PROPOSE, fipa_dialogue.is_self_initiated
)
```
+
If we receive a decline message from the buyer we close the dialogue and terminate this conversation with `my_generic_buyer`.
Alternatively, we might receive an `ACCEPT` message. In order to handle this option add the following code below the `_handle_decline` function:
@@ -485,7 +491,8 @@ Alternatively, we might receive an `ACCEPT` message. In order to handle this opt
self.context.outbox.put_message(message=match_accept_msg)
```
-When `my_generic_buyer` accepts the `Proposal` we send it and sends an `ACCEPT` message, we have to respond with another message (`MATCH_ACCEPT_W_INFORM`) to match the acceptance of the terms of trade and to inform the buyer of the address we would like it to send the funds to.
+
+When `my_generic_buyer` accepts the `Proposal` we send it and sends an `ACCEPT` message, we have to respond with another message (`MATCH_ACCEPT_W_INFORM`) to match the acceptance of the terms of trade and to inform the buyer of the address we would like it to send the funds to.
Lastly, we must handle an `INFORM` message, which the buyer uses to inform us that it has indeed sent the funds to the provided address. Add the following code at the end of the file:
@@ -556,9 +563,11 @@ Lastly, we must handle an `INFORM` message, which the buyer uses to inform us th
)
)
```
-In the above code, we check the `INFORM` message. If it contains a transaction digest, then we verify that the transaction matches the proposal the buyer accepted. If the transaction is valid and we received the funds then we send the data to the buyer. Otherwise, we do not send the data.
+
+In the above code, we check the `INFORM` message. If it contains a transaction digest, then we verify that the transaction matches the proposal the buyer accepted. If the transaction is valid and we received the funds, then we send the data to the buyer. Otherwise, we do not send the data.
The remaining handlers are as follows:
+
``` python
def _handle_invalid(
self, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue
@@ -876,7 +885,7 @@ class GenericOefSearchHandler(Handler):
The `GenericLedgerApiHandler` deals with `LedgerApiMessages` from the ledger connection and the `GenericOefSearchHandler` handles `OefSearchMessages` from the SOEF connection.
-### Step 4: Create the strategy
+### Step 4: Create the Strategy
Next, we are going to create the strategy that we want our `my_generic_seller` AEA to follow. Rename the `my_model.py` file (`my_generic_seller/skills/generic_seller/my_model.py`) to `strategy.py` and replace the stub code with the following:
@@ -1143,7 +1152,7 @@ The following properties and methods deal with different aspects of the strategy
The helper private function `collect_from_data_source` is where we read data from a sensor or if there are no sensor we use some default data provided (see the `data_for_sale` property).
-### Step 5: Create the dialogues
+### Step 5: Create the Dialogues
To keep track of the structure and progress of interactions, including negotiations with a buyer AEA and interactions with search nodes and ledgers, we use dialogues. Create a new file in the skill folder (`my_generic_seller/skills/generic_seller/`) and name it `dialogues.py`. Inside this file add the following code:
@@ -1397,7 +1406,7 @@ The `FipaDialogues` class contains negotiation dialogues with each `my_generic_b
The `FipaDialogues` class extends `BaseFipaDialogues`, which itself derives from the base `Dialogues` class. Similarly, the `FipaDialogue` class extends `BaseFipaDialogue` which itself derives from the base `Dialogue` class. To learn more about dialogues have a look here.
-### Step 6: Update the YAML files
+### Step 6: Update the YAML Files
Since we made so many changes to our AEA we have to update the `skill.yaml` (at `my_generic_seller/skills/generic_seller/skill.yaml`). Make sure you update your `skill.yaml` with the following configuration:
@@ -1484,7 +1493,6 @@ aea fingerprint skill fetchai/generic_seller:0.1.0
This will hash each file and save the hash in the fingerprint. This way, in the future we can easily track if any of the files have changed.
-
## Generic Buyer AEA
### Step 1: Create the AEA
@@ -1511,7 +1519,7 @@ This command creates the correct structure for a new skill inside our AEA projec
- `my_model.py`
- `skills.yaml`
-### Step 2: Create the behaviour
+### Step 2: Create the Behaviour
Open the `behaviours.py` file (`my_generic_buyer/skills/generic_buyer/behaviours.py`) and replace the stub code with the following:
@@ -1700,7 +1708,7 @@ class GenericTransactionBehaviour(TickerBehaviour):
This `TickerBehaviour` will send a search query to the SOEF search node at regular tick intervals.
-### Step 3: Create the handler
+### Step 3: Create the Handler
So far, the AEA is tasked with sending search queries to the SOEF search node. However, currently the AEA has no way of handling the responses it receives from the SOEF or messages from other agents.
@@ -1779,6 +1787,7 @@ class GenericFipaHandler(Handler):
def teardown(self) -> None:
"""Implement the handler teardown."""
```
+
You will see that we are following similar logic to the `generic_seller` when we develop the `generic_buyer`’s side of the negotiation. First, we create a new dialogue and store it in the dialogues class. Then we are checking what kind of message we received by checking its performative. So lets start creating our handlers:
``` python
@@ -1801,6 +1810,7 @@ You will see that we are following similar logic to the `generic_seller` when we
)
self.context.outbox.put_message(message=default_msg)
```
+
The above code handles messages referencing unidentified dialogues and responds with an error message to the sender. Next we will handle the `PROPOSE` message received from the `my_generic_seller` AEA:
``` python
@@ -1843,6 +1853,7 @@ The above code handles messages referencing unidentified dialogues and responds
)
self.context.outbox.put_message(message=decline_msg)
```
+
When we receive a proposal, we have to check if we have the funds to complete the transaction and if the proposal is acceptable based on our strategy. If the proposal is not affordable or acceptable, we respond with a `DECLINE` message. Otherwise, we send an `ACCEPT` message to the seller.
The next code-block handles the `DECLINE` message that we may receive from the seller as a response to our `CFP` or `ACCEPT` messages:
@@ -1880,7 +1891,8 @@ The next code-block handles the `DECLINE` message that we may receive from the s
FipaDialogue.EndState.DECLINED_ACCEPT, fipa_dialogue.is_self_initiated
)
```
-The above code terminates each dialogue with the specific AEA and stores the state of the terminated dialogue (whether it was terminated after a `CFP` or an `ACCEPT`).
+
+The above code terminates each dialogue with the specific AEA and stores the state of the terminated dialogue (whether it was terminated after a `CFP` or an `ACCEPT`).
If `my_generic_seller` AEA wants to move on with the sale, it will send a `MATCH_ACCEPT` message. In order to handle this we add the following code:
@@ -1922,7 +1934,8 @@ def _handle_match_accept(
"informing counterparty={} of payment.".format(fipa_msg.sender[-5:])
)
```
-The first thing we are checking is if we enabled our AEA to transact with a ledger. If so, we add this negotiation to the queue of transactions to be processed. If not, we simulate non-ledger payment by sending an inform to the seller that the payment is done (say via bank transfer).
+
+The first thing we are checking is if we enabled our AEA to transact with a ledger. If so, we add this negotiation to the queue of transactions to be processed. If not, we simulate non-ledger payment by sending an `inform` to the seller that the payment is done (say via bank transfer).
Lastly, we need to handle `INFORM` messages. This is the message that will have our data:
@@ -2101,7 +2114,8 @@ class GenericOefSearchHandler(Handler):
)
)
```
-When we receive a message from the SOEF search node of a type `OefSearchMessage.Performative.SEARCH_RESULT`, we are passing the details to the relevant handler method. In the `_handle_search` function we are checking that the response contains some agents and we stop the search if it does. We pick our first agent and we send a `CFP` message.
+
+When we receive a message from the SOEF search node of a type `OefSearchMessage.Performative.SEARCH_RESULT`, we are passing the details to the relevant handler method. In the `_handle_search` function, we are checking that the response contains some agents, and we stop the search if it does. We pick our first agent and send a `CFP` message.
The last handlers we need are the `GenericSigningHandler` and the `GenericLedgerApiHandler`. These handlers are responsible for `SigningMessages` that we receive from the `DecisionMaker`, and `LedgerApiMessages` that we receive from the ledger connection, respectively.
@@ -2443,7 +2457,7 @@ class GenericLedgerApiHandler(Handler):
)
```
-### Step 4: Create the strategy
+### Step 4: Create the Strategy
We are going to create the strategy that we want our AEA to follow. Rename the `my_model.py` file (in `my_generic_buyer/skills/generic_buyer/`) to `strategy.py` and replace the stub code with the following:
@@ -2647,7 +2661,7 @@ The following code block checks if the proposal that we received is acceptable a
return result
```
-The `is_affordable_proposal` method in the following code block checks if we can afford the transaction based on the funds we have in our wallet on the ledger. The rest of the methods are self-explanatory.
+The `is_affordable_proposal` method in the following code block checks if we can afford the transaction based on the funds we have in our wallet on the ledger. The rest of the methods are self-explanatory.
``` python
def is_affordable_proposal(self, proposal: Description) -> bool:
@@ -2720,7 +2734,7 @@ The `is_affordable_proposal` method in the following code block checks if we can
"""Update agent location and query for search."""
```
-### Step 5: Create the dialogues
+### Step 5: Create the Dialogues
As mentioned during the creation of the seller AEA, we should keep track of the various interactions an AEA has with others and this is done via dialogues. Create a new file and name it `dialogues.py` (in `my_generic_buyer/skills/generic_buyer/`). Inside this file add the following code:
@@ -3059,7 +3073,7 @@ class SigningDialogues(Model, BaseSigningDialogues):
The various dialogues classes in the above code snippet store dialogues with other AEAs, services and components, (e.g. SOEF search node via the `fetchai/soef` connection, ledgers via the `fetchai/ledger` connection and the decision maker). They expose useful methods to manipulate these interactions, access previous messages, and enable us to identify possible communications problems between `my_generic_seller` and `my_generic_buyer` AEAs.
-### Step 6: Update the YAML files
+### Step 6: Update the YAML Files
After making so many changes to our skill, we have to update the `skill.yaml` configuration file so it reflects our newly created classes, and contains the values used by the strategy. Make sure `skill.yaml` contains the following configuration:
@@ -3149,7 +3163,8 @@ models:
is_abstract: false
dependencies: {}
```
-We must pay attention to the models and the strategy’s variables. Here we can change the price we would like to buy each reading at, the maximum transaction fee we are prepared to pay, and so on.
+
+We must pay attention to the models and the strategy’s variables. Here we can change the price we would like to buy each reading at, the maximum transaction fee we are prepared to pay, and so on.
Finally, we fingerprint our new skill:
@@ -3161,28 +3176,32 @@ This will hash each file and save the hash in the fingerprint. This way, in the
## Run the AEAs
-### Create private keys
+### Create Private Keys
For each AEA, create a private key:
+
``` bash
aea generate-key fetchai
aea add-key fetchai fetchai_private_key.txt
```
Next, create a private key to secure the AEA's communications:
+
``` bash
aea generate-key fetchai fetchai_connection_private_key.txt
aea add-key fetchai fetchai_connection_private_key.txt --connection
```
Finally, certify the key for use by the connections that request that:
+
``` bash
aea issue-certificates
```
-### Update the AEA configurations
+### Update the AEA Configurations
In both AEAs run:
+
``` bash
aea config set --type dict agent.default_routing \
'{
@@ -3191,7 +3210,7 @@ aea config set --type dict agent.default_routing \
}'
```
-### Fund the buyer AEA
+### Fund the Buyer AEA
Create some wealth for your buyer on the Fetch.ai testnet (this operation might take a while).
@@ -3199,7 +3218,7 @@ Create some wealth for your buyer on the Fetch.ai testnet (this operation might
aea generate-wealth fetchai --sync
```
-### Run seller AEA
+### Run Seller AEA
Add the remaining packages for the seller AEA, then run it:
@@ -3216,7 +3235,7 @@ aea run
Once you see a message of the form `To join its network use multiaddr: ['SOME_ADDRESS']` take note of the address.
-#### Run buyer AEA
+#### Run Buyer AEA
Add the remaining packages for the buyer AEA:
@@ -3247,6 +3266,7 @@ aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
where `SOME_ADDRESS` is replaced accordingly.
Then run the buyer AEA:
+
``` bash
aea run
```
@@ -3256,20 +3276,19 @@ You will see that the AEAs negotiate and then transact using the Dorado testnet.
## Delete the AEAs
When you are done, go up a level and delete the AEAs.
+
``` bash
cd ..
aea delete my_generic_seller
aea delete my_generic_buyer
```
-## Next steps
+## Next Steps
You have completed the "Getting Started" series. Congratulations!
The following guide provides some hints on AEA development setup.
-### Recommended
+### Recommended
We recommend you build your own AEA next. There are many helpful guides and demos in the documentation, and a developer community on Discord. Speak to you there!
-
-
diff --git a/docs/generic-skills.md b/docs/generic-skills.md
index 3edd084053..2d78f1bf6a 100644
--- a/docs/generic-skills.md
+++ b/docs/generic-skills.md
@@ -1,11 +1,13 @@
+# Generic Skills
+
The AEA generic buyer and seller skills demonstrate an interaction between two AEAs:
-* An AEA that provides a (data selling) service.
-* An AEA that demands this service.
+- An AEA that provides a (data selling) service.
+- An AEA that demands this service.
## Discussion
-The scope of this guide is demonstrating how to create easily configurable AEAs. The buyer AEA finds the seller, negotiates the terms of trade, and if successful purchases the data by sending payment. The seller AEA sells the service specified in its `skill.yaml` file, delivering it to the buyer upon receiving payment.
+The scope of this guide is demonstrating how to create easily configurable AEAs. The buyer AEA finds the seller, negotiates the terms of trade, and if successful purchases the data by sending payment. The seller AEA sells the service specified in its `skill.yaml` file, delivering it to the buyer upon receiving payment.
Note that these agents do not utilize a smart contract but interact with a ledger to complete a transaction. Moreover, in this setup, the buyer agent has to trust the seller to send the data upon successful payment.
@@ -15,7 +17,7 @@ The corresponding packages can be customised to allow for a database or sensor t
The following diagram shows the communication between various entities in this interaction.
-
+``` mermaid
sequenceDiagram
participant Search
participant Buyer_AEA
@@ -42,21 +44,21 @@ The following diagram shows the communication between various entities in this i
deactivate Buyer_AEA
deactivate Search
deactivate Seller_AEA
- deactivate Blockchain
-
-
+ deactivate Blockchain
+```
+
+## Preparation Instructions
-## Preparation instructions
-
### Dependencies
Follow the Preliminaries and Installation sections from the AEA quick start.
-## Demo instructions
+## Demo Instructions
-### Create the seller AEA
+### Create the Seller AEA
First, fetch the seller AEA:
+
``` bash
aea fetch fetchai/generic_seller:0.29.4 --alias my_seller_aea
cd my_seller_aea
@@ -64,125 +66,125 @@ aea install
aea build
```
-Alternatively, create from scratch.
-
-
-
-
-### Add keys for the seller AEA
+??? note "Alternatively, create from scratch:"
+ The following steps create the buyer from scratch:
+
+ ``` bash
+ aea create my_buyer_aea
+ cd my_buyer_aea
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/generic_buyer:0.27.5
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
+ }'
+ aea install
+ aea build
+ ```
+
+### Add Keys for the Seller AEA
Create the private key for the seller AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai `Dorado` use:
+
``` bash
aea generate-key fetchai
aea add-key fetchai fetchai_private_key.txt
```
Next, create a private key used to secure the AEA's communications:
+
``` bash
aea generate-key fetchai fetchai_connection_private_key.txt
aea add-key fetchai fetchai_connection_private_key.txt --connection
```
Finally, certify the key for use by the connections that request that:
+
``` bash
aea issue-certificates
```
-### Add keys and generate wealth for the buyer AEA
+### Add Keys and Generate Wealth for the Buyer AEA
The buyer needs to have some wealth to purchase the data from the seller.
First, create the private key for the buyer AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai `Dorado` use:
+
``` bash
aea generate-key fetchai
aea add-key fetchai fetchai_private_key.txt
```
Then, create some wealth for your buyer based on the network you want to transact with. On the Fetch.ai `Dorado` network:
+
``` bash
aea generate-wealth fetchai
```
Next, create a private key used to secure the AEA's communications:
+
``` bash
aea generate-key fetchai fetchai_connection_private_key.txt
aea add-key fetchai fetchai_connection_private_key.txt --connection
```
Finally, certify the key for use by the connections that request that:
+
``` bash
aea issue-certificates
```
-### Update the skill configurations
+### Update the Skill Configurations
The default skill configurations assume that the transaction is settled against the Fetch.ai ledger.
In the generic seller's skill configuration file (`my_seller_aea/vendor/fetchai/skills/generi_seller/skill.yaml`) the `data_for_sale` is the data the seller AEA is offering for sale. In the following case, this is a one item dictionary where key is `generic` and value is `data`.
-Furthermore, the `service_data` is used to register the seller's service in the SOEF search node and make your agent discoverable.
+Furthermore, the `service_data` is used to register the seller's service in the SOEF search node and make your agent discoverable.
+
``` yaml
models:
...
@@ -230,7 +232,7 @@ models:
class_name: GenericStrategy
```
-### Update the skill configurations
+### Update the Skill Configurations
Both skills are abstract skills, make them instantiable:
@@ -256,6 +258,7 @@ Once you see a message of the form `To join its network use multiaddr 'SOME_ADDR
This is the entry peer address for the local agent communication network created by the seller.
Then, configure the buyer to connect to this same local ACN by running the following command in the buyer terminal, replacing `SOME_ADDRESS` with the value you noted above:
+
``` bash
aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
'{
@@ -279,8 +282,8 @@ You will see that the AEAs negotiate and then transact using the Fetch.ai testne
When you're done, stop the agents (`CTRL+C`), go up a level and delete the AEAs.
-``` bash
+``` bash
cd ..
aea delete my_seller_aea
aea delete my_buyer_aea
-```
\ No newline at end of file
+```
diff --git a/docs/generic-storage.md b/docs/generic-storage.md
index cfd9640f5f..ecce155224 100644
--- a/docs/generic-storage.md
+++ b/docs/generic-storage.md
@@ -1,41 +1,45 @@
+# Generic Storage
+
The AEA generic storage: description and usage.
## AEA Generic Storage
+
AEA generic storage allows AEA skill's components to store data permanently and use it any time.
The primary scenario: to save AEA data on shutdown and load back on startup.
Generic storage provides an API for general data manipulation in key-object style.
-
## Configuration
+
Storage is enabled by providing in the agent configuration (`aea-config.yaml`) an optional `storage_uri`. The storage URI consists of the backend name and string data provided to selected backend.
The storage URI schema is `://[Optional string]`
Example: `storage_uri: sqlite://./some_file.db` tells the AEA to use SQLite backend and store data in `./some_file.db`.
Supported backends:
-* SQLite - bundled with python simple SQL engine that uses file or in-memory storage.
-## Dialogues and Storage integration
+- SQLite - bundled with python simple SQL engine that uses file or in-memory storage.
+
+## Dialogues and Storage Integration
One of the most useful cases is the integration of the dialogues subsystem and storage. It helps maintain dialogues state during agent restarts and reduced memory requirements due to the offloading feature.
-### Keep terminal state dialogues
+### Keep Terminal State Dialogues
The Dialogues class has the optional boolean argument `keep_terminal_state_dialogues`
-which specifies whether a dialogue which has reached its terminal state is kept in memory or not. If `keep_terminal_state_dialogues` is `False`, dialogues that reach a terminal state are removed from memory and can not be used any more. If `keep_terminal_state_dialogues` is `True`, dialogues that reach a terminal state are kept in memory or storage (if configured). If storage is configured, all dialogues in memory are stored on agent stop and restored on agent start.
+which specifies whether a dialogue which has reached its terminal state is kept in memory or not. If `keep_terminal_state_dialogues` is `False`, dialogues that reach a terminal state are removed from memory and can not be used anymore. If `keep_terminal_state_dialogues` is `True`, dialogues that reach a terminal state are kept in memory or storage (if configured). If storage is configured, all dialogues in memory are stored on agent stop and restored on agent start.
-It useful to save memory with dialogues that are in terminal state and probably will be never used again.
+It is useful to save memory with terminated dialogues that will (possibly) be never used again.
Default behaviour on keep terminals state dialogues is set according to the protocol specification but can be set explicitly with skill configuration section.
-
Skill configuration to keep terminated dialogues for `DefaultDialogues`.
Example:
-### Dialogues dump/restore on agent restart
-If storage is enabled then all the dialogues present in memory will be stored on agent's teardown and loaded on agent's start.
+### Dialogues Dump/Restore on Agent Restart
-### Offload terminal state dialogues
+If storage is enabled then all the dialogues present in memory will be stored on agent's teardown and loaded on agent's start.
+
+### Offload Terminal State Dialogues
If keep options is set and storage is available dialogues in terminal state will be dumped to generic storage and removed from memory. This option helps to save memory and handle terminated dialogues with the same functionality as when they are kept in memory.
@@ -43,8 +47,8 @@ All the active dialogues will be stored and loaded during agent restart. All the
To enable dialogues offloading `keep_terminal_state_dialogues` has to be enabled and storage configured.
+## Manual Usage with Skill Components
-## Manual usage with skill components
Handlers, Behaviours and Models are able to use storage if enabled.
Storage is available with skill context: `self.context.storage`
@@ -56,14 +60,15 @@ Objects consist of the `object_id` (unique string) and object body. The object b
Collection is a group of the objects, objects data types can vary in the same collection.
Collection name is name consists of letters, numbers and `_`.
-
To get/put specific object collection instance should be used.
+
``` python
my_collection = self.context.storage.get_sync_connection('my_collection')
```
Collection instance provide set of methods to handle data objects.
List of collection methods:
+
``` python
def put(self, object_id: str, object_body: JSON_TYPES) -> None:
"""
@@ -110,11 +115,10 @@ List of collection methods:
"""
```
-
-
Simple behaviour example:
It saves the `datetime` string of the first act and print it to stdout.
+
``` python
class TestBehaviour(TickerBehaviour):
"""Simple behaviour to count how many acts were called."""
@@ -125,14 +129,14 @@ class TestBehaviour(TickerBehaviour):
def act(self) -> None:
"""Make an action."""
if not (self.context.storage and self.context.storage.is_connected):
- return
+ return
collection = self.context.storage.get_sync_collection('my_collection')
first_call_datetime = collection.get("first_call_ts")
if not first_call_ts:
# there is no object with "first_call_ts" id.
first_call_datetime = str(datetime.datetime.now())
- col.put(first_call_ts, first_call_datetime)
- print("Act was called for the first time on:", first_call_datetime)
+ col.put(first_call_ts, first_call_datetime)
+ print("Act was called for the first time on:", first_call_datetime)
```
Please, pay attention: `datetime` object is not JSON friendly and can not be stored directly. it should be transformed to `timestamp` or string before put into the storage.
diff --git a/docs/glossary.md b/docs/glossary.md
index 24b96a4f7b..7781323cea 100644
--- a/docs/glossary.md
+++ b/docs/glossary.md
@@ -1,9 +1,11 @@
+# Glossary
+
This glossary defines a number of terms commonly used across the documentation. For the definitions of framework components consult the API docs.
-* **AEA (Autonomous Economic Agent)**: An AEA is "an intelligent agent acting on an owner's behalf, with limited or no interference, and whose goal is to generate economic value to its owner". AEAs are a special type of agent. [more]
+- **AEA (Autonomous Economic Agent)**: An AEA is "an intelligent agent acting on an owner's behalf, with limited or no interference, and whose goal is to generate economic value to its owner". AEAs are a special type of agent. [more]
-* **Software Agent**: a software agent is a computer program that acts on behalf of an entity (e.g. individual, organisation, business). [more]
+- **Software Agent**: a software agent is a computer program that acts on behalf of an entity (e.g. individual, organisation, business). [more]
-* **sOEF (Simple Open Economic Framework)**: The simple-OEF, or sOEF, is a search and discovery service for autonomous economic agents. [more]
+- **sOEF (Simple Open Economic Framework)**: The simple-OEF, or sOEF, is a search and discovery service for autonomous economic agents. [more]
-* **ACN (Agent Communication Network)**: The ACN is a peer-to-peer communication network for autonomous economic agents. [more]
\ No newline at end of file
+- **ACN (Agent Communication Network)**: The ACN is a peer-to-peer communication network for autonomous economic agents. [more]
diff --git a/docs/gym-example.md b/docs/gym-example.md
index 25b6516cbf..fab789cedc 100644
--- a/docs/gym-example.md
+++ b/docs/gym-example.md
@@ -1,18 +1,21 @@
+# Gym Example
+
The `gym` example demonstrates the AEA framework's flexibility with respect to Reinforcement Learning using OpenAI's `gym` framework.
-### Discussion
+## Discussion
-There is no immediate use case for this example as you can train an RL agent without the AEA proxy layer just fine (and faster).
+There is no immediate use case for this example as you can train an RL agent without the AEA proxy layer just fine (and faster).
However, the example decouples the RL agent from the `gym.Env` allowing them to run in separate execution environments, potentially owned by different entities.
-## Preparation instructions
+## Preparation Instructions
### Dependencies
Follow the Preliminaries and Installation sections from the AEA quick start.
Download the necessary directories into your working directory:
+
``` bash
svn export https://github.com/fetchai/agents-aea.git/trunk/examples
svn export https://github.com/fetchai/agents-aea.git/trunk/packages
@@ -24,15 +27,15 @@ Install the `gym` and `numpy` library.
pip install numpy gym
```
-## Demo instructions
+## Demo Instructions
-### Run the example
+### Run the Example
``` bash
python examples/gym_ex/train.py
```
-Notice the usual RL setup, i.e. the fit method of the RL agent has the typical signature and a familiar implementation.
+Notice the usual RL setup, i.e. the fit method of the RL agent has the typical signature and a familiar implementation.
Note how `train.py` demonstrates how easy it is to use an AEA agent as a proxy layer between an OpenAI `gym.Env` and a standard RL agent.
@@ -59,5 +62,3 @@ if __name__ == "__main__":
rl_agent = RLAgent(nb_goods=NB_GOODS)
rl_agent.fit(env=proxy_env, nb_steps=NB_STEPS)
```
-
-
diff --git a/docs/gym-skill.md b/docs/gym-skill.md
index 882ef0a4c5..cc41d3d347 100644
--- a/docs/gym-skill.md
+++ b/docs/gym-skill.md
@@ -1,12 +1,13 @@
+# Gym Skill
+
The AEA gym skill demonstrates how a custom Reinforcement Learning agent, that uses OpenAI's gym library, may be embedded into an AEA skill and connection.
-### Discussion
+## Discussion
The gym skills demonstrate how to wrap a Reinforcement Learning agent in a skill.
The example decouples the RL agent from the `gym.Env` allowing them to run in separate execution environments, potentially owned by different entities.
-
-## Preparation instructions
+## Preparation Instructions
### Dependencies
@@ -25,69 +26,72 @@ Install the `gym` and `numpy` library.
pip install numpy gym
```
-## Demo instructions
-
+## Demo Instructions
### Create the AEA
First, fetch the gym AEA:
+
``` bash
aea fetch fetchai/gym_aea:0.26.4 --alias my_gym_aea
cd my_gym_aea
aea install
```
-Alternatively, create from scratch.
-
+??? note "Alternatively, create from scratch:"
-### Create the AEA
-In the root directory, create the gym AEA and enter the project.
-``` bash
-aea create my_gym_aea
-cd my_gym_aea
-```
+ ### Create the AEA
-### Add the gym skill
-``` bash
-aea add skill fetchai/gym:0.21.5
-```
+ In the root directory, create the gym AEA and enter the project.
-### Set gym connection as default
-``` bash
-aea config set agent.default_connection fetchai/gym:0.20.5
-```
+ ``` bash
+ aea create my_gym_aea
+ cd my_gym_aea
+ ```
-### Install the skill dependencies
+ ### Add the gym skill
-To install the `gym` package, a dependency of the gym skill, from PyPI run
-``` bash
-aea install
-```
+ ``` bash
+ aea add skill fetchai/gym:0.21.5
+ ```
+
+ ### Set gym connection as default
+
+ ``` bash
+ aea config set agent.default_connection fetchai/gym:0.20.5
+ ```
+
+ ### Install the skill dependencies
+
+ To install the `gym` package, a dependency of the gym skill, from PyPI run
+
+ ``` bash
+ aea install
+ ```
-
-
+### Set up the Training Environment
-### Set up the training environment
+#### Copy the Gym Environment to the AEA Directory
-#### Copy the gym environment to the AEA directory
``` bash
mkdir gyms
cp -a ../examples/gym_ex/gyms/. gyms/
```
-#### Update the connection configuration
+#### Update the Connection Configuration
+
``` bash
aea config set vendor.fetchai.connections.gym.config.env 'gyms.env.BanditNArmedRandom'
```
-#### Create and add a private key
+#### Create and Add a Private Key
``` bash
aea generate-key fetchai
aea add-key fetchai
```
-### Run the AEA with the gym connection
+### Run the AEA with the Gym Connection
``` bash
aea run
@@ -95,10 +99,8 @@ aea run
You will see the gym training logs.
-
-
### Delete the AEA
When you're done, you can go up a level and delete the AEA.
@@ -109,9 +111,10 @@ aea delete my_gym_aea
```
## Communication
+
This diagram shows the communication between the AEA and the gym environment
-
+``` mermaid
sequenceDiagram
participant AEA
participant Environment
@@ -127,7 +130,7 @@ This diagram shows the communication between the AEA and the gym environment
deactivate AEA
deactivate Environment
-
+```
## Skill Architecture
@@ -142,5 +145,3 @@ In this particular skill, which chiefly serves for demonstration purposes, we im
The illustration shows how the RL agent only interacts with the proxy environment by sending it `action (A)` and receiving `observation (O)`, `reward (R)`, `done (D)` and `info (I)`.
-
-
diff --git a/docs/http-connection-and-skill.md b/docs/http-connection-and-skill.md
index cd3ed4f14f..676b818527 100644
--- a/docs/http-connection-and-skill.md
+++ b/docs/http-connection-and-skill.md
@@ -1,10 +1,12 @@
+# HTTP Connection
+
## Description
-The HTTP client and HTTP server connections enable an AEA to communicate with external servers, respectively clients, via HTTP.
+The HTTP client and HTTP server connections enable an AEA to communicate with external servers, respectively clients, via HTTP.
The HTTP client connection receives request envelops from an agent's skill, translates each into an HTTP request and sends it to a server external to the agent. If it receives an HTTP response from the server within a timeout window, it translates it into a response envelope, and sends this back to the relevant skill inside the agent.
-The HTTP server connection allows you to run a server inside the connection itself which accepts requests from clients external to the agent. The HTTP server connection validates requests it receives against a provided OpenAPI file. It translates each valid request into an envelope and sends it to the skill specified in the connections configuration. If it receives a valid response envelope from the skill within a timeout window, the connection translates the response envelope into an HTTP response and serves it to the client.
+The HTTP server connection allows you to run a server inside the connection itself which accepts requests from clients external to the agent. The HTTP server connection validates requests it receives against a provided OpenAPI file. It translates each valid request into an envelope and sends it to the skill specified in the `connections` configuration. If it receives a valid response envelope from the skill within a timeout window, the connection translates the response envelope into an HTTP response and serves it to the client.
## HTTP Client
@@ -12,7 +14,7 @@ The `fetchai/simple_data_request:0.14.5` skill demonstrates a simple use case of
The `HttpRequestBehaviour` in `behaviours.py` periodically sends HTTP envelops to the HTTP client connection. Its `act()` method, periodically called, simply calls `_generate_http_request` which contains the logic for enqueueing an HTTP request envelop.
-The `HttpHandler` in `handler.py` is a basic handler for dealing with HTTP response envelops received from the HTTP client connection. In the `handle()` method, the responses are dealt with by the private `_handle_response` method which essentially logs the response and adds the body of the response into the skill's shared state.
+The `HttpHandler` in `handler.py` is a basic handler for dealing with HTTP response envelops received from the HTTP client connection. In the `handle()` method, the responses are dealt with by the private `_handle_response` method which essentially logs the response and adds the body of the response into the skill's shared state.
## HTTP Server
@@ -64,15 +66,11 @@ aea scaffold skill http_echo
You can implement a simple http echo skill (modelled after the standard echo skill) which prints out the content of received messages and responds with success.
-
First, delete the `my_model.py` and `behaviour.py` files (in `my_aea/skills/http_echo/`). The server will be purely reactive, so you only need the `handlers.py` file, and the `dialogues.py` to record the state of the dialogues. Update `skill.yaml` accordingly, so set `models: {}` and `behaviours: {}`.
Next implement a basic handler which prints the received envelopes and responds.
-
-Then, replace the content of `handlers.py` with the following code snippet,
-after having replaced the placeholder `YOUR_USERNAME` with
-the author username (i.e. the output of `aea config get agent.author`):
+Then, replace the content of `handlers.py` with the following code snippet, after having replaced the placeholder `YOUR_USERNAME` with the author username (i.e. the output of `aea config get agent.author`):
``` python
import json
@@ -217,6 +215,7 @@ class HttpHandler(Handler):
```
Moreover, add a `dialogues.py` file with the following code:
+
``` python
from typing import Any
@@ -314,13 +313,12 @@ models:
class_name: HttpDialogues
```
-
Run the fingerprinter (note, you will have to replace the author name with your author handle):
+
``` bash
aea fingerprint skill fetchai/http_echo:0.21.5
```
-
Moreover, we need to tell to the `http_server` connection to what skill
the HTTP requests should be forwarded.
In our case, this is the `http_echo` that you have just scaffolded.
@@ -331,11 +329,13 @@ aea config set vendor.fetchai.connections.http_server.config.target_skill_id "$(
```
You can now run the AEA:
+
``` bash
aea run
```
In a separate terminal, you can create a client and communicate with the server:
+
``` python
import requests
diff --git a/docs/identity.md b/docs/identity.md
index 82ff28c31d..1cd87f4c67 100644
--- a/docs/identity.md
+++ b/docs/identity.md
@@ -1,8 +1,7 @@
-
-
Note
-
This section is incomplete and will soon be updated.
-
-
+# Identity
+
+!!! note
+ This section is incomplete and will soon be updated.
The AEAs currently use the addresses associated with their private-public key pairs to identify themselves.
diff --git a/docs/index.md b/docs/index.md
index f852eab46f..ab90cd3825 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,27 +1,31 @@
+# AEA Framework Documentation
The AEA framework provides the tools for creating Autonomous Economic Agents (AEA).
-## What are AEAs?
+## Autonomous Economic Agents (AEAs)
-We define an autonomous economic agent or AEA as:
+
-> An intelligent agent acting on an owner's behalf, with limited or no interference, and whose goal is to generate economic value for its owner.
+### What is an AEA?
-
+!!! info "Definition"
+ An Autonomous Economic Agent (AEA) is an intelligent agent that acts on its owner's behalf, with limited or no interference, and whose goal is to generate economic value for its owner.
-An AEA represents an individual, organisation or object and looks after its interests. AEAs act independently of constant input from their owner and autonomously execute actions to achieve their prescribed goals. Their purpose is to create economic value for you, their owner, in clearly defined domains. AEAs have a wide range of application areas and we provide demo guides to highlight examples of their use cases.
+- **Agent**: An AEA represents an individual, organisation or object and looks after their interests.
+- **Autonomous**: AEAs act independently of constant input from their owner and autonomously execute actions to achieve their prescribed goals.
+- **Economic**: Their purpose is to create economic value for their owner, in clearly defined domains.
+
+AEAs have a wide range of application areas and we provide demo guides to highlight examples of their use cases.
### What is not an AEA
-* Any agent: AEAs' purpose is to generate economic value in a multi-stakeholder environment with competing incentives between agents. They represent humans, organisations or objects.
-* APIs or sensors which do not have agency.
-* Smart contracts which do not display any proactiveness and are purely reactive to external requests (=contract calls and transactions).
-* Artificial General Intelligence (AGI). AEAs can have a very narrow, goal directed focus involving some economic gain and can have a very simple logic.
+- Any agent: AEAs' purpose is to generate economic value in a multi-stakeholder environment with competing incentives between agents. They represent humans, organisations or objects.
+- APIs or sensors which do not have agency.
+- Smart contracts which do not display any proactivity and are purely reactive to external requests (=contract calls and transactions).
+- Artificial General Intelligence (AGI). AEAs can have a very narrow, goal directed focus involving some economic gain and can have a very simple logic.
-
-
Note
-
In the rest of the documentation, unless specified otherwise, we use the terms AEA and agent interchangeably to refer to AEA as defined above.
-
+!!! note
+ In the rest of the documentation, unless specified otherwise, we use the terms AEA and agent interchangeably to refer to AEA as defined above.
## What is the AEA Framework?
@@ -29,19 +33,17 @@ The AEA framework is a development suite, currently implemented in Python, which
AEAs achieve their goals with the help of a search & discovery service for AEAs -- the simple Open Economic Framework (sOEF) -- a decentralized agent communication system -- the Agent Communication Network (ACN) -- and using Fetch.ai's blockchain as a financial settlement and commitment layer. AEAs can also be integrated with third-party blockchains, such as Ethereum.
-
-## Why build with the AEA Framework?
+## Why Build with the AEA Framework?
The AEA framework provides the developer with a number of features, which combined cannot be found anywhere else:
-* The peer-to-peer agent communication network (ACN) allows your AEAs to interact with all other AEAs over the public internet.
-* The search and discovery system sOEF allows your AEAs to find other AEAs.
-* The AEA registry enables code sharing and re-use by providing a space in which AEAs or their individual components may be shared.
-* The framework's crypto and ledger APIs make it possible for AEAs to interact with blockchains.
-* The contract packages enable AEAs to interact with smart contracts in Fetch.ai and other third-party decentralised ledgers.
+- The peer-to-peer agent communication network (ACN) allows your AEAs to interact with all other AEAs over the public internet.
+- The search and discovery system sOEF allows your AEAs to find other AEAs.
+- The AEA registry enables code sharing and re-use by providing a space in which AEAs or their individual components may be shared.
+- The framework's crypto and ledger APIs make it possible for AEAs to interact with blockchains.
+- The contract packages enable AEAs to interact with smart contracts in Fetch.ai and other third-party decentralized ledgers.
-
-## Next steps
+## Next Steps
To get started developing your own AEA, check out the getting started section.
@@ -51,12 +53,7 @@ If you would like to develop an AEA in a language different to Python then check
If you want to run a demo, check out the demo guides.
+## Help us Improve
-## Help us improve
-
-
-
Note
-
This developer documentation is a work in progress. If you spot any errors please open an issue on Github or contact us in the developer Discord channel.
-
-
-
+!!! note
+ This developer documentation is a work in progress. If you spot any errors please open an issue on Github or contact us in the developer Discord channel.
diff --git a/docs/interaction-protocol.md b/docs/interaction-protocol.md
index 0bc3b52293..aa0df4dd45 100644
--- a/docs/interaction-protocol.md
+++ b/docs/interaction-protocol.md
@@ -1,3 +1,5 @@
+# How AEAs Talk to Each Other - Interaction Protocols
+
Although one can imagine scenarios where single AEAs pursue their goals in isolation without interacting with other AEAs, there is no doubt that by working together, AEAs have the potential of achieving much more, especially when taking into account agents' heterogeneity, specialisations, and differing and often complimentary local views of the environment.
Interactions in the AEA world are in the form of communication. This is influenced by established practices in the field of multi-agent systems and the prominent speech-act theory which suggests that a communicative expression is not only about transferring information from the speaker to the hearer, but that there may be meanings and commitments beyond the statement's appearance. Therefore, speech may more suitably be considered as action. For example, "I hereby appoint you as chairman" is not just a sequence of words, but an action done by the speaker with wide-ranging consequences for the hearer and any other audience to that sentence.
@@ -10,36 +12,35 @@ There are multiple types of interactions an AEA can have:
- Interactions between an AEA's internal components.
-
-
Usually, an interaction involves three types of framework packages: skills, protocols and connections.
-### Example 1: negotiation
+## Examples
+
+### Example 1: Negotiation
The generic buyer/seller skills use the `fetchai/fipa` protocol which defines the negotiation dialogue between two AEAs. The `fetchai/generic_buyer` and `fetchai/generic_seller` skills implement specific strategies for engaging in such negotiations, by providing the logic for producing negotiation messages to be sent, handling negotiation messages received. The `fetchai/p2p_libp2p` connection is then used for connecting to the agent communication network enabling two AEAs with these skills to deliver negotiation messages to each other.
-### Example 2: AEA <> web client
+### Example 2: AEA <> Web Client
-In the http connection guide we demonstrate how an AEA with an http server connection (e.g. `fetchai/http_server`) receives http payloads from web clients, translates them to messages conforming with the `fetchai/http` protocol and passes it to a skill (e.g. `fetchai/http_echo`) to process. The `fetchai/http` protocol in this case is used for communication between the connection and the skill.
+In the http connection guide we demonstrate how an AEA with a http server connection (e.g. `fetchai/http_server`) receives http payloads from web clients, translates them to messages conforming with the `fetchai/http` protocol and passes it to a skill (e.g. `fetchai/http_echo`) to process. The `fetchai/http` protocol in this case is used for communication between the connection and the skill.
-### Example 3 : AEA <> 3rd party server
+### Example 3 : AEA <> 3rd Party Server
-The `fetchai/http_client` connection can be used to make requests to third party servers. In this case, a skill containing the logic for the production of http requests would create messages conforming with the `fetchai/http` protocol and sends it to the `fetchai/http_client` connection which in turn translates it into http payload and sends it to the destination server.
+The `fetchai/http_client` connection can be used to make requests to third party servers. In this case, a skill containing the logic for the production of http requests would create messages conforming with the `fetchai/http` protocol and sends it to the `fetchai/http_client` connection which in turn translates it into http payload and sends it to the destination server.
Note that in general, third party SDKs can be wrapped in a connection and shared with other developers as a package. Often this also involves creating a custom protocol to enforce the type of interactions permitted between skills and the connection wrapping the SDK.
+## Next Steps
-## Next steps
-
-### Recommended
+### Recommended
We recommend you continue with the next step in the 'Getting Started' series:
- Trade between two AEAs
-### Relevant deep-dives
+### Relevant Deep-Dives
Most AEA development focuses on developing the `Skills` and `Protocols` necessary for an AEA to deliver against its economic objectives and implement interaction protocols.
@@ -54,5 +55,3 @@ Most of an AEA developer's time is spent on `Skill` development. `Skills` are th
In most cases, one of the available `Connection` packages can be used. Occasionally, you might develop your own `Connection`:
- Connections
-
-
\ No newline at end of file
diff --git a/docs/known-limits.md b/docs/known-limits.md
index f5b6410338..1b15c78a34 100644
--- a/docs/known-limits.md
+++ b/docs/known-limits.md
@@ -1,3 +1,5 @@
+# Known Limitations
+
The AEA framework makes a multitude of tradeoffs.
Here we present an incomplete list of known limitations:
@@ -7,5 +9,3 @@ Here we present an incomplete list of known limitations:
- The `AEABuilder` assumes that packages with public ids of identical author and package name have a matching version. As a result, if a developer uses a package with matching author and package name but different version in the public id, then the `AEABuilder` will not detect this and simply use the last loaded package.
- The order in which `setup` and `teardown` are called on the skills, and `act` is called on the behaviours, is not guaranteed. Skills should be designed to work independently. Where skills use the `shared_context` to exchange information they must do so safely.
-
-
diff --git a/docs/language-agnostic-definition.md b/docs/language-agnostic-definition.md
index 4ee4b6b428..0b42a81ce8 100644
--- a/docs/language-agnostic-definition.md
+++ b/docs/language-agnostic-definition.md
@@ -1,136 +1,126 @@
+# Language Agnostic Definition
+
Currently, there is an implementation of the AEA framework in Python which enables the development of AEAs in Python, and allows AEAs which are built with it to run.
-However, AEAs can be developed in different programming languages. This is further backed by the idea that agent-based solutions are suited for multi-stakeholder environments where the different AEAs may be developed independently of one another, resulting in heterogeneous systems.
+However, AEAs can be developed in different programming languages. This is further backed by the idea that agent-based solutions are suited for multi-stakeholder environments where the different AEAs may be developed independently of one another, resulting in heterogeneous systems.
This means that in principle, there could be different implementations of the AEA framework, in various programming languages and for different platforms. However, to ensure that AEAs under any implementation are compatible with one another and able to interact, they must satisfy specific definitions. In this page, we compile a set of definitions which any AEA independent of its implementation must satisfy in order to be able to interact with other AEAs.
An AEA, in technical terms, must satisfy the following requirements:
-
-
It MUST be capable of receiving and sending Envelopes which satisfy the following protobuf schema:
-
-``` proto
-syntax = "proto3";
-
-package aea.base.v0_1_0;
-
-message Envelope{
- string to = 1;
- string sender = 2;
- string protocol_id = 3;
- bytes message = 4;
- string uri = 5;
-}
-```
-
-The format for the above fields are as follows:
-
-
-
to and sender: an address derived from the private key of a secp256k1-compatible elliptic curve
It MUST implement each protocol's message with the required meta-fields:
-
-``` proto
-syntax = "proto3";
-
-package aea.base.v0_1_0;
-
-import "google/protobuf/struct.proto";
-
-
-message DialogueMessage {
- int32 message_id = 1;
- string dialogue_starter_reference = 2;
- string dialogue_responder_reference = 3;
- int32 target = 4;
- bytes content = 5;
-}
-
-message Message {
- oneof message {
- google.protobuf.Struct body = 1;
- DialogueMessage dialogue_message = 2;
- }
-}
-
-message Envelope{
- string to = 1;
- string sender = 2;
- string protocol_id = 3;
- bytes message = 4;
- string uri = 5;
-}
-```
- where content is replaced with the protocol specific content (see here for details).
-
-
-
It MUST implement protocols according to their specification (see here for details).
-
-
It SHOULD implement the fetchai/default:1.1.6 protocol which satisfies the following protobuf schema:
-
-``` proto
-syntax = "proto3";
-
-package aea.fetchai.default.v1_0_0;
-
-message DefaultMessage{
-
- // Custom Types
- message ErrorCode{
- enum ErrorCodeEnum {
- UNSUPPORTED_PROTOCOL = 0;
- DECODING_ERROR = 1;
- INVALID_MESSAGE = 2;
- UNSUPPORTED_SKILL = 3;
- INVALID_DIALOGUE = 4;
+- It MUST be capable of receiving and sending `Envelopes` which satisfy the following protobuf schema:
+
+ ``` proto
+ syntax = "proto3";
+
+ package aea.base.v0_1_0;
+
+ message Envelope{
+ string to = 1;
+ string sender = 2;
+ string protocol_id = 3;
+ bytes message = 4;
+ string uri = 5;
+ }
+ ```
+
+ The format for the above fields are as follows:
+
+ - `to` and `sender`: an address derived from the private key of a secp256k1-compatible elliptic curve
+ - `protocol_id`: this must match a defined regular expression (see below)
+ - `message`: a bytes string representing a serialized message in the specified protocol
+ - `URI`: follows this syntax
+
+- It MUST implement each protocol's `message` with the required meta-fields:
+
+ ``` proto
+ syntax = "proto3";
+
+ package aea.base.v0_1_0;
+
+ import "google/protobuf/struct.proto";
+
+
+ message DialogueMessage {
+ int32 message_id = 1;
+ string dialogue_starter_reference = 2;
+ string dialogue_responder_reference = 3;
+ int32 target = 4;
+ bytes content = 5;
+ }
+
+ message Message {
+ oneof message {
+ google.protobuf.Struct body = 1;
+ DialogueMessage dialogue_message = 2;
+ }
+ }
+
+ message Envelope{
+ string to = 1;
+ string sender = 2;
+ string protocol_id = 3;
+ bytes message = 4;
+ string uri = 5;
+ }
+ ```
+
+ where `content` is replaced with the protocol specific content (see here for details).
+
+- It MUST implement protocols according to their specification (see here for details).
+
+- It SHOULD implement the `fetchai/default:1.1.6` protocol which satisfies the following protobuf schema:
+
+ ``` proto
+ syntax = "proto3";
+
+ package aea.fetchai.default.v1_0_0;
+
+ message DefaultMessage{
+
+ // Custom Types
+ message ErrorCode{
+ enum ErrorCodeEnum {
+ UNSUPPORTED_PROTOCOL = 0;
+ DECODING_ERROR = 1;
+ INVALID_MESSAGE = 2;
+ UNSUPPORTED_SKILL = 3;
+ INVALID_DIALOGUE = 4;
+ }
+ ErrorCodeEnum error_code = 1;
+ }
+
+
+ // Performatives and contents
+ message Bytes_Performative{
+ bytes content = 1;
+ }
+
+ message Error_Performative{
+ ErrorCode error_code = 1;
+ string error_msg = 2;
+ map error_data = 3;
+ }
+
+ message End_Performative{
+ }
+
+
+ oneof performative{
+ Bytes_Performative bytes = 5;
+ End_Performative end = 6;
+ Error_Performative error = 7;
+ }
}
- ErrorCodeEnum error_code = 1;
- }
-
-
- // Performatives and contents
- message Bytes_Performative{
- bytes content = 1;
- }
-
- message Error_Performative{
- ErrorCode error_code = 1;
- string error_msg = 2;
- map error_data = 3;
- }
-
- message End_Performative{
- }
-
-
- oneof performative{
- Bytes_Performative bytes = 5;
- End_Performative end = 6;
- Error_Performative error = 7;
- }
-}
-```
-
-
The protocol id MUST match the following regular expression: ^([a-zA-Z_][a-zA-Z0-9_]{0,127})/([a-zA-Z_][a-zA-Z0-9_]{0,127})(:((any|latest|((0|[1-9]\d*))\.((0|[1-9]\d*))\.((0|[1-9]\d*))(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)))?$
-
It is recommended that it processes Envelopes asynchronously. Note, the specification regarding the processing of messages does not impose any particular implementation, and the AEA can be designed to process envelopes either synchronously and asynchronously. However, asynchronous message handling enables the agent to be more responsive and scalable in maintaining many concurrent dialogues with its peers.
-
-
It MUST have an identity in the form of, at a minimum, an address derived from a public key and its associated private key (where the elliptic curve must be of type SECP256k1).
-
-
It SHOULD implement handling of errors using the fetchai/default:1.1.6 protocol. The protobuf schema is given above.
-
-
It MUST implement the following principles when handling messages:
-
-
It MUST ALWAYS handle incoming envelopes/messages and NEVER raise an exception when decoding and validating the message. This ensures another AEA cannot cause the agent to fail by sending a malicious envelope/message.
-
It MUST NEVER handle outgoing messages and ALWAYS raise an exception when validating the message. An exception implies that the handler is resolving a bug in the implementation.
-
-
-
-
-
Note
-
Additional constraints will be added soon!
-
+ ```
+
+- The protocol id MUST match the following regular expression: `^([a-zA-Z_][a-zA-Z0-9_]{0,127})/([a-zA-Z_][a-zA-Z0-9_]{0,127})(:((any|latest|((0|[1-9]\d*))\.((0|[1-9]\d*))\.((0|[1-9]\d*))(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)))?$`
+- It is recommended that it processes `Envelopes` asynchronously. Note, the specification regarding the processing of messages does not impose any particular implementation, and the AEA can be designed to process envelopes either synchronously and asynchronously. However, asynchronous message handling enables the agent to be more responsive and scalable in maintaining many concurrent dialogues with its peers.
+- It MUST have an identity in the form of, at a minimum, an address derived from a public key and its associated private key (where the elliptic curve must be of type SECP256k1).
+- It SHOULD implement handling of errors using the `fetchai/default:1.1.6` protocol. The protobuf schema is given above.
+- It MUST implement the following principles when handling messages:
+ - It MUST ALWAYS handle incoming envelopes/messages and NEVER raise an exception when decoding and validating the message. This ensures another AEA cannot cause the agent to fail by sending a malicious envelope/message.
+ - It MUST NEVER handle outgoing messages and ALWAYS raise an exception when validating the message. An exception implies that the handler is resolving a bug in the implementation.
+
+!!! note
+ Additional constraints will be added soon!
diff --git a/docs/ledger-integration.md b/docs/ledger-integration.md
index 29f56b5270..a64caed4bb 100644
--- a/docs/ledger-integration.md
+++ b/docs/ledger-integration.md
@@ -1,6 +1,8 @@
+# Ledger & Crypto APIs
+
In this section, we show you how to integrate the AEA with the Fetch.ai and third-party ledgers.
-## Ledger support
+## Ledger Support
For a ledger to be considered _supported_ in the framework, three abstract base classes need to be implemented:
@@ -10,10 +12,10 @@ For a ledger to be considered _supported_ in the framework, three abstract base
These three classes have their own registries, which allow the developer to import the relevant object where needed.
-## Ledger plug-in architecture
+## Ledger Plug-in Architecture
-The AEA framework provides a plug-in mechanism to support ledger functionalities in
-an easily extendible way. At import time, the framework will load
+The AEA framework provides a plug-in mechanism to support ledger functionalities in
+an easily extendable way. At import time, the framework will load
all the crypto plug-ins available in the current Python environment.
A _crypto plug-in_ is a Python package which declares some specific
@@ -27,7 +29,7 @@ In particular, there are three types of entry points the framework looks up:
This is an example of `setup.py` script for a ledger plug-in `aea-ledger-myledger`:
-```python
+``` python
# sample ./setup.py file
from setuptools import setup
@@ -55,7 +57,7 @@ and the importable package name is `aea_ledger_myledger`.
You can search for AEA ledger plug-ins on PyPI:
https://pypi.org/search/?q=aea-ledger
-## Maintained plug-ins
+## Maintained Plug-ins
At the moment, the framework natively supports the following three ledgers:
@@ -65,7 +67,6 @@ At the moment, the framework natively supports the following three ledgers:
However, support for additional ledgers can be added to the framework at runtime.
-
## Examples
- Examples of how to interact with the crypto registry:
@@ -128,27 +129,25 @@ The framework wraps all `LedgerApi` classes and exposes them in the https://explore-dorado.fetch.ai |
-| Token Faucet | Use block explorer |
+| Token Faucet | Use block explorer |
You can access more details on docs section.
The configurations can be specified for the `fetchai/ledger:0.21.4` connection.
-## CosmWasm supporting chains
+## CosmWasm Supporting Chains
The Fetch.ai networks use CosmWasm for smart contract support.
-
diff --git a/docs/limits.md b/docs/limits.md
index 716855a954..b3efc7ee2c 100644
--- a/docs/limits.md
+++ b/docs/limits.md
@@ -1,8 +1,10 @@
+# Limitations of v1
+
This document describes some of the limitations of `v1` of the AEA framework and tradeoffs made in its design.
-### Rejected ideas:
+## Rejected Ideas
-#### Handlers implemented as behaviours:
+### Handlers Implemented as Behaviours
Handlers can be considered a special cases of a "behaviour that listens for specific events to happen".
@@ -10,24 +12,21 @@ One could implement `Handler` classes in terms of `Behaviours`, after having imp
This was rejected in favour of a clear separation of concerns, and to avoid purely reactive (handlers) and proactive (behaviours) components to be conflated into one concept. The proposal would also add complexity to behaviour development.
-
-#### Multiple versions of the same package
+### Multiple Versions of the Same Package
The framework does not allow for the usage of multiple versions of the same package in a given project.
Although one could re-engineer the project to allow for this, it does introduce significant additional complexities. Furthermore, Python modules are by design only allowed to exist as one version in a given process. Hence, it seems sensible to maintain this approach in the AEA.
+## Potential Extensions, Considered yet not Decided
-### Potential extensions, considered yet not decided:
-
-
-#### Alternative skill design
+### Alternative Skill Design
-For very simple skills, the splitting of skills into `Behaviour`, `Handler`, `Model` and `Task` classes can add unnecessary complexity to the framework and a counter-intuitive responsibility split. The splitting also implies the framework needs to introduce the `SkillContext` object to allow for access to data across the skill. Furthermore, the framework requires implementing all functionality in `SkillComponent` classes `Handler`, `Behaviour` or `Model`. This approach is consistent and transparent, however it creates a lot of boiler plate code for simple skills.
+For very simple skills, the splitting of skills into `Behaviour`, `Handler`, `Model` and `Task` classes can add unnecessary complexity to the framework and a counter-intuitive responsibility split. The splitting also implies the framework needs to introduce the `SkillContext` object to allow for access to data across the skill. Furthermore, the framework requires implementing all functionality in `SkillComponent` classes `Handler`, `Behaviour` or `Model`. This approach is consistent and transparent, however it creates a lot of boilerplate code for simple skills.
Hence, for some use cases it would be useful to have a single `Skill` class with abstract methods `setup`, `act`, `handle` and `teardown`. Then the developer can decide how to split up their code.
-```
+``` python
class SkillTemplate(SimpleSkill):
protocol_ids: Optional[List[PublicId]] = None
@@ -48,16 +47,15 @@ class SkillTemplate(SimpleSkill):
Alternatively, we could use decorators to let a developer define whether a function is part of a handler or behaviour. That way, a single file with a number of functions could implement a skill. (Behind the scenes this would utilise a number of virtual `Behaviour` and `Handler` classes provided by the framework).
-The downside of this approach is that it does not advocate for much modularity on the skill level. Part of the role of a framework is to propose a common way to do things. The above approach can cause for a larger degree of heterogeneity in the skill design which makes it harder for developers to understand each others' code.
+The downside of this approach is that it does not advocate for much modularity on the skill level. Part of the role of a framework is to propose a common way to do things. The above approach can cause for a larger degree of heterogeneity in the skill design which makes it harder for developers to understand each other's code.
The separation between all four base classes does exist both in convention *and* at the code level. Handlers deal with skill-external events (messages), behaviours deal with scheduled events (ticks), models represent data and tasks are used to manage long-running business logic.
By adopting strong convention around skill development we allow for the framework to take a more active role in providing guarantees. E.g. handlers' and behaviours' execution can be limited to avoid them being blocking, models can be persisted and recreated, tasks can be executed with different task backends. The opinionated approach is thought to allow for better scaling.
+### Further Modularity for Skill Level Code
-#### Further modularity for skill level code
-
-Currently we have three levels of modularity:
+Currently, we have three levels of modularity:
- PyPI packages
- framework packages: protocols, contracts, connections and skills
@@ -65,11 +63,11 @@ Currently we have three levels of modularity:
We could consider having a fourth level: common behaviours, handlers, models exposed as modules which can then speed up skill development.
-
-#### "promise" pattern:
+### "promise" Pattern
Given the asynchronous nature of the framework, it is often hard to implement reactions to specific messages, without making a "fat" handler. Take the example of a handler for a certain type of message `A` for a certain protocol `p`. The handler for protocol `p` would look something like this:
-```
+
+``` python
class PHandler:
...
def handle(msg):
@@ -78,7 +76,8 @@ def handle(msg):
```
However, it could be helpful to overwrite this handler reaction with another callback (e.g. consider this in context):
-```
+
+``` python
# callable that handles the reply
def my_callback(msg):
# handle reply
@@ -88,15 +87,13 @@ self.context.outbox.put_message(message, handler_func=my_callback, failure_func=
This feature would introduce additional complexity for the framework to correctly wire up the callbacks and messages with the dialogues.
-
-#### CLI using standard lib
+### CLI using Standard Lib
Removing the click dependency from the CLI would further reduce the dependencies in the AEA framework which is overall desirable.
+### Metadata vs Configurations
-#### Meta data vs configurations
-
-The current approach uses `yaml` files to specify both meta data and component configuration. It would be desirable to introduce the following separation:
+The current approach uses `yaml` files to specify both metadata and component configuration. It would be desirable to introduce the following separation:
- package metadata
- package default developer configuration
@@ -104,39 +101,35 @@ The current approach uses `yaml` files to specify both meta data and component c
A user can only configure a subset of the configuration. The developer should be able to define these constraints for the user. Similarly, a developer cannot modify all fields in a package, some of them are determined by the framework.
-
-#### Configuring agent goal setup
+### Configuring Agent Goal Setup
By default, the agent's goals are implicitly defined by its skills and the configurations thereof. This is because the default decision maker signs every message and transaction presented to it.
It is already possible to design a custom decision maker. However, more work needs to be done to understand how to improve the usability and configuration of the decision maker. In this context different types of decision makers can be implemented for the developer/user.
-
-#### Connection status monitoring
+### Connection Status Monitoring
Currently, connections are responsible for managing their own status after they have been "connected" by the `Multiplexer`. Developers writing connections must take care to properly set its connection status at all times and manage any disconnection. It would potentially be desirable to offer different policies to deal with connection problems on the multiplexer level:
- disconnect one, keep others alive
- disconnect all
-- try reconnect indefinitely
+- try to reconnect indefinitely
-
-#### Agent snapshots on teardown or error
+### Agent Snapshots on Teardown or Error
Currently, the developer must implement snapshots on the component level. It would be desirable if the framework offered more help to persist the agent state on teardown or error.
-
-#### Dialogues management
+### Dialogues Management
The current implementation of Dialogues is verbose. Developers often need to subclass `Dialogues` and `Dialogue` classes. More effort can be made to simplify and streamline dialogues management.
-#### Instantiate multiple instances of the same class of `SkillComponent`
+### Instantiate Multiple Instances of the Same Class of `SkillComponent`
Currently, configuration and metadata of a package are conflated making it not straightforward to run one package component with multiple sets of configuration. It could be desirable to configure an agent to run a given package with multiple different configurations.
-This feature could be problematic with respect to component to component messaging which currently relies on component ids, which are bound to the package and not its instance.
+This feature could be problematic with respect to component-to-component messaging which currently relies on component ids, which are bound to the package and not its instance.
-#### Containerized Agents
+### Containerized Agents
Agent management, especially when many of them live on the same host, can be cumbersome. The framework should provide more utilities for these large-scale use cases. But a proper isolation of the agent environment is something that helps also simple use cases.
@@ -144,7 +137,7 @@ A new software architecture, somehow inspired to the Docker system. The CLI only
Users and developers would potentially like to run many AEAs of different versions and with differences in the versions of their dependencies. It is not possible to import different versions of the same Python (PyPI) package in the same process in a clean way. However, in different processes this is trivial with virtual environments. It would be desirable to consider this in the context of a container solution for agents.
-#### Dependency light version of the AEA framework
+### Dependency Light Version of the AEA Framework
The `v1` of the Python AEA implementation makes every effort to minimise the amount of third-party dependencies. However, some dependencies remain to lower development time.
@@ -152,22 +145,22 @@ It would be desirable to further reduce the dependencies, and potentially have a
This could be taken further, and a reduced spec version for micropython could be designed.
-#### Compiled AEA
+### Compiled AEA
Python is not a compiled language. However, various projects attempt this, e.g. Nuitka and it would be desirable to explore how useful and practical this would be in the context of AEA.
-#### DID integration
+### DID Integration
It would be great to integrate DID in the framework design, specifically identification of packages (most urgently protocols). Other projects and standards worth reviewing in the context (in particular with respect to identity):
- ERC 725: Ethereum Identity Standard and here.
- ERC 735: Claim Holder
-#### Optimise protocol schemas and messages
+### Optimise Protocol Schemas and Messages
The focus of protocol development was on extensibility and compatibility, not on optimisation. For instance, the dialogue references use inefficient string representations.
-#### Constraints on primitive types in protocols
+### Constraints on Primitive Types in Protocols
The protocol generator currently does not support custom constraints. The framework could add support for custom constraints for the protocol generator and specification.
@@ -192,8 +185,7 @@ This would automatically enable support for signed/unsigned `int` and `float`. T
Currently, the developer has to specify a custom type to implement any constraints on primitive types.
-
-#### Subprotocols & multi-party interactions
+### Sub-protocols & Multi-Party Interactions
Protocols can be allowed to depend on each other. Similarly, protocols might have multiple parties.
@@ -201,13 +193,11 @@ Furthermore, a turn-taking function that specifies who's turn it is at any given
Then the current `fipa` setup is a specific case of turn-taking where the turn shifts after a player sends a single move (unique-reply). But generally, it does not have to be like this. Players could be allowed to send multiple messages until the turn shifts, or until they send specific speech-acts (multiple-replies).
-
-#### Timeouts in protocols
+### Timeouts in Protocols
Protocols currently do not implement the concept of timeouts. We leave it to the skill developer to implement any time-specific protocol rules.
-
-#### Framework internal messages
+### Framework Internal Messages
The activation/deactivation of skills and addition/removal of components is implemented in a "passive" way - the skill posts a request in its skill context queue (in the case of new behaviours), or it just sets a flag (in case of activation/deactivation of skills).
@@ -215,11 +205,11 @@ One could consider that a skill can send requests to the framework, via the inte
This is a further small but meaningful step toward an actor-based model for agent internals.
-#### Ledger transaction management
+### Ledger Transaction Management
Currently, the framework does not manage any aspect of submitting multiple transactions to the ledgers. This responsibility is left to skills. Additionally, the ledger APIs/contract APIs take the ledger as a reference to determine the nonce for a transaction. If a new transaction is sent before a previous transaction has been processed then the nonce will not be incremented correctly for the second transaction. This can lead to submissions of multiple transactions with the same nonce, and therefore failure of subsequent transactions.
-A naive approach would involve manually incrementing the nonce and then submitting transactions into the pool with the correct nonce for eventual inclusion. The problem with this approach is that any failure of a transaction will cause non of the subsequent transactions to be processed for some ledgers (https://ethereum.stackexchange.com/questions/2808/what-happens-when-a-transaction-nonce-is-too-high). To recover from a transaction failure not only the failed transaction would need to be handled, but potentially also all subsequent transactions. It is easy to see that logic required to recover from a transaction failure early in a sequence can be arbitrarily complex (involving potentially new negotiations between agents, new signatures having to be generated etc.).
+A naive approach would involve manually incrementing the nonce and then submitting transactions into the pool with the correct nonce for eventual inclusion. The problem with this approach is that any failure of a transaction will cause none of the subsequent transactions to be processed for some ledgers (). To recover from a transaction failure not only the failed transaction would need to be handled, but potentially also all subsequent transactions. It is easy to see that logic required to recover from a transaction failure early in a sequence can be arbitrarily complex (involving potentially new negotiations between agents, new signatures having to be generated etc.).
A further problem with the naive approach is that it (imperfectly) replicates the ledger state (with respect to (subset of state of) a specific account).
@@ -229,32 +219,26 @@ This approach is currently used and implemented across all the reference skills.
Related, the topic of latency in transactions. State channels provide a solution. E.g. Perun. There could also be an interesting overlap with our protocols here.
-
-#### Unsolved problems in `Multiplexer` - `AgentLoop` interplay
+### Unsolved Problems in `Multiplexer` - `AgentLoop` Interplay
Problem 1: connection generates too many messages in a short amount of time, that are not consumed by the multiplexer
Solution: Can be solved by slowing down connections receive method called, controlled by the inbox messages amount
Side effects: Most of the connections should have an internal queue because there is no synchronization between internal logic and multiplexer connection `receive` calls.
-
Problem 2: the send method can take a long time (because send retries logic in connection)
-Solution: Currently, we apply timeouts on send. Other solutions could be considered, like parallelisation.
-
+Solution: Currently, we apply timeouts on send. Other solutions could be considered, like parallelization.
Problem 3: too many messages are produced by a skill.
Solution: Raise an exception on outbox is full or slow down agent loop?
-
## ACN
-### Agent mobility on ACN
+### Agent Mobility on ACN
If a peer-client or full client switches peer, then the DHT is not updated properly at the moment under certain conditions.
-### Mailbox connection
+### Mailbox Connection
The two available connections `p2p_libp2p` and `p2p_libp2p_client` imply that the agent is continuously connected and therefore must have uninterrupted network access and the resources to maintain a connection.
For more lightweight implementations, a mailbox connection is desirable, as outlined in the ACN documentation.
-
-
diff --git a/docs/logging.md b/docs/logging.md
index 660b8d21a6..bbd05c3e4b 100644
--- a/docs/logging.md
+++ b/docs/logging.md
@@ -1,9 +1,10 @@
+# Logging
+
The AEA framework supports flexible logging capabilities with the standard Python logging library.
In this tutorial, we configure logging for an AEA.
-First of all, create your AEA.
-
+First, create your AEA.
``` bash
aea create my_aea
@@ -75,8 +76,7 @@ logging_config:
This configuration will set up a logger with name `aea`. It prints both on console and on file with a format specified by the `standard` formatter.
-
-## Streaming to browser
+## Streaming to Browser
It is possible to configure the AEA to stream logs to a browser.
@@ -167,5 +167,3 @@ if __name__ == "__main__":
Save the script in a file called `server.py`, install flask with `pip install flask` and run the server with `python server.py`.
Third, run your AEA and visit `localhost:5000` in your browser.
-
-
diff --git a/docs/message-routing.md b/docs/message-routing.md
index c32f886417..a77dcf45a7 100644
--- a/docs/message-routing.md
+++ b/docs/message-routing.md
@@ -1,3 +1,4 @@
+# Message Routing
Message routing can be split up into the routing of incoming and outgoing `Messages`.
@@ -11,14 +12,11 @@ It is important to keep in mind that interacti
- the `AEA` tries to decode the message; errors are handled by the `ErrorHandler`
- `Messages` are dispatched based on two rules:
- 1. checks if `to` field can be interpreted as `skill_id`, if so uses that together with the `protocol_id` to dispatch to the protocol's `Handler` in the specified `Skill`, else
- 2. uses the `protocol_id` to dispatch to the protocol's `Handler` in all skills supporting the protocol.
+ 1. checks if `to` field can be interpreted as `skill_id`, if so uses that together with the `protocol_id` to dispatch to the protocol's `Handler` in the specified `Skill`, else
+ 2. uses the `protocol_id` to dispatch to the protocol's `Handler` in all skills supporting the protocol.
-
-
-
-## Option 1: AEA Manager approach
+## Option 1: AEA Manager Approach
-Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.
+Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.
-### Preparation instructions
+### Preparation Instructions
-* Install the AEA Manager.
-* Install Tensorflow
+- Install the AEA Manager.
+- Install Tensorflow
-### Demo instructions
+### Demo Instructions
The following steps assume you have launched the AEA Manager Desktop app.
@@ -73,153 +73,153 @@ The following steps assume you have launched the AEA Manager Desktop app.
4. Run the `ml_data_provider` AEA. Navigate to its logs and copy the multiaddress displayed.
5. Navigate to the settings of the `ml_model_trainer` and under `components > connection >` `fetchai/p2p_libp2p:0.22.0` update as follows (make sure to replace the placeholder with the multiaddress):
-``` bash
-{
- "delegate_uri": "127.0.0.1:11001",
- "entry_peers": ["REPLACE_WITH_MULTI_ADDRESS_HERE"],
- "local_uri": "127.0.0.1:9001",
- "log_file": "libp2p_node.log",
- "public_uri": "127.0.0.1:9001"
-}
-```
+
+ ``` bash
+ {
+ "delegate_uri": "127.0.0.1:11001",
+ "entry_peers": ["REPLACE_WITH_MULTI_ADDRESS_HERE"],
+ "local_uri": "127.0.0.1:9001",
+ "log_file": "libp2p_node.log",
+ "public_uri": "127.0.0.1:9001"
+ }
+ ```
6. Run the `ml_model_trainer`.
-In the AEA's logs, you should see the agents trading successfully, and the training agent training its machine learning model using the data purchased.
+In the AEA's logs, you should see the agents trading successfully, and the training agent training its machine learning model using the data purchased.
The trainer keeps purchasing data and training its model until stopped.
-
-## Option 2: CLI approach
+## Option 2: CLI Approach
Follow this approach when using the `aea` CLI.
-### Preparation instructions
+### Preparation Instructions
#### Dependencies
-* Follow the Preliminaries and Installation sections from the AEA quick start.
-* Install Tensorflow
+- Follow the Preliminaries and Installation sections from the AEA quick start.
+- Install Tensorflow
-### Demo instructions
+### Demo Instructions
-#### Create data provider AEA
+#### Create Data Provider AEA
First, fetch the data provider AEA:
-``` bash
-aea fetch fetchai/ml_data_provider:0.32.4
-cd ml_data_provider
-aea install
-aea build
-```
-
-Alternatively, create from scratch.
-
-The following steps create the data provider from scratch:
``` bash
-aea create ml_data_provider
+aea fetch fetchai/ml_data_provider:0.32.4
cd ml_data_provider
-aea add connection fetchai/p2p_libp2p:0.27.4
-aea add connection fetchai/soef:0.27.5
-aea add connection fetchai/ledger:0.21.4
-aea add skill fetchai/ml_data_provider:0.27.5
-aea config set --type dict agent.dependencies \
-'{
- "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"}
-}'
-aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
-aea config set --type dict agent.default_routing \
-'{
- "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
- "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
-}'
aea install
aea build
```
-
-
-
-#### Create model trainer AEA
+??? note "Alternatively, create from scratch:"
+ The following steps create the data provider from scratch:
+
+ ``` bash
+ aea create ml_data_provider
+ cd ml_data_provider
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/ml_data_provider:0.27.5
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
+ }'
+ aea install
+ aea build
+ ```
+
+#### Create Model Trainer AEA
Then, fetch the model trainer AEA:
-``` bash
-aea fetch fetchai/ml_model_trainer:0.33.4
-cd ml_model_trainer
-aea install
-aea build
-```
-
-Alternatively, create from scratch.
-
-The following steps create the model trainer from scratch:
``` bash
-aea create ml_model_trainer
+aea fetch fetchai/ml_model_trainer:0.33.4
cd ml_model_trainer
-aea add connection fetchai/p2p_libp2p:0.27.4
-aea add connection fetchai/soef:0.27.5
-aea add connection fetchai/ledger:0.21.4
-aea add skill fetchai/ml_train:0.29.5
-aea config set --type dict agent.dependencies \
-'{
- "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"}
-}'
-aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
-aea config set --type dict agent.default_routing \
-'{
- "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
- "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
-}'
aea install
aea build
```
-
-
-
-
-#### Add keys for the data provider AEA
+??? note "Alternatively, create from scratch:"
+ The following steps create the model trainer from scratch:
+
+ ``` bash
+ aea create ml_model_trainer
+ cd ml_model_trainer
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/ml_train:0.29.5
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
+ }'
+ aea install
+ aea build
+ ```
+
+#### Add Keys for the Data Provider AEA
First, create the private key for the data provider AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai `Dorado` use:
+
``` bash
aea generate-key fetchai
aea add-key fetchai fetchai_private_key.txt
```
Next, create a private key used to secure the AEA's communications:
+
``` bash
aea generate-key fetchai fetchai_connection_private_key.txt
aea add-key fetchai fetchai_connection_private_key.txt --connection
```
Finally, certify the key for use by the connections that request that:
+
``` bash
aea issue-certificates
```
-#### Add keys and generate wealth for the model trainer AEA
+#### Add Keys and Generate Wealth for the Model Trainer AEA
The model trainer needs to have some wealth to purchase the data from the data provider.
First, create the private key for the model trainer AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai `Dorado` use:
+
``` bash
aea generate-key fetchai
aea add-key fetchai fetchai_private_key.txt
```
Then, create some wealth for your model trainer based on the network you want to transact with. On the Fetch.ai `Dorado` network:
+
``` bash
aea generate-wealth fetchai
```
Next, create a private key used to secure the AEA's communications:
+
``` bash
aea generate-key fetchai fetchai_connection_private_key.txt
aea add-key fetchai fetchai_connection_private_key.txt --connection
```
Finally, certify the key for use by the connections that request that:
+
``` bash
aea issue-certificates
```
@@ -237,25 +237,8 @@ aea run
Once you see a message of the form `To join its network use multiaddr 'SOME_ADDRESS'` take note of the address. (Alternatively, use `aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.4 -u public_uri` to retrieve the address.)
This is the entry peer address for the local agent communication network created by the ML data provider.
-
Then, in the ML model trainer, run this command (replace `SOME_ADDRESS` with the correct value as described above):
+
``` bash
aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
'{
@@ -266,24 +249,24 @@ aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
"public_uri": "127.0.0.1:9001"
}'
```
-This allows the model trainer to connect to the same local agent communication network as the data provider.
+This allows the model trainer to connect to the same local agent communication network as the data provider.
Then run the model trainer AEA:
+
``` bash
aea run
```
-You can see that the AEAs find each other, negotiate and eventually trade. After the trade, the model trainer AEA trains its ML model using the data it has purchased.
+You can see that the AEAs find each other, negotiate and eventually trade. After the trade, the model trainer AEA trains its ML model using the data it has purchased.
This AEA keeps purchasing data and training its model until stopped.
#### Cleaning up
When you're finished, delete your AEAs:
+
``` bash
cd ..
aea delete ml_data_provider
aea delete ml_model_trainer
```
-
-
\ No newline at end of file
diff --git a/docs/modes.md b/docs/modes.md
index cc142afb3d..cc3198bb42 100644
--- a/docs/modes.md
+++ b/docs/modes.md
@@ -1,3 +1,4 @@
+# Modes of Running an AEA
We can run an AEA in multiple modes thanks to the configurable design of the framework.
diff --git a/docs/multi-agent-manager.md b/docs/multi-agent-manager.md
index c645a66f3e..bcee29e2de 100644
--- a/docs/multi-agent-manager.md
+++ b/docs/multi-agent-manager.md
@@ -1,3 +1,4 @@
+# Multi Agent Manager
The `MultiAgentManager` allows managing multiple agent projects programmatically.
@@ -16,7 +17,7 @@ manager = MultiAgentManager(WORKING_DIR)
manager.start_manager()
```
-## Adding projects
+## Adding Projects
We first add a couple of finished AEA project:
@@ -31,10 +32,10 @@ weather_station_name = weather_station_id.name
weather_client_name = weather_client_id.name
```
-## Adding agent instances
-
+## Adding Agent Instances
Add the agent instances
+
``` python
agent_overrides = {
"private_key_paths": {"fetchai": "fetchai_private_key.txt"},
@@ -98,8 +99,8 @@ component_overrides = [{
manager.add_agent(weather_client_id, component_overrides=component_overrides, agent_overrides=agent_overrides)
```
-
Save the following private keys in the respective files.
+
``` python
FET_PRIVATE_KEY_STATION = b"72d3149f5689f0749eaec5ebf6dba5deeb1e89b93ae1c58c71fd43dfaa231e87"
FET_PRIVATE_KEY_PATH_STATION = Path(manager.data_dir, weather_station_name, "fetchai_private_key.txt").absolute()
@@ -118,7 +119,7 @@ FET_CONNECTION_PRIVATE_KEY_PATH_CLIENT = Path(manager.data_dir, weather_client_n
FET_CONNECTION_PRIVATE_KEY_PATH_CLIENT.write_bytes(FET_CONNECTION_PRIVATE_KEY_CLIENT)
```
-## Running the agents:
+## Running the Agents
``` python
import time
@@ -133,7 +134,7 @@ manager.start_agent(weather_client_id.name)
time.sleep(5.0)
```
-## Stopping the agents:
+## Stopping the Agents
``` python
manager.stop_all_agents()
@@ -145,6 +146,6 @@ manager.stop_all_agents()
manager.stop_manager()
```
-# Limitations
+## Limitations
The `MultiAgentManager` can only be used with compatible package versions, in particular the same package (with respect to author and name) cannot be used in different versions. If you want to run multiple agents with differing versions of the same package then use the `aea launch` command in the multi-processing mode, or simply launch each agent individually with `aea run`.
diff --git a/docs/multiplexer-standalone.md b/docs/multiplexer-standalone.md
index b66adb12ae..0f46adaadb 100644
--- a/docs/multiplexer-standalone.md
+++ b/docs/multiplexer-standalone.md
@@ -1,6 +1,9 @@
+# Use Multiplexer Stand-Alone
+
The `Multiplexer` can be used stand-alone. This way a developer can utilise the protocols and connections independent of the `Agent` or `AEA` classes.
-First, import the Python and application specific libraries and set the static variables. (Get the packages directory from the AEA repository `svn export https://github.com/fetchai/agents-aea.git/trunk/packages`.)
+First, import the Python and application specific libraries and set the static variables. (Get the `packages` directory from the AEA repository `svn export https://github.com/fetchai/agents-aea.git/trunk/packages`.)
+
``` python
import os
import time
@@ -49,7 +52,7 @@ A `Multiplexer` only needs a list of connections. The `StubConnection` is a simp
## Start the `Multiplexer`
-We can run a multiplexer by calling, `connect()` which starts the receive and sending loops. We run the multiplexer from a different thread so that we can still use the main thread to pass it messages.
+We can run a multiplexer by calling, `connect()` which starts the 'receive' and 'send' loops. We run the multiplexer from a different thread so that we can still use the main thread to pass it messages.
``` python
try:
@@ -66,8 +69,10 @@ We can run a multiplexer by calling, `connect()` which starts the receive and se
raise Exception("Not connected")
```
-## Send and receive an envelope
+## Send and Receive an Envelope
+
We use the input and output text files to send an envelope to our agent and receive a response
+
``` python
# Create a message inside an envelope and get the stub connection to pass it into the multiplexer
message_text = (
@@ -112,7 +117,9 @@ We use the input and output text files to send an envelope to our agent and rece
```
## Shutdown
-Finally stop our multiplexer and wait for it to finish
+
+Finally, stop our multiplexer and wait for it to finish
+
``` python
finally:
# Shut down the multiplexer
@@ -120,121 +127,117 @@ Finally stop our multiplexer and wait for it to finish
t.join()
```
-## Your turn
+## Your Turn
Now it is your turn to develop a simple use case which utilises the `Multiplexer` to send and receive Envelopes.
-## Entire code listing
-If you just want to copy and paste the entire script in you can find it here:
-
-Click here to see full listing
-
-
-``` python
-import os
-import time
-from copy import copy
-from threading import Thread
-from typing import Optional
-
-from aea.configurations.base import ConnectionConfig
-from aea.helpers.file_io import write_with_lock
-from aea.identity.base import Identity
-from aea.mail.base import Envelope
-from aea.multiplexer import Multiplexer
-
-from packages.fetchai.connections.stub.connection import StubConnection
-from packages.fetchai.protocols.default.message import DefaultMessage
-
-
-INPUT_FILE = "input.txt"
-OUTPUT_FILE = "output.txt"
+## Entire Code Listing
+If you just want to copy and paste the entire script in you can find it here:
-def run():
- """Run demo."""
-
- # Ensure the input and output files do not exist initially
- if os.path.isfile(INPUT_FILE):
- os.remove(INPUT_FILE)
- if os.path.isfile(OUTPUT_FILE):
- os.remove(OUTPUT_FILE)
-
- # create the connection and multiplexer objects
- configuration = ConnectionConfig(
- input_file=INPUT_FILE,
- output_file=OUTPUT_FILE,
- connection_id=StubConnection.connection_id,
- )
- stub_connection = StubConnection(
- configuration=configuration,
- data_dir=".",
- identity=Identity("some_agent", "some_address", "some_public_key"),
- )
- multiplexer = Multiplexer([stub_connection], protocols=[DefaultMessage])
- try:
- # Set the multiplexer running in a different thread
- t = Thread(target=multiplexer.connect)
- t.start()
-
- # Wait for everything to start up
- for _ in range(20):
- if multiplexer.is_connected:
- break
- time.sleep(1)
- else:
- raise Exception("Not connected")
-
- # Create a message inside an envelope and get the stub connection to pass it into the multiplexer
- message_text = (
- "multiplexer,some_agent,fetchai/default:1.0.0,\x08\x01*\x07\n\x05hello,"
+??? note "Click here to see full listing:"
+
+ ``` python
+ import os
+ import time
+ from copy import copy
+ from threading import Thread
+ from typing import Optional
+
+ from aea.configurations.base import ConnectionConfig
+ from aea.helpers.file_io import write_with_lock
+ from aea.identity.base import Identity
+ from aea.mail.base import Envelope
+ from aea.multiplexer import Multiplexer
+
+ from packages.fetchai.connections.stub.connection import StubConnection
+ from packages.fetchai.protocols.default.message import DefaultMessage
+
+
+ INPUT_FILE = "input.txt"
+ OUTPUT_FILE = "output.txt"
+
+
+ def run():
+ """Run demo."""
+
+ # Ensure the input and output files do not exist initially
+ if os.path.isfile(INPUT_FILE):
+ os.remove(INPUT_FILE)
+ if os.path.isfile(OUTPUT_FILE):
+ os.remove(OUTPUT_FILE)
+
+ # create the connection and multiplexer objects
+ configuration = ConnectionConfig(
+ input_file=INPUT_FILE,
+ output_file=OUTPUT_FILE,
+ connection_id=StubConnection.connection_id,
)
- with open(INPUT_FILE, "w") as f:
- write_with_lock(f, message_text)
-
- # Wait for the envelope to get processed
- for _ in range(20):
- if not multiplexer.in_queue.empty():
- break
- time.sleep(1)
- else:
- raise Exception("No message!")
-
- # get the envelope
- envelope = multiplexer.get() # type: Optional[Envelope]
- assert envelope is not None
-
- # Inspect its contents
- print(
- "Envelope received by Multiplexer: sender={}, to={}, protocol_specification_id={}, message={}".format(
- envelope.sender,
- envelope.to,
- envelope.protocol_specification_id,
- envelope.message,
- )
+ stub_connection = StubConnection(
+ configuration=configuration,
+ data_dir=".",
+ identity=Identity("some_agent", "some_address", "some_public_key"),
)
-
- # Create a mirrored response envelope
- response_envelope = copy(envelope)
- response_envelope.to = envelope.sender
- response_envelope.sender = envelope.to
-
- # Send the envelope back
- multiplexer.put(response_envelope)
-
- # Read the output envelope generated by the multiplexer
- with open(OUTPUT_FILE, "r") as f:
- print("Envelope received from Multiplexer: " + f.readline())
- finally:
- # Shut down the multiplexer
- multiplexer.disconnect()
- t.join()
-
-
-if __name__ == "__main__":
- run()
-```
-
-
-
-
+ multiplexer = Multiplexer([stub_connection], protocols=[DefaultMessage])
+ try:
+ # Set the multiplexer running in a different thread
+ t = Thread(target=multiplexer.connect)
+ t.start()
+
+ # Wait for everything to start up
+ for _ in range(20):
+ if multiplexer.is_connected:
+ break
+ time.sleep(1)
+ else:
+ raise Exception("Not connected")
+
+ # Create a message inside an envelope and get the stub connection to pass it into the multiplexer
+ message_text = (
+ "multiplexer,some_agent,fetchai/default:1.0.0,\x08\x01*\x07\n\x05hello,"
+ )
+ with open(INPUT_FILE, "w") as f:
+ write_with_lock(f, message_text)
+
+ # Wait for the envelope to get processed
+ for _ in range(20):
+ if not multiplexer.in_queue.empty():
+ break
+ time.sleep(1)
+ else:
+ raise Exception("No message!")
+
+ # get the envelope
+ envelope = multiplexer.get() # type: Optional[Envelope]
+ assert envelope is not None
+
+ # Inspect its contents
+ print(
+ "Envelope received by Multiplexer: sender={}, to={}, protocol_specification_id={}, message={}".format(
+ envelope.sender,
+ envelope.to,
+ envelope.protocol_specification_id,
+ envelope.message,
+ )
+ )
+
+ # Create a mirrored response envelope
+ response_envelope = copy(envelope)
+ response_envelope.to = envelope.sender
+ response_envelope.sender = envelope.to
+
+ # Send the envelope back
+ multiplexer.put(response_envelope)
+
+ # Read the output envelope generated by the multiplexer
+ with open(OUTPUT_FILE, "r") as f:
+ print("Envelope received from Multiplexer: " + f.readline())
+ finally:
+ # Shut down the multiplexer
+ multiplexer.disconnect()
+ t.join()
+
+
+ if __name__ == "__main__":
+ run()
+ ```
diff --git a/docs/oef-ledger.md b/docs/oef-ledger.md
index 5880a21d99..f9b6151435 100644
--- a/docs/oef-ledger.md
+++ b/docs/oef-ledger.md
@@ -1,3 +1,4 @@
+# Relation to OEF and Ledger
The Open Economic Framework (OEF) and Decentralized Ledger Technologies (DLTs) allow AEAs to create value through their interaction with other AEAs. The following diagram illustrates the relation of AEAs to the OEF and DLTs.
@@ -5,12 +6,10 @@ The Open Economic Framework (OEF) and Decentralized Ledger Technologies (DLTs) a
## Open Economic Framework (OEF)
-The _Open Economic Framework_ (OEF) consists of protocols, languages and market mechanisms agents use to search and find each other, communicate with as well as trade with each other. As such the OEF defines the decentralised virtual environment that supplies and supports APIs for autonomous third-party software agents, also known as Autonomous Economic Agents (AEAs).
+The _Open Economic Framework_ (OEF) consists of protocols, languages and market mechanisms agents use to search and find each other, communicate with as well as trade with each other. As such the OEF defines the decentralized virtual environment that supplies and supports APIs for autonomous third-party software agents, also known as Autonomous Economic Agents (AEAs).
-
-
Note
-
The OEF is under development. Expect frequent changes. What follows is a description of the current implementation.
-
+!!! note
+ The OEF is under development. Expect frequent changes. What follows is a description of the current implementation.
At present, the OEF's capabilities are fulfilled by three components:
@@ -30,7 +29,7 @@ Agents can receive messages from other agents if they are both connected to the
### Search and Discovery
-A simple OEF (sOEF) node allows agents to discover each other. In particular, agents can register themselves and the services they offer, and can search for agents who offer specific services.
+A simple OEF (sOEF) node allows agents to discover each other. In particular, agents can register themselves and the services they offer, and can search for agents who offer specific services.
For two agents to be able to find each other, at least one must register itself on the sOEF and the other must query the sOEF node for it. Detailed documentation is provided here.
@@ -48,7 +47,7 @@ The Python implementation of the AEA Framework currently integrates with three l
However, the framework makes it straightforward for any developer to add support for other ledgers.
-### AEAs as second layer technology
+### AEAs as Second Layer Technology
The following presentation discusses how AEAs can be seen as second layer technology to ledgers.
diff --git a/docs/oracle-demo.md b/docs/oracle-demo.md
index d1d0d17046..2c49d28c83 100644
--- a/docs/oracle-demo.md
+++ b/docs/oracle-demo.md
@@ -1,3 +1,5 @@
+# Oracle Skills
+
This demo shows how an AEA can be used to maintain an oracle and how another AEA can request the oracle value.
## Discussion
@@ -6,15 +8,15 @@ This demo shows how an AEA can be used to maintain an oracle and how another AEA
This demonstration shows how to set up a simple oracle agent who deploys an oracle contract and updates the contract with a token price fetched from a public API. It also shows how to create an oracle client agent that can request the value from the oracle contract.
-## Preparation instructions
-
+## Preparation Instructions
+
### Dependencies
Follow the Preliminaries and Installation sections from the AEA quick start.
## Demo
-### Create the oracle AEA
+### Create the Oracle AEA
Fetch the AEA that will deploy and update the oracle contract.
@@ -24,104 +26,106 @@ cd coin_price_oracle
aea install
```
-Alternatively, create from scratch (and customize the data source)
-
-
-Create the AEA that will deploy the contract.
-
-``` bash
-aea create coin_price_oracle
-cd coin_price_oracle
-aea add connection fetchai/http_client:0.24.5
-aea add connection fetchai/ledger:0.21.4
-aea add connection fetchai/prometheus:0.9.5
-aea add skill fetchai/advanced_data_request:0.7.5
-aea add skill fetchai/simple_oracle:0.16.4
-aea config set --type dict agent.dependencies \
-'{
- "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"},
- "aea-ledger-ethereum": {"version": "<2.0.0,>=1.0.0"}
-}'
-aea config set agent.default_connection fetchai/ledger:0.21.4
-aea install
-```
-
-Set the URL for the data request skill:
-``` bash
-aea config set --type str vendor.fetchai.skills.advanced_data_request.models.advanced_data_request_model.args.url "https://api.coingecko.com/api/v3/simple/price?ids=fetch-ai&vs_currencies=usd"
-```
-
-Specify the name and JSON path of the data to fetch from the API:
-``` bash
-aea config set --type list vendor.fetchai.skills.advanced_data_request.models.advanced_data_request_model.args.outputs '[{"name": "price", "json_path": "fetch-ai.usd"}]'
-```
-
-Set the name of the oracle value in the simple oracle skill:
-``` bash
-aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.oracle_value_name price
-```
-
-Then update the agent configuration with the default routing:
-``` bash
-aea config set --type dict agent.default_routing \
-'{
-"fetchai/contract_api:1.1.6": "fetchai/ledger:0.21.4",
-"fetchai/http:1.1.6": "fetchai/http_client:0.24.5",
-"fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4"
-}'
-```
-
-Update the default ledger.
-``` bash
-aea config set agent.default_ledger fetchai
-```
-
-Set the following configuration for the oracle skill:
-``` bash
-aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.ledger_id fetchai
-aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.update_function update_oracle_value
-```
-
-
-
+??? note "Alternatively, create from scratch (and customize the data source):"
+ Create the AEA that will deploy the contract.
+
+ ``` bash
+ aea create coin_price_oracle
+ cd coin_price_oracle
+ aea add connection fetchai/http_client:0.24.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add connection fetchai/prometheus:0.9.5
+ aea add skill fetchai/advanced_data_request:0.7.5
+ aea add skill fetchai/simple_oracle:0.16.4
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"},
+ "aea-ledger-ethereum": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/ledger:0.21.4
+ aea install
+ ```
+
+ Set the URL for the data request skill:
+
+ ``` bash
+ aea config set --type str vendor.fetchai.skills.advanced_data_request.models.advanced_data_request_model.args.url "https://api.coingecko.com/api/v3/simple/price?ids=fetch-ai&vs_currencies=usd"
+ ```
+
+ Specify the name and JSON path of the data to fetch from the API:
+
+ ``` bash
+ aea config set --type list vendor.fetchai.skills.advanced_data_request.models.advanced_data_request_model.args.outputs '[{"name": "price", "json_path": "fetch-ai.usd"}]'
+ ```
+
+ Set the name of the oracle value in the simple oracle skill:
+
+ ``` bash
+ aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.oracle_value_name price
+ ```
+
+ Then update the agent configuration with the default routing:
+
+ ``` bash
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/contract_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/http:1.1.6": "fetchai/http_client:0.24.5",
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4"
+ }'
+ ```
+
+ Update the default ledger.
+
+ ``` bash
+ aea config set agent.default_ledger fetchai
+ ```
+
+ Set the following configuration for the oracle skill:
+
+ ``` bash
+ aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.ledger_id fetchai
+ aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.update_function update_oracle_value
+ ```
This demo runs on the `fetchai` ledger by default. Set the following variable for use in the configuration steps:
+
``` bash
LEDGER_ID=fetchai
```
-Alternatively, configure the agent to use an ethereum ledger
-
+??? note "Alternatively, configure the agent to use an ethereum ledger:"
-``` bash
-LEDGER_ID=ethereum
-```
+ ``` bash
+ LEDGER_ID=ethereum
+ ```
-Update the default ledger.
-``` bash
-aea config set agent.default_ledger ethereum
-```
+ Update the default ledger.
-Set the following configuration for the oracle skill:
-``` bash
-aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.ledger_id ethereum
-aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.update_function updateOracleValue
-```
+ ``` bash
+ aea config set agent.default_ledger ethereum
+ ```
+
+ Set the following configuration for the oracle skill:
-
-
+ ``` bash
+ aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.ledger_id ethereum
+ aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.update_function updateOracleValue
+ ```
Additionally, create the private key for the oracle AEA. Generate and add a key for use with the ledger:
+
``` bash
aea generate-key $LEDGER_ID --add-key
```
If running on a testnet (not including Ganache), generate some wealth for your AEA:
+
``` bash
aea generate-wealth $LEDGER_ID
```
-### Create the oracle client AEA
+### Create the Oracle Client AEA
From a new terminal (in the same top-level directory), fetch the AEA that will deploy the oracle client contract and call the function that requests the coin price from the oracle contract.
@@ -131,66 +135,62 @@ cd coin_price_oracle_client
aea install
```
-Alternatively, create from scratch
-
-
-Create the AEA that will deploy the contract.
-
-``` bash
-aea create coin_price_oracle_client
-cd coin_price_oracle_client
-aea add connection fetchai/http_client:0.24.5
-aea add connection fetchai/ledger:0.21.4
-aea add skill fetchai/simple_oracle_client:0.13.4
-aea config set --type dict agent.dependencies \
-'{
- "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"},
- "aea-ledger-ethereum": {"version": "<2.0.0,>=1.0.0"}
-}'
-aea config set agent.default_connection fetchai/ledger:0.21.4
-aea install
-```
-
-Then update the agent configuration with the default routing:
-``` bash
-aea config set --type dict agent.default_routing \
-'{
-"fetchai/contract_api:1.1.6": "fetchai/ledger:0.21.4",
-"fetchai/http:1.1.6": "fetchai/http_client:0.24.5",
-"fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4"
-}'
-```
-
-Set the default ledger:
-``` bash
-aea config set agent.default_ledger fetchai
-```
-Set the following configuration for the oracle client skill:
-``` bash
-aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.ledger_id fetchai
-aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.query_function query_oracle_value
-```
-
-
-
+??? note "Alternatively, create from scratch:"
+ Create the AEA that will deploy the contract.
+
+ ``` bash
+ aea create coin_price_oracle_client
+ cd coin_price_oracle_client
+ aea add connection fetchai/http_client:0.24.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/simple_oracle_client:0.13.4
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"},
+ "aea-ledger-ethereum": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/ledger:0.21.4
+ aea install
+ ```
+
+ Then update the agent configuration with the default routing:
+
+ ``` bash
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/contract_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/http:1.1.6": "fetchai/http_client:0.24.5",
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4"
+ }'
+ ```
+
+ Set the default ledger:
+
+ ``` bash
+ aea config set agent.default_ledger fetchai
+ ```
+ Set the following configuration for the oracle client skill:
+
+ ``` bash
+ aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.ledger_id fetchai
+ aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.query_function query_oracle_value
+ ```
Similar to above, set a temporary variable `LEDGER_ID=fetchai` or `LEDGER_ID=ethereum`.
-Follow these steps to configure for an ethereum ledger
-
+??? note "Follow these steps to configure for an ethereum ledger:"
+ Set the default ledger:
-Set the default ledger:
-``` bash
-aea config set agent.default_ledger ethereum
-```
-Set the following configuration for the oracle client skill:
-``` bash
-aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.ledger_id ethereum
-aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.query_function queryOracleValue
-```
+ ``` bash
+ aea config set agent.default_ledger ethereum
+ ```
+
+ Set the following configuration for the oracle client skill:
-
-
+ ``` bash
+ aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.ledger_id ethereum
+ aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.query_function queryOracleValue
+ ```
Create the private key for the oracle client AEA. Generate and add a key for use on the ledger:
@@ -199,98 +199,100 @@ aea generate-key $LEDGER_ID --add-key
```
If running on a testnet (not including Ganache), generate some wealth for your AEA:
+
``` bash
aea generate-wealth $LEDGER_ID
```
-### Configuring a ledger
+### Configuring a Ledger
The oracle AEAs require either a locally running ledger node or a connection to a remote ledger. By default, they are configured to use the latest `fetchai` testnet.
-Follow these steps to configure local Ethereum test node
-
-
-The easiest way to test the oracle agents on an Ethereum-based ledger to set up a local test node using Ganache. This can be done by running the following docker command from the directory you started from (in a new terminal). This command will also fund the accounts of the AEAs:
-
-``` bash
-docker run -p 8545:8545 trufflesuite/ganache-cli:latest --verbose --gasPrice=0 --gasLimit=0x1fffffffffffff --account="$(cat coin_price_oracle/ethereum_private_key.txt),1000000000000000000000" --account="$(cat coin_price_oracle_client/ethereum_private_key.txt),1000000000000000000000"
-```
-
-Run the following Python script (with web3 installed) from the top-level directory to deploy a mock Fetch ERC20 contract and give some test FET to the client agent.
-
-``` python
-import json
-import os
-from web3 import Web3
-
-FILE_DIR = os.path.dirname(os.path.realpath(__file__))
-CONTRACT_PATH = os.path.join(FILE_DIR, "coin_price_oracle_client/vendor/fetchai/contracts/fet_erc20/build/FetERC20Mock.json")
-ORACLE_PRIVATE_KEY_PATH = os.path.join(FILE_DIR, "coin_price_oracle/ethereum_private_key.txt")
-CLIENT_PRIVATE_KEY_PATH = os.path.join(FILE_DIR, "coin_price_oracle_client/ethereum_private_key.txt")
-
-# Solidity source code
-with open(CONTRACT_PATH) as file:
- compiled_sol = json.load(file)
-
-# web3.py instance
-w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))
-
-# Import oracle account from private key and set to default account
-with open(ORACLE_PRIVATE_KEY_PATH) as file:
- private_key = file.read()
-oracle_account = w3.eth.account.privateKeyToAccount(private_key)
-w3.eth.defaultAccount = oracle_account.address
-
-# Import client account from private key
-with open(CLIENT_PRIVATE_KEY_PATH) as file:
- private_key = file.read()
-client_account = w3.eth.account.privateKeyToAccount(private_key)
-
-# Deploy mock Fetch ERC20 contract
-FetERC20Mock = w3.eth.contract(abi=compiled_sol['abi'], bytecode=compiled_sol['bytecode'])
-
-# Submit the transaction that deploys the contract
-tx_hash = FetERC20Mock.constructor(
- name="FetERC20Mock",
- symbol="MFET",
- initialSupply=int(1e23),
- decimals_=18).transact()
-
-# Wait for the transaction to be mined, and get the transaction receipt
-tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
-
-# Print out the contract address
-print("FetERC20Mock contract deployed at:", tx_receipt.contractAddress)
-
-# Get deployed contract
-fet_erc20_mock = w3.eth.contract(address=tx_receipt.contractAddress, abi=compiled_sol['abi'])
-
-# Transfer some test FET to oracle client account
-tx_hash = fet_erc20_mock.functions.transfer(client_account.address, int(1e20)).transact()
-tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
-```
-
-Set the ERC20 contract address for the oracle AEA
-``` bash
-aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.erc20_address $ERC20_ADDRESS
-```
-as well as for the oracle client AEA
-``` bash
-aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.erc20_address $ERC20_ADDRESS
-```
-where `ERC20_ADDRESS` is in the output of the script above.
-
-
-
-
-### Run the oracle AEA
+??? note "Follow these steps to configure local Ethereum test node:"
+ The easiest way to test the oracle agents on an Ethereum-based ledger to set up a local test node using Ganache. This can be done by running the following docker command from the directory you started from (in a new terminal). This command will also fund the accounts of the AEAs:
+
+ ``` bash
+ docker run -p 8545:8545 trufflesuite/ganache-cli:latest --verbose --gasPrice=0 --gasLimit=0x1fffffffffffff --account="$(cat coin_price_oracle/ethereum_private_key.txt),1000000000000000000000" --account="$(cat coin_price_oracle_client/ethereum_private_key.txt),1000000000000000000000"
+ ```
+
+ Run the following Python script (with web3 installed) from the top-level directory to deploy a mock Fetch ERC20 contract and give some test FET to the client agent.
+
+ ``` python
+ import json
+ import os
+ from web3 import Web3
+
+ FILE_DIR = os.path.dirname(os.path.realpath(__file__))
+ CONTRACT_PATH = os.path.join(FILE_DIR, "coin_price_oracle_client/vendor/fetchai/contracts/fet_erc20/build/FetERC20Mock.json")
+ ORACLE_PRIVATE_KEY_PATH = os.path.join(FILE_DIR, "coin_price_oracle/ethereum_private_key.txt")
+ CLIENT_PRIVATE_KEY_PATH = os.path.join(FILE_DIR, "coin_price_oracle_client/ethereum_private_key.txt")
+
+ # Solidity source code
+ with open(CONTRACT_PATH) as file:
+ compiled_sol = json.load(file)
+
+ # web3.py instance
+ w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))
+
+ # Import oracle account from private key and set to default account
+ with open(ORACLE_PRIVATE_KEY_PATH) as file:
+ private_key = file.read()
+ oracle_account = w3.eth.account.privateKeyToAccount(private_key)
+ w3.eth.defaultAccount = oracle_account.address
+
+ # Import client account from private key
+ with open(CLIENT_PRIVATE_KEY_PATH) as file:
+ private_key = file.read()
+ client_account = w3.eth.account.privateKeyToAccount(private_key)
+
+ # Deploy mock Fetch ERC20 contract
+ FetERC20Mock = w3.eth.contract(abi=compiled_sol['abi'], bytecode=compiled_sol['bytecode'])
+
+ # Submit the transaction that deploys the contract
+ tx_hash = FetERC20Mock.constructor(
+ name="FetERC20Mock",
+ symbol="MFET",
+ initialSupply=int(1e23),
+ decimals_=18).transact()
+
+ # Wait for the transaction to be mined, and get the transaction receipt
+ tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
+
+ # Print out the contract address
+ print("FetERC20Mock contract deployed at:", tx_receipt.contractAddress)
+
+ # Get deployed contract
+ fet_erc20_mock = w3.eth.contract(address=tx_receipt.contractAddress, abi=compiled_sol['abi'])
+
+ # Transfer some test FET to oracle client account
+ tx_hash = fet_erc20_mock.functions.transfer(client_account.address, int(1e20)).transact()
+ tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
+ ```
+
+ Set the ERC20 contract address for the oracle AEA
+
+ ``` bash
+ aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.erc20_address $ERC20_ADDRESS
+ ```
+
+ as well as for the oracle client AEA
+
+ ``` bash
+ aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.erc20_address $ERC20_ADDRESS
+ ```
+
+ where `ERC20_ADDRESS` is in the output of the script above.
+
+### Run the Oracle AEA
Run the oracle agent. This will deploy a contract to the testnet, grant oracle permissions to the AEA's wallet address, and periodically update the contract with the latest price of FET (or whichever coin was specified).
+
``` bash
aea run
```
After a few moments, you should see the following notices in the logs:
+
``` bash
info: [coin_price_oracle] Oracle contract successfully deployed at address: ...
...
@@ -298,28 +300,35 @@ info: [coin_price_oracle] Oracle role successfully granted!
...
info: [coin_price_oracle] Oracle value successfully updated!
```
+
The oracle contract will continue to be updated with the latest retrieved coin price at the default time interval (every 15 seconds).
-### Set the ERC20 and oracle contract addresses for the oracle client AEA:
+### Set the ERC20 and Oracle Contract Addresses for the Oracle Client AEA
+
``` bash
aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.oracle_contract_address $ORACLE_ADDRESS
```
+
where `ORACLE_ADDRESS` should be set to the address shown in the oracle AEA logs:
+
``` bash
Oracle contract successfully deployed at address: ORACLE_ADDRESS
```
-### Run the oracle client AEA
+### Run the Oracle Client AEA
Run the oracle client agent. This will deploy an oracle client contract to the testnet, approve the contract to spend tokens on behalf of the AEA, and periodically call the contract function that requests the latest price of FET (or whichever coin was specified).
+
``` bash
aea run
```
After a few moments, you should see the following notices in the logs:
+
``` bash
info: [coin_price_oracle_client] Oracle client contract successfully deployed at address: ...
...
info: [coin_price_oracle_client] Oracle value successfully requested!
```
+
The AEA will continue to request the latest coin price at the default time interval (every 15 seconds).
diff --git a/docs/orm-integration.md b/docs/orm-integration.md
index 8182e596fd..a2b9459f07 100644
--- a/docs/orm-integration.md
+++ b/docs/orm-integration.md
@@ -1,3 +1,5 @@
+# ORM Integration
+
This guide demonstrates how to configure an AEA to interact with a database using `python-sql` objects.
## Discussion
@@ -8,13 +10,13 @@ Object-relational-mapping (ORM) is the idea of being able to write SQL queries,
- We assume, that we have a database `genericdb.db` with table name `data`. This table contains the following columns `timestamp` and `thermometer`.
- We assume, that we have a hardware thermometer sensor that adds the readings in the `genericdb` database (although you can follow the guide without having access to a sensor).
-Since the AEA framework enables us to use third-party libraries hosted on PyPI we can directly reference the external dependencies. The `aea install` command will install each dependency that the specific AEA needs and which is listed in the skill's YAML file.
+Since the AEA framework enables us to use third-party libraries hosted on PyPI we can directly reference the external dependencies. The `aea install` command will install each dependency that the specific AEA needs and which is listed in the skill's YAML file.
## Communication
-This diagram shows the communication between the various entities in the case where the thermometer data is successfully sold by the seller AEA to the buyer.
+This diagram shows the communication between the various entities in the case where the thermometer data is successfully sold by the seller AEA to the buyer.
-
+``` mermaid
sequenceDiagram
participant Search
participant Buyer_AEA
@@ -41,147 +43,145 @@ This diagram shows the communication between the various entities in the case wh
deactivate Buyer_AEA
deactivate Search
deactivate Seller_AEA
- deactivate Blockchain
-
-
+ deactivate Blockchain
+```
-## Preparation instructions
+## Preparation Instructions
### Dependencies
Follow the Preliminaries and Installation sections from the AEA quick start.
-## Demo instructions
+## Demo Instructions
This demo involves a true ledger transaction on Fetch.ai's `testnet` network or Ethereum's `ropsten`. This demo assumes the buyer trusts the seller AEA to send the data upon successful payment.
-### Create the seller AEA
+### Create the Seller AEA
First, fetch the seller AEA which provides thermometer data:
-``` bash
-aea fetch fetchai/thermometer_aea:0.30.4 --alias my_thermometer_aea
-cd my_thermometer_aea
-aea install
-aea build
-```
-
-Alternatively, create from scratch.
-
-
-
-
-### Create the buyer client
+??? note "Alternatively, create from scratch:"
+ The following steps create the seller from scratch:
+
+ ``` bash
+ aea create my_thermometer_aea
+ cd my_thermometer_aea
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/thermometer:0.27.5
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
+ }'
+ aea install
+ aea build
+ ```
+
+### Create the Buyer Client
In another terminal, fetch the buyer AEA:
-``` bash
-aea fetch fetchai/thermometer_client:0.32.4 --alias my_thermometer_client
-cd my_thermometer_client
-aea install
-aea build
-```
-
-Alternatively, create from scratch.
-
-The following steps create the car data client from scratch:
``` bash
-aea create my_thermometer_client
+aea fetch fetchai/thermometer_client:0.32.4 --alias my_thermometer_client
cd my_thermometer_client
-aea add connection fetchai/p2p_libp2p:0.27.4
-aea add connection fetchai/soef:0.27.5
-aea add connection fetchai/ledger:0.21.4
-aea add skill fetchai/thermometer_client:0.26.5
-aea config set --type dict agent.dependencies \
-'{
- "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"}
-}'
-aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
-aea config set --type dict agent.default_routing \
-'{
- "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
- "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
-}'
aea install
aea build
```
-
-
-
-
-### Add keys for the seller AEA
+??? note "Alternatively, create from scratch:"
+ The following steps create the car data client from scratch:
+
+ ``` bash
+ aea create my_thermometer_client
+ cd my_thermometer_client
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/thermometer_client:0.26.5
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
+ }'
+ aea install
+ aea build
+ ```
+
+### Add Keys for the Seller AEA
First, create the private key for the seller AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai `Dorado` use:
+
``` bash
aea generate-key fetchai
aea add-key fetchai fetchai_private_key.txt
```
Next, create a private key used to secure the AEA's communications:
+
``` bash
aea generate-key fetchai fetchai_connection_private_key.txt
aea add-key fetchai fetchai_connection_private_key.txt --connection
```
Finally, certify the key for use by the connections that request that:
+
``` bash
aea issue-certificates
```
-### Add keys and generate wealth for the buyer AEA
+### Add Keys and Generate Wealth for the Buyer AEA
The buyer needs to have some wealth to purchase the thermometer data.
First, create the private key for the buyer AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai use:
+
``` bash
aea generate-key fetchai
aea add-key fetchai fetchai_private_key.txt
```
Then, create some wealth for the buyer based on the network you want to transact with. On the Fetch.ai `Dorado` network:
+
``` bash
aea generate-wealth fetchai
```
Next, create a private key used to secure the AEA's communications:
+
``` bash
aea generate-key fetchai fetchai_connection_private_key.txt
aea add-key fetchai fetchai_connection_private_key.txt --connection
```
Finally, certify the key for use by the connections that request that:
+
``` bash
aea issue-certificates
```
-
-### Update the seller and buyer AEA skill configurations
+### Update the Seller and Buyer AEA Skill Configurations
In `my_thermometer_aea/vendor/fetchai/skills/thermometer/skill.yaml`, replace the `data_for_sale` with your data:
+
``` yaml
models:
...
@@ -205,6 +205,7 @@ models:
dependencies:
SQLAlchemy: {}
```
+
The `service_data` is used to register the service in the SOEF search node and make your agent discoverable.
In `my_thermometer_client/vendor/fetchai/skills/thermometer_client/skill.yaml`) ensure you have matching data.
@@ -233,11 +234,12 @@ models:
```
After changing the skill configuration files you should run the following command for both agents to install each dependency:
+
``` bash
aea install
```
-### Modify the seller's strategy
+### Modify the Seller's Strategy
Before being able to modify a package we need to eject it from vendor:
@@ -250,10 +252,13 @@ This will move the package to your `skills` directory and reset the version to `
Open `strategy.py` (in `my_thermometer_aea/skills/thermometer/strategy.py`) and make the following modifications:
Import the newly installed `sqlalchemy` library in your strategy.
+
``` python
import sqlalchemy as db
```
+
Then modify your strategy's `__init__` function to match the following code:
+
``` python
class Strategy(GenericStrategy):
"""This class defines a strategy for the agent."""
@@ -271,9 +276,10 @@ class Strategy(GenericStrategy):
self._tbl = self.create_database_and_table()
self.insert_data()
super().__init__(**kwargs)
-```
+```
+
+At the end of the file modify the `collect_from_data_source` function:
-At the end of the file modify the `collect_from_data_source` function:
``` python
def collect_from_data_source(self) -> Dict[str, str]:
"""Implement the logic to collect data."""
@@ -283,6 +289,7 @@ At the end of the file modify the `collect_from_data_source` function:
data_points = result_proxy.fetchall()
return {"data": json.dumps(list(map(tuple, data_points)))}
```
+
Also, create two new functions, one that creates a connection with the database, and another that populates the database with some fake data. This is needed in the case you do not have access to an actual thermometer sensor that inserts data in the database.
``` python
@@ -315,7 +322,7 @@ After modifying the skill we need to fingerprint it:
aea fingerprint skill {YOUR_AUTHOR_HANDLE}/thermometer:0.1.0
```
-### Run both AEAs
+### Run Both AEAs
First, run the thermometer (seller) AEA:
@@ -327,6 +334,7 @@ Once you see a message of the form `To join its network use multiaddr 'SOME_ADDR
This is the entry peer address for the local agent communication network created by the thermometer AEA.
Then, configure the thermometer client (buyer) to connect to this same local ACN by running the following command in the buyer terminal, replacing `SOME_ADDRESS` with the value you noted above:
+
``` bash
aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
'{
@@ -339,6 +347,7 @@ aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
```
Then run the thermometer client AEA:
+
``` bash
aea run
```
@@ -348,8 +357,9 @@ You will see that the AEAs negotiate and then transact using the configured test
## Delete the AEAs
When you're done, stop the agents (`CTRL+C`), go up a level and delete the AEAs.
-``` bash
+
+``` bash
cd ..
aea delete my_thermometer_aea
aea delete my_thermometer_client
-```
\ No newline at end of file
+```
diff --git a/docs/p2p-connection.md b/docs/p2p-connection.md
index f795c5c06b..5253d3f1b5 100644
--- a/docs/p2p-connection.md
+++ b/docs/p2p-connection.md
@@ -1,14 +1,17 @@
+# P2P Connection
+
The `fetchai/p2p_libp2p:0.27.4` connection allows AEAs to create a peer-to-peer communication network. In particular, the connection creates an overlay network which maps agents' public keys to IP addresses.
-## Local demo
+## Local Demo
First, make sure you have installed the crypto plugin
of the target test-net. E.g. for Fetch.AI:
+
``` bash
pip install aea-ledger-fetchai
```
-### Create and run the genesis AEA
+### Create and Run the Genesis AEA
Create one AEA as follows:
@@ -40,7 +43,7 @@ aea run --connections fetchai/p2p_libp2p:0.27.4
Once you see a message of the form `To join its network use multiaddr 'SOME_ADDRESS'` take note of the address. (Alternatively, use `aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.4 -u public_uri` to retrieve the address.)
This is the entry peer address for the local agent communication network created by the genesis AEA.
-### Create and run another AEA
+### Create and Run Another AEA
Create a second AEA:
@@ -75,6 +78,7 @@ aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
"public_uri": "127.0.0.1:9001"
}'
```
+
Here `SOME_ADDRESS` needs to be replaced with the list of multi addresses displayed in the log output of the genesis AEA.
Run the AEA:
@@ -85,29 +89,27 @@ aea run --connections fetchai/p2p_libp2p:0.27.4
You can inspect the `libp2p_node.log` log files of the AEA to see how they discover each other.
-
-
Note
-
Currently p2p_libp2p connection limits the total message size to 3 MB.
-
-
+!!! note
+ Currently `p2p_libp2p` connection limits the total message size to 3 MB.
-
-## Local demo with skills
+## Local Demo with Skills
Explore the demo section for further examples.
-## Deployed agent communication network
+## Deployed Agent Communication Network
You can connect to the deployed public test network by adding one or multiple of the following addresses as the `p2p_libp2p` connection's `entry_peers`:
``` yaml
/dns4/acn.fetch.ai/tcp/9000/p2p/16Uiu2HAkw1ypeQYQbRFV5hKUxGRHocwU5ohmVmCnyJNg36tnPFdx
```
+
``` yaml
/dns4/acn.fetch.ai/tcp/9001/p2p/16Uiu2HAmVWnopQAqq4pniYLw44VRvYxBUoRHqjz1Hh2SoCyjbyRW
```
Specifically, in an AEA's configuration `aea-config.yaml` add the above addresses for `entry_peers` as follows:
+
``` yaml
---
public_id: fetchai/p2p_libp2p:0.27.4
@@ -121,32 +123,35 @@ config:
Note, this configuration change must be made for all agents attempting to communicate with each other via the Agent Communication Network. For example, in demos involving two agents, both agents will need the above modifications to their respective `aea-config.yaml` file. However, remember to use different ports in `local_uri.` This will allow both agents to default to this communication network without the added overhead of opening ports and specifying hosts on the individual host machines running each agent.
-
-## Configuring the `connection.yaml` entries:
+## Configuring the `connection.yaml` Entries
To learn more about how to configure your `fetchai/p2p_libp2p:0.27.4` connection consult the `README.md` file supplied with the connection package.
-## Running Go peer standalone
+## Running Go Peer Standalone
You can run a peer node in _standalone mode_; that is, as a Go process with no dependency on the AEA framework. To facilitate such a deployment, we provide a script
`run_acn_node_standalone.py`
- and a corresponding
+ and a corresponding
Dockerfile.
First, you need to build the node's binary (`libp2p_node`) either:
- locally
- ``` bash
- svn export https://github.com/fetchai/agents-aea.git/trunk/packages/fetchai/connections/p2p_libp2p
- cd p2p_libp2p
- go build
- chmod +x libp2p_node
- ```
- Make sure you satisfy the system requirements.
+
+ ``` bash
+ svn export https://github.com/fetchai/agents-aea.git/trunk/packages/fetchai/connections/p2p_libp2p
+ cd p2p_libp2p
+ go build
+ chmod +x libp2p_node
+ ```
+
+ Make sure you satisfy the system requirements.
+
- or within a docker image using the provided Dockerfile:
- ``` bash
- docker build -t acn_node_standalone -f scripts/acn/Dockerfile .
- ```
+
+ ``` bash
+ docker build -t acn_node_standalone -f scripts/acn/Dockerfile .
+ ```
Next, to run the node binary in standalone mode, it requires values for the following entries:
@@ -154,35 +159,44 @@ Next, to run the node binary in standalone mode, it requires values for the foll
- `AEA_P2P_URI`: the local host and port to use by node
- `AEA_P2P_URI_PUBLIC`: the URI under which the peer is publicly reachable
- `AEA_P2P_DELEGATE_URI`: the URI under which the peer receives delegate connections
-- `AEA_P2P_ENTRY_URIS`: an optionally supplied list of comma-separated (`,`) entry Multiaddresses for the peer to bootstrap
+- `AEA_P2P_ENTRY_URIS`: an optionally supplied list of comma-separated (`,`) entry multiaddresses for the peer to bootstrap
The script allows different methods to pass these values to the node:
- As environment variables exported in the format `=` for each entry. Then:
- ``` bash
- python3 run_acn_node_standalone.py libp2p_node --config-from-env
- ```
+
+ ``` bash
+ python3 run_acn_node_standalone.py libp2p_node --config-from-env
+ ```
+
- Using an environment file containing the entries and their values in the format `=`, one entry per line. Then:
- ``` bash
- python3 run_acn_node_standalone.py libp2p_node --config-from-file
- ```
- or
- ``` bash
- docker run -v :/acn/acn_config -it acn_node_standalone --config-from-file /acn/acn_config
- ```
+
+ ``` bash
+ python3 run_acn_node_standalone.py libp2p_node --config-from-file
+ ```
+
+ or
+
+ ``` bash
+ docker run -v :/acn/acn_config -it acn_node_standalone --config-from-file /acn/acn_config
+ ```
+
- Using command line arguments:
- ``` bash
- python3 run_acn_node_standalone.py libp2p_node --key-file \
- --uri --uri-external \
- --uri-delegate \
- --entry-peers-maddrs ...
- ```
- or
- ``` bash
- docker run -v :/acn/key.txt -it acn_node_standalone --key-file /acn/key.txt \
- --uri --uri-external \
- --uri-delegate \
- --entry-peers-maddrs ...
- ```
+
+ ``` bash
+ python3 run_acn_node_standalone.py libp2p_node --key-file \
+ --uri --uri-external \
+ --uri-delegate \
+ --entry-peers-maddrs ...
+ ```
+
+ or
+
+ ``` bash
+ docker run -v :/acn/key.txt -it acn_node_standalone --key-file /acn/key.txt \
+ --uri --uri-external \
+ --uri-delegate \
+ --entry-peers-maddrs ...
+ ```
Note that the script will always save the configuration of the running node as a file under the name `.acn_config` in the current working directory. This can be handy when you want the exact same configuration for future runs of the node.
diff --git a/docs/package-imports.md b/docs/package-imports.md
index 75f5b0a5a7..20302f20d4 100644
--- a/docs/package-imports.md
+++ b/docs/package-imports.md
@@ -1,6 +1,8 @@
+# File Structure
+
An agent that is generated using the AEA framework is a modular system with different connections, contracts, protocols and skills.
-## File structure
+## An AEA Project's File Structure
The file structure of an AEA is fixed.
@@ -45,6 +47,7 @@ The developer can create new directories where necessary but the core structure
The `aea-config.yaml` is the top level configuration file of an AEA. It defines the global configurations as well as the component/package dependencies of the AEA. In some sense, the AEA can therefore be understood as an orchestrator of components.
For the AEA to use a package, the `public_id` for the package must be listed in the `aea-config.yaml` file, e.g.
+
``` yaml
connections:
- fetchai/stub:0.21.2
@@ -52,15 +55,15 @@ connections:
The above shows a part of the `aea-config.yaml`. If you see the connections, you will see that we follow a pattern of `author/name_package:version` to identify each package, also referred to as `public_id`. Here the `author` is the author of the package.
-## Vendor and package directories
+## Vendor and Package Directories
-The `vendor` folder contains the packages from the registry (local or remote) which have been developed by ourselves, other authors or Fetch.ai and are namespaced by author name.
+The `vendor` folder contains the packages from the registry (local or remote) which have been developed by ourselves, other authors or Fetch.ai and are placed in different namespaces according to the author name.
The packages we develop as part of the given AEA project are in the respective `connections/`, `contracts/`, `protocols/`, and `skills/` folders.
In the above configuration example, the package is authored by Fetch.ai and is located inside the `vendor/fetchai/connections` folder.
-## Importing modules from packages
+## Importing Modules from Packages
The way we import modules from packages inside the agent is in the form of `packages.{author}.{package_type}.{package_name}.{module_name}`. So for the above example, the import path is `packages.fetchai.connections.stub.{module_name}`.
@@ -68,24 +71,24 @@ The framework loads the modules from the local agent project and adds them to Py
We use a custom package management approach for the AEAs rather than the default Python one as it provides us with more flexibility, especially when it comes to extension beyond the Python ecosystem.
-## Python dependencies of packages
+## Python Dependencies of Packages
Python dependencies of packages are specified in their respective configuration files under `dependencies`. They will be installed when `aea install` is run on an agent project.
-## Create a package
+## Create a Package
If you want to create a package, you can use the CLI command `aea scaffold connection/contract/protocol/skill [name]` and this will create the package and put it inside the respective folder based on the command for example if we `scaffold` skill with the name `my_skill`
it will be located inside the folder skills in the root directory of the agent (`my_aea/skills/my_skill`).
-## Use published packages from the registry
+## Use Published Packages from the Registry
If you want to use a finished package, you can use a package from the registry.
There or two registries. The remote registry operated by Fetch.ai and a local registry stub. The local registry stub is a directory called `packages` which contains packages in a nested structure with authors on the top level, followed by the package type, then package name. An example of such a directory is the `packages` directory located in the AEA repository. The local registry is useful for development.
-You can use the CLI to interact with the registry. By default the CLI points to the remote registry. You can point it to the local registry via the flag `--local`.
+You can use the CLI to interact with the registry. By default, the CLI points to the remote registry. You can point it to the local registry via the flag `--local`.
-## Package versioning
+## Package Versioning
By default, the AEA can only handle one version per package. That is, a project should never use both `some_author/some_package_name:0.1.0` and `some_author/some_package_name:0.2.0`.
diff --git a/docs/performance-benchmark.md b/docs/performance-benchmark.md
index 3b6544e6e3..9b2af48262 100644
--- a/docs/performance-benchmark.md
+++ b/docs/performance-benchmark.md
@@ -1,31 +1,31 @@
+# Performance Benchmark
+
Test AEA framework performance.
## What is it?
The benchmark module is a set of tools to measure execution time, CPU load and memory usage of the AEA Python code. It produces text reports and draws charts to present the results.
-## How does it work?
+## How does it Work?
The framework:
-* spawns a dedicated process for each test run to execute the function to test.
-* measures CPU and RAM usage periodically.
-* waits for function exits or terminates them by timeout.
-* repeats test execution multiple times to get more accurate results.
-
-
+- spawns a dedicated process for each test run to execute the function to test.
+- measures CPU and RAM usage periodically.
+- waits for function exits or terminates them by timeout.
+- repeats test execution multiple times to get more accurate results.
-## How to use
+## How to Use
Steps to run a test:
-* Write a function you would like to test with all arguments you would like to parametrise, add some doc strings.
-* Split the function into two parts: prepare part and performance part. The prepare part will not be included in the measurement.
-* Add `BenchmarkControl` support, to notify framework to start measurement.
-* Import `TestCli` class, `TestCli().run(function_to_be_tested)`
-* Call it from console to get text results.
+- Write a function you would like to test with all arguments you would like to parametrise, add some doc strings.
+- Split the function into two parts: 'prepare' and 'performance' part. The 'prepare' part will not be included in the measurement.
+- Add `BenchmarkControl` support, to notify framework to start measurement.
+- Import `TestCli` class, `TestCli().run(function_to_be_tested)`
+- Call it from console to get text results.
-### Simple example
+### Simple Example
`cpuburn` - simple test of CPU load depends on idle sleep time. Shows how much CPU consumed during the execution.
@@ -59,8 +59,8 @@ if __name__ == "__main__":
TestCli(cpu_burn).run()
```
-
Run it with `python ./benchmark/cases/cpu_burn.py --help` to get help about usage.
+
``` bash
Usage: cpu_burn.py [OPTIONS] [ARGS]...
@@ -83,8 +83,8 @@ Options:
--help Show this message and exit.
```
-
Run it with `python ./benchmark/cases/cpu_burn.py` to start with default parameters.
+
``` bash
Test execution timeout: 10.0
Test execution measure period: 0.1
@@ -116,11 +116,11 @@ mem mean (kb): 53.98828125 ± 0
Here you can see test report for default arguments set.
-
Run with multiple arguments set, multiple repeats and draw a chart on resources
`python ./benchmark/cases/cpu_burn.py -N 5 -P 1 3,0.00001 3,0.001 3,0.01`
Report is:
+
``` bash
Test execution timeout: 10.0
Test execution measure period: 0.1
@@ -179,27 +179,24 @@ Chart is drawn for argument 1: sleep:
The most interesting part is CPU usage, as you can see CPU usage decreases with increasing value of idle sleep.
Memory usage and execution time can slightly differ per case execution.
+## Requirements for Tested Function
-## Requirements for tested function
-
-* The first function's argument has to be `benchmark: BenchmarkControl` which is passed by default by the framework.
-* All arguments except the fist one have to set default values.
-* Function doc string is required, it used for help information.
-* `benchmark.start()` has to be called once in the function body to start measurement. The timeout is counted from this point!
-* All the "prepare part" in the function that should not be measured has to be placed before `benchmark.start()`
-* Code to be measured has to go after `benchmark.start()`
-* Try to avoid infinitive loops and assume the test should exit after a while.
+- The first function's argument has to be `benchmark: BenchmarkControl` which is passed by default by the framework.
+- All arguments except the fist one have to set default values.
+- Function doc string is required, it used for help information.
+- `benchmark.start()` has to be called once in the function body to start measurement. The timeout is counted from this point!
+- All the "prepare part" in the function that should not be measured has to be placed before `benchmark.start()`
+- Code to be measured has to go after `benchmark.start()`
+- Try to avoid infinitive loops and assume the test should exit after a while.
+## Execution Options
-## Execution options
-
-* To pass an arguments set just provide it as a comma separated string like `10,0.1`
-* To pass several argument sets just separate them by white space `10,0.1 20,0.2`
-* `--timeout FLOAT` is test execution timeout in seconds. If the test takes more time, it will be terminated.
-* `--period FLOAT` is measurement interval in seconds, how often to make CPU and RAM usage measurements.
-* `-N, --num-executions INTEGER` - how many time to run the same argument set to make result more accurate.
-* `-P, --plot INTEGER` - Draw a chart using, using values of argument specified as values for axis X. argument positions started with 0, argument benchmark does not counted. for example `-P 0` will use `run_time` values, `-P 1` will use `sleep` values.
-
+- To pass an arguments set just provide it as a comma separated string like `10,0.1`
+- To pass several argument sets just separate them by white space `10,0.1 20,0.2`
+- `--timeout FLOAT` is test execution timeout in seconds. If the test takes more time, it will be terminated.
+- `--period FLOAT` is measurement interval in seconds, how often to make CPU and RAM usage measurements.
+- `-N, --num-executions INTEGER` - how many times to run the same argument set to make result more accurate.
+- `-P, --plot INTEGER` - Draw a chart, using values in the argument on the X axis, argument positions started with 0, argument benchmark not counted. For example `-P 0` will use `run_time` values, `-P 1` will use `sleep` values, and so on.
## Limitations
@@ -207,9 +204,7 @@ Currently, the benchmark framework does not measure resources consumed by subpro
Asynchronous functions or coroutines are not supported directly. So you have to set up an event loop inside test function and start loop manually.
-
-
-## Testing AEA: handlers example
+## Testing AEA: Handlers Example
Test react speed on specific messages amount.
@@ -247,8 +242,8 @@ def react_speed_in_loop(benchmark: BenchmarkControl, inbox_amount=1000) -> None:
aea_test_wrapper.stop_loop()
```
-
Create AEA wrapper with specified handler:
+
``` python
skill_definition = {
"handlers": {"dummy_handler": DummyHandler}
@@ -259,8 +254,8 @@ aea_test_wrapper = AEATestWrapper(
)
```
-
Populate inbox with dummy messages:
+
``` python
for _ in range(inbox_amount):
aea_test_wrapper.put_inbox(aea_test_wrapper.dummy_envelope())
@@ -271,6 +266,7 @@ Set timeout `0`, for maximum messages processing speed: `aea_test_wrapper.set_lo
Start benchmark: `benchmark.start()`
Start/stop AEA:
+
``` python
aea_test_wrapper.start()
...
@@ -278,6 +274,7 @@ aea_test_wrapper.stop()
```
Wait till messages present in inbox:
+
``` python
while not aea_test_wrapper.is_inbox_empty():
time.sleep(0.1)
diff --git a/docs/por.md b/docs/por.md
index 5a5c6e8acd..34030410a7 100644
--- a/docs/por.md
+++ b/docs/por.md
@@ -1,3 +1,4 @@
+# Proof of Representation
An AEA can use several key pairs. In particular, it can use different keys for securing its communication and for engaging in exchange. In the ACN we make use of this fact. To be able to signal to other agents that the address derived from one key pair is allowed to represent the agent controlling the other key pair, the key pair which is being represented must sign a message to prove that the other key pair is allowed to represent it. The `aea issue-certificates` command allows to create this association.
@@ -16,6 +17,6 @@ cert_requests:
save_path: .certs/conn_cert.txt
```
-The `identifier` refers to the environment for which the signature is generated, here `acn`. The `ledger_id` refers to the key pair to be used from the `private_key_paths` specified in `aea-config.yaml` for signing. The `not_after` and `not_before` fields specify constraints on the validity of the signature. The `public_key` can specify either the identifier of the key pair in `connection_private_key_paths` of which the public key is signed or it can contain the to be signed public key in plain text. The `save_path` specifies the path where the certificate is to be saved at.
+The `identifier` refers to the environment for which the signature is generated, here `acn`. The `ledger_id` refers to the key pair to be used from the `private_key_paths` specified in `aea-config.yaml` for signing. The `not_after` and `not_before` fields specify constraints on the validity of the signature. The `public_key` can specify either the identifier of the key pair in `connection_private_key_paths`, of which the public key is signed, or it can contain the to be signed public key in plain text. The `save_path` specifies the path where the certificate is to be saved at.
In the above example, the connection requests a certificate which is a signature of the `fetchai` public key in `connection_private_key_paths` with the `fetchai` key pair in `private_key_paths`. The validity of the signature will be constrained to the year `2021` for the environment `acn`.
diff --git a/docs/prometheus.md b/docs/prometheus.md
index 1f4094766c..14640ee71e 100644
--- a/docs/prometheus.md
+++ b/docs/prometheus.md
@@ -1,6 +1,9 @@
+# Prometheus Monitoring
+
AEAs can create and update prometheus metrics for remote monitoring by sending messages to the prometheus connection `fetchai/prometheus:0.9.5`.
To see this working in an agent, fetch and run the `coin_price_feed` agent and check `localhost:9090/metrics` to see the latest values of the metrics `num_retrievals` and `num_requests`:
+
``` bash
aea fetch fetchai/coin_price_feed:0.15.4
cd coin_price_feed
@@ -8,49 +11,48 @@ aea install
aea build
aea run
```
+
You can then instruct a prometheus server running on the same computing cluster as a deployed agent to scrape these metrics for remote monitoring and visualisation with the Prometheus/Grafana toolset.
To use this connection, add a model `prometheus_dialogues` to your skill to handle the metrics configuration and messages to the prometheus connection.
-Click here for example
-
-
-``` python
-class PrometheusDialogues(Model, BasePrometheusDialogues):
- """The dialogues class keeps track of all prometheus dialogues."""
-
- def __init__(self, **kwargs) -> None:
- """
- Initialize dialogues.
-
- :return: None
- """
-
- self.enabled = kwargs.pop("enabled", False)
- self.metrics = kwargs.pop("metrics", [])
-
- Model.__init__(self, **kwargs)
+??? note "Click here for example:"
+ ``` python
+ class PrometheusDialogues(Model, BasePrometheusDialogues):
+ """The dialogues class keeps track of all prometheus dialogues."""
- def role_from_first_message( # pylint: disable=unused-argument
- message: Message, receiver_address: Address
- ) -> BaseDialogue.Role:
- """Infer the role of the agent from an incoming/outgoing first message
-
- :param message: an incoming/outgoing first message
- :param receiver_address: the address of the receiving agent
- :return: The role of the agent
+ def __init__(self, **kwargs) -> None:
"""
- return PrometheusDialogue.Role.AGENT
-
- BasePrometheusDialogues.__init__(
- self,
- self_address=str(self.skill_id),
- role_from_first_message=role_from_first_message,
- )
-```
-
+ Initialize dialogues.
+
+ :return: None
+ """
+
+ self.enabled = kwargs.pop("enabled", False)
+ self.metrics = kwargs.pop("metrics", [])
+
+ Model.__init__(self, **kwargs)
+
+ def role_from_first_message( # pylint: disable=unused-argument
+ message: Message, receiver_address: Address
+ ) -> BaseDialogue.Role:
+ """Infer the role of the agent from an incoming/outgoing first message
+
+ :param message: an incoming/outgoing first message
+ :param receiver_address: the address of the receiving agent
+ :return: The role of the agent
+ """
+ return PrometheusDialogue.Role.AGENT
+
+ BasePrometheusDialogues.__init__(
+ self,
+ self_address=str(self.skill_id),
+ role_from_first_message=role_from_first_message,
+ )
+ ```
Then configure your metrics in the `skill.yaml` file. For example (from the `advanced_data_request` skill):
+
``` yaml
models:
prometheus_dialogues:
@@ -69,6 +71,7 @@ models:
```
Add a metric `metric_name` of type `metric_type` {`Gauge`, `Counter`, ...} and description `description` by sending a message with performative `ADD_METRIC` to the prometheus connection:
+
``` python
def add_prometheus_metric(
self,
@@ -103,7 +106,9 @@ def add_prometheus_metric(
# send message
self.context.outbox.put_message(message=message)
```
+
where `PROM_CONNECTION_ID` should be imported to your skill as follows:
+
``` python
from packages.fetchai.connections.prometheus.connection import (
PUBLIC_ID as PROM_CONNECTION_ID,
@@ -111,6 +116,7 @@ from packages.fetchai.connections.prometheus.connection import (
```
Update metric `metric_name` with update function `update_func` {`inc`, `set`, `observe`, ...} and value `value` by sending a message with performative `UPDATE_METRIC` to the prometheus connection:
+
``` python
def update_prometheus_metric(
self, metric_name: str, update_func: str, value: float, labels: Dict[str, str],
@@ -143,6 +149,7 @@ def update_prometheus_metric(
```
Initialize the metrics from the configuration file in the behaviour setup:
+
``` python
def setup(self) -> None:
"""Implement the setup of the behaviour"""
@@ -157,6 +164,7 @@ def setup(self) -> None:
Then call the `update_prometheus_metric` function from the appropriate places.
For example, the following code in `handlers.py` for the `advanced_data_request` skill updates the number of http requests served:
+
``` python
if self.context.prometheus_dialogues.enabled:
self.context.behaviours.advanced_data_request_behaviour.update_prometheus_metric(
@@ -166,73 +174,71 @@ if self.context.prometheus_dialogues.enabled:
Finally, you can add a `PrometheusHandler` to your skill to process response messages from the prometheus connection.
-Click here for example
-
-
-``` python
-class PrometheusHandler(Handler):
- """This class handles responses from the prometheus server."""
-
- SUPPORTED_PROTOCOL = PrometheusMessage.protocol_id
-
- def __init__(self, **kwargs):
- """Initialize the handler."""
- super().__init__(**kwargs)
-
- self.handled_message = None
-
- def setup(self) -> None:
- """Set up the handler."""
- if self.context.prometheus_dialogues.enabled:
- self.context.logger.info("setting up PrometheusHandler")
-
- def handle(self, message: Message) -> None:
- """
- Implement the reaction to a message.
-
- :param message: the message
- :return: None
- """
-
- message = cast(PrometheusMessage, message)
-
- # recover dialogue
- prometheus_dialogues = cast(
- PrometheusDialogues, self.context.prometheus_dialogues
- )
- prometheus_dialogue = cast(
- PrometheusDialogue, prometheus_dialogues.update(message)
- )
- if prometheus_dialogue is None:
- self._handle_unidentified_dialogue(message)
- return
-
- self.handled_message = message
- if message.performative == PrometheusMessage.Performative.RESPONSE:
- self.context.logger.debug(
- f"Prometheus response ({message.code}): {message.message}"
+??? note "Click here for example:"
+ ``` python
+ class PrometheusHandler(Handler):
+ """This class handles responses from the prometheus server."""
+
+ SUPPORTED_PROTOCOL = PrometheusMessage.protocol_id
+
+ def __init__(self, **kwargs):
+ """Initialize the handler."""
+ super().__init__(**kwargs)
+
+ self.handled_message = None
+
+ def setup(self) -> None:
+ """Set up the handler."""
+ if self.context.prometheus_dialogues.enabled:
+ self.context.logger.info("setting up PrometheusHandler")
+
+ def handle(self, message: Message) -> None:
+ """
+ Implement the reaction to a message.
+
+ :param message: the message
+ :return: None
+ """
+
+ message = cast(PrometheusMessage, message)
+
+ # recover dialogue
+ prometheus_dialogues = cast(
+ PrometheusDialogues, self.context.prometheus_dialogues
)
- else:
- self.context.logger.debug(
- f"got unexpected prometheus message: Performative = {PrometheusMessage.Performative}"
+ prometheus_dialogue = cast(
+ PrometheusDialogue, prometheus_dialogues.update(message)
)
-
- def _handle_unidentified_dialogue(self, msg: Message) -> None:
- """
- Handle an unidentified dialogue.
-
- :param msg: the unidentified message to be handled
- :return: None
- """
-
- self.context.logger.info(
- "received invalid message={}, unidentified dialogue.".format(msg)
- )
-
- def teardown(self) -> None:
- """
- Teardown the handler.
-
- :return: None
- """
-```
+ if prometheus_dialogue is None:
+ self._handle_unidentified_dialogue(message)
+ return
+
+ self.handled_message = message
+ if message.performative == PrometheusMessage.Performative.RESPONSE:
+ self.context.logger.debug(
+ f"Prometheus response ({message.code}): {message.message}"
+ )
+ else:
+ self.context.logger.debug(
+ f"got unexpected prometheus message: Performative = {PrometheusMessage.Performative}"
+ )
+
+ def _handle_unidentified_dialogue(self, msg: Message) -> None:
+ """
+ Handle an unidentified dialogue.
+
+ :param msg: the unidentified message to be handled
+ :return: None
+ """
+
+ self.context.logger.info(
+ "received invalid message={}, unidentified dialogue.".format(msg)
+ )
+
+ def teardown(self) -> None:
+ """
+ Teardown the handler.
+
+ :return: None
+ """
+ ```
diff --git a/docs/protocol-generator.md b/docs/protocol-generator.md
index d90b6c5f2a..dbe8c5a6ed 100644
--- a/docs/protocol-generator.md
+++ b/docs/protocol-generator.md
@@ -1,9 +1,6 @@
-
-
Note
-
This is currently an experimental feature. To try it follow this guide.
-
+# Generating Protocols
-## How to run
+## How to Run
First make sure you are inside your AEA's folder (see here on how to create a new agent).
@@ -17,25 +14,25 @@ where `` is the path to a
-
-
+!!! note
+ Note the protocol buffer compiler `protoc` that the generator uses requires a plugin to produce `go` code. Follow this instruction.
## Protocol Specification
+
A protocol can be described in a YAML file. This is called a _protocol specification_. The following is an example protocol specification:
``` yaml
@@ -92,27 +88,27 @@ keep_terminal_state_dialogues: true
...
```
-Each protocol specification must follow the YAML format, and have a minimum of one and a maximum of three YAML documents (each YAML document is enclosed within --- and ...).
+Each protocol specification must follow the YAML format, and have a minimum of one and a maximum of three YAML documents (each YAML document is enclosed within --- and ...).
### Basic Protocol Detail and Messages Syntax
-The first YAML document is mandatory in any protocol specification. It contains some basic information about the protocol and describes the syntax of communicative messages allowed under this protocol.
+The first YAML document is mandatory in any protocol specification. It contains some basic information about the protocol and describes the syntax of communicative messages allowed under this protocol.
The allowed fields and what they represent are:
- * `name`: The name of the protocol (written in snake_case)
- * `author`: The creator of the protocol
- * `version`: The current version of the protocol
- * `license`: Licensing information
- * `aea_version`: The version(s) of the framework that support this protocol. The format is described here.
- * `description`: A short description of the protocol
- * `protocol_specification_id`: The id which identifies the protocol for over-the-wire transport. This id is decoupled from the `protocol_id` (`{author}/{name}:{version}`) which is tied to the Python implementation.
+- `name`: The name of the protocol (written in snake_case)
+- `author`: The creator of the protocol
+- `version`: The current version of the protocol
+- `license`: Licensing information
+- `aea_version`: The version(s) of the framework that support this protocol. The format is described here.
+- `description`: A short description of the protocol
+- `protocol_specification_id`: The id which identifies the protocol for over-the-wire transport. This id is decoupled from the `protocol_id` (`{author}/{name}:{version}`) which is tied to the Python implementation.
-All of the above fields are mandatory and each is a key/value pair, where both key and value are YAML strings.
+All of the above fields are mandatory and each is a key/value pair, where both key and value are YAML strings.
Additionally, the first YAML document of a protocol specification must describe the syntax of valid messages according to this protocol. Therefore, it must contain another mandatory `speech-acts` field which defines the set of _performatives_ valid under this protocol, and a set of _contents_ for each performative.
-A _performative_ defines the type of a message (e.g. propose, accept) and has a set of _contents_ (or parameters) of varying types.
+A _performative_ defines the _type_ of a message (e.g. propose, accept) and has a set of _contents_ (or parameters) of varying types.
The format of the `speech-act` is as follows: `speech-act` is a dictionary, where each key is a **unique** _performative_ (YAML string), and the value is a _content_ dictionary. If a performative does not have any content, then its content dictionary is empty, for instance `accept` and `decline` in the specification above.
@@ -122,19 +118,19 @@ A content dictionary in turn has key/value pairs, where each key is the name of
The specific types which could be assigned to contents in a protocol specification are described in the table below.
-Types are either user defined (i.e. custom types) or primitive:
+Types are either user defined (i.e. custom types) or primitive:
+
+- Custom types are prepended with `ct:` and their format is described using regular expression in the table below.
+- Primitive types are prepended with `pt:`. There are different categories of primitive types. For example, `` such as integers and booleans, `` such as sets and lists, and so on. Primitive types are compositional:
+ - For example, consider `pt:set[...]` under ``, i.e. an unordered collection of elements without duplicates. A `pt:set[...]` describes the type of its elements (called "sub-type") in square brackets. The subtype of a `pt:set[...]` must be a `` (e.g. `pt:int`, `pt:bool`).
+ - In describing the format of types, `/` between two subtypes should be treated as "or". For example, the subtype of a `pt:optional[...]` is either a ``, ``, ``, `` or ``.
-* Custom types are prepended with `ct:` and their format is described using regular expression in the table below.
-* Primitive types are prepended with `pt:`. There are different categories of primitive types. For example, `` such as integers and booleans, `` such as sets and lists, and so on. Primitive types are compositional:
- - For example, consider `pt:set[...]` under ``, i.e. an unordered collection of elements without duplicates. A `pt:set[...]` describes the type of its elements (called "sub-type") in square brackets. The sub-type of a `pt:set[...]` must be a `` (e.g. `pt:int`, `pt:bool`).
- - In describing the format of types, `/` between two sub-types should be treated as "or". For example, the sub-type of a `pt:optional[...]` is either a ``, ``, ``, `` or ``.
+A multi type denotes an "or" separated set of subtypes. For example, a content whose type is specified as `pt:union[pt:str, pt:int]` should either be `pt:int` or `pt:float`.
-A multi type denotes an "or" separated set of sub-types. For example, a content whose type is specified as `pt:union[pt:str, pt:int]` should either be `pt:int` or `pt:float`.
+An optional type `pt:optional[...]` assigned to a content means the content's existence is optional, but if it is present, its type must match `pt:optional[...]`'s subtype.
-An optional type `pt:optional[...]` assigned to a content means the content's existence is optional, but if it is present, its type must match `pt:optional[...]`'s sub-type.
-
| Type | Code | Format | Example | In Python |
-| ------------------------------------| --------| --------------------------------------------------------------|------------------------------------------|------------------------------------|
+|-------------------------------------|---------|---------------------------------------------------------------|------------------------------------------|------------------------------------|
| Custom types1 | `` | `ct:RegExp(^[A-Z][a-zA-Z0-9]*$)` | `ct:DataModel` | Custom Class |
| Primitive types | `` | `pt:bytes` | `pt:bytes` | `bytes` |
| | | `pt:int` | `pt:int` | `int` |
@@ -163,29 +159,29 @@ You can optionally specify the structure of dialogues conforming to your protoco
The allowed fields and what they represent are:
- * `initiation`: The list of initial performatives
- * `reply`: The reply structure of speech-acts
- * `termination`: The list of terminal performatives
- * `roles`: The roles of players participating in a dialogue
- * `end_states`: The possible outcomes a terminated dialogue.
- * `keep_terminal_state_dialogues`: whether to keep or drop a terminated dialogue. When a storage backend is configured, the dialogues will be persisted in storage when kept.
+- `initiation`: The list of initial performatives
+- `reply`: The reply structure of speech-acts
+- `termination`: The list of terminal performatives
+- `roles`: The roles of players participating in a dialogue
+- `end_states`: The possible outcomes a terminated dialogue.
+- `keep_terminal_state_dialogues`: whether to keep or drop a terminated dialogue. When a storage backend is configured, the dialogues will be persisted in storage when kept.
-All of the above fields are mandatory.
+All of the above fields are mandatory.
-`initiation` is a YAML list, containing the performatives which can be used to start a dialogue.
+`initiation` is a YAML list, containing the performatives which can be used to start a dialogue.
-`reply` specifies for every performative, what its valid replies are. If a performative `per_1` is a valid reply to another `per_2`, this means a message with performative `per_1` can target a message whose performative is `per_2`.
+`reply` specifies for every performative, what its valid replies are. If a performative `per_1` is a valid reply to another `per_2`, this means a message with performative `per_1` can target a message whose performative is `per_2`.
-`reply` is a YAML dictionary, where the keys are the performatives (YAML string) defined in `speech-acts`. For each performative key, its value is a list of performatives which are defined to be a valid reply.
+`reply` is a YAML dictionary, where the keys are the performatives (YAML string) defined in `speech-acts`. For each performative key, its value is a list of performatives which are defined to be a valid reply.
For example, valid replies to `cfp` are `propose` and `decline`.
`termination` is a YAML list, containing the performatives which terminate a dialogue. Once any of these performatives are used in a dialogue, the dialogue is terminated and no other messages may be added to it.
`roles` is a YAML set, containing the roles players participating in dialogues can take. `roles` may contain one or two roles, each role being a YAML string. If there are two roles, each participant has a distinguished role in the dialogue (e.g. buyer and seller in the above specification). If there is only one role, then both participants in a dialogue have this same role.
-`end_states` lists the final states a terminated dialogue may have. `end_states` is a YAML list of strings.
+`end_states` lists the final states a terminated dialogue may have. `end_states` is a YAML list of strings.
-`keep_terminal_state_dialogues` has a boolean value and specifies whether the terminated dialogues of this protocol are to be kept or discarded.
+`keep_terminal_state_dialogues` has a boolean value and specifies whether the terminated dialogues of this protocol are to be kept or discarded.
## Design Guidelines
@@ -195,20 +191,19 @@ For example, valid replies to `cfp` are `propose` and `decline`.
3. If a speech-act is listed in `termination`, it must not have any replies in `reply`. The reason is simple: a terminal speech-act terminates a dialogue and so its reply can never be used.
-4. If a speech-act replies to no other speech-acts, it should be listed in `initiation` otherwise it could never be used in a dialogue (neither to a start a dialogue with, nor as a reply to another speech-act).
+4. If a speech-act replies to no other speech-acts, it should be listed in `initiation` otherwise it could never be used in a dialogue (neither to a start a dialogue with, nor as a reply to another speech-act).
### Notes
1. Currently, there is no way to describe custom types in a programming language independent format. This means that if a protocol specification includes custom types, the required implementations must be provided manually.
- * Before generating the protocol, the protocol buffer schema code for every custom type must be provided in the protocol specification.
- * Once the generator is called, it produces a `custom_types` module containing stub implementations for every custom type in the specification. The user must then modify this module and add implementations for every custom type in the specification. This includes implementations of how an object of a custom type can be encoded and decoded using protocol buffer.
- * Note, currently the way custom types are dealt with in the generator is admittedly inconvenient. The reason is, the generator does not know the structure of custom types and how they may be serialised/deserialised. Although this approach works, it is only a temporary solution until further work on a programming language-independent type description language is finished (similar to how the generator is designed to be a programming language-independent protocol description language).
+ _ Before generating the protocol, the protocol buffer schema code for every custom type must be provided in the protocol specification.
+ - Once the generator is called, it produces a `custom_types` module containing stub implementations for every custom type in the specification. The user must then modify this module and add implementations for every custom type in the specification. This includes implementations of how an object of a custom type can be encoded and decoded using protocol buffer.
+ - Note, currently the way custom types are dealt with in the generator is admittedly inconvenient. The reason is, the generator does not know the structure of custom types and how they may be serialized/deserialized. Although this approach works, it is only a temporary solution until further work on a programming language-independent type description language is finished (similar to how the generator is designed to be a programming language-independent protocol description language).
2. Currently, the first element in `pt:dict` cannot be a ``, `pt:float` or `pt:bytes`. This is because of a constraint in protocol buffer version 3 which is the framework's underlying serialisation mechanism. In a future version, we may address this limitation, in which case we will relax this constraint.
3. In protocol buffer version 3, which is the version used by the generator, there is no way to check whether an optional field (i.e. contents of type `pt:optional[...]`) has been set or not (see discussion here). In proto3, all optional fields are assigned a default value (e.g. `0` for integers types, `false` for boolean types, etc). Therefore, given an optional field whose value is the default value, there is no way to know from the optional field itself, whether it is not set, or in fact is set but its value happens to be the default value. Because of this, in the generated protocol schema file (the `.proto` file), for every optional content there is a second field that declares whether this field is set or not. We will maintain this temporary solution until a cleaner alternative is found.
-4. Be aware that currently, using the generated protocols in python, there might be some rounding errors when serialising and then deserialising values of `pt:float` contents.
-
+4. Be aware that currently, using the generated protocols in python, there might be some rounding errors when serialising and then deserializing values of `pt:float` contents.
-## Demo instructions
+## Demo Instructions
First, create a new AEA project:
@@ -226,6 +221,3 @@ aea generate protocol ../examples/protocol_specification_ex/sample.yaml
This will generate the protocol and place it in your AEA project.
Third, try generating other protocols by first defining a specification, then running the generator.
-
-
-
diff --git a/docs/protocol.md b/docs/protocol.md
index 67a3f3c337..c146e8bb2c 100644
--- a/docs/protocol.md
+++ b/docs/protocol.md
@@ -1,12 +1,12 @@
-`Protocols` define the structure of agent-to-agent and component-to-component interactions, which in the AEA world, are in the form of communication. To learn more about interactions and interaction protocols, see here.
+# Protocols
-Protocols in the AEA world provide definitions for:
-
-* `messages` defining the structure and syntax of messages;
+`Protocols` define the structure of agent-to-agent and component-to-component interactions, which in the AEA world, are in the form of communication. To learn more about interactions and interaction protocols, see here.
-* `serialization` defining how a message is encoded/decoded for transport; and optionally
+Protocols in the AEA world provide definitions for:
-* `dialogues` defining the structure of dialogues formed from exchanging series of messages.
+- `messages` defining the structure and syntax of messages;
+- `serialization` defining how a message is encoded/decoded for transport; and optionally
+- `dialogues` defining the structure of dialogues formed from exchanging series of messages.
@@ -16,62 +16,54 @@ Additional protocols - i.e. a new type of interaction - can be added as packages
We highly recommend you to **not** attempt writing your protocol manually as they tend to have involved logic; always use existing packages or the protocol generator!
-## Components of a protocol
+## Components of a Protocol
A protocol package contains the following files:
-* `__init__.py`
-* `message.py`, which defines message representation
-* `serialization.py`, which defines the encoding and decoding logic
-* two protobuf related files
+- `__init__.py`
+- `message.py`, which defines message representation
+- `serialization.py`, which defines the encoding and decoding logic
+- two protobuf related files
It optionally also contains
-* `dialogues.py`, which defines the structure of dialogues formed from the exchange of a series of messages
-* `custom_types.py`, which defines custom types
+- `dialogues.py`, which defines the structure of dialogues formed from the exchange of a series of messages
+- `custom_types.py`, which defines custom types
All protocols are for point to point interactions between two agents or agent-like services.
-
-
## Metadata
Each `Message` in an interaction protocol has a set of default fields:
-* `dialogue_reference: Tuple[str, str]`, a reference of the dialogue the message is part of. The first part of the tuple is the reference assigned to by the agent who first initiates the dialogue (i.e. sends the first message). The second part of the tuple is the reference assigned to by the other agent. The default value is `("", "")`.
-* `message_id: int`, the identifier of the message in a dialogue. The default value is `1`.
-* `target: int`, the id of the message this message is replying to. The default value is `0`.
-* `performative: Enum`, the purpose/intention of the message.
-* `sender: Address`, the address of the sender of this message.
-* `to: Address`, the address of the receiver of this message.
+- `dialogue_reference: Tuple[str, str]`, a reference of the dialogue the message is part of. The first part of the tuple is the reference assigned to by the agent who first initiates the dialogue (i.e. sends the first message). The second part of the tuple is the reference assigned to by the other agent. The default value is `("", "")`.
+- `message_id: int`, the identifier of the message in a dialogue. The default value is `1`.
+- `target: int`, the id of the message this message is replying to. The default value is `0`.
+- `performative: Enum`, the purpose/intention of the message.
+- `sender: Address`, the address of the sender of this message.
+- `to: Address`, the address of the receiver of this message.
The default values for `message_id` and `target` assume the message is the first message in a dialogue. Therefore, the `message_id` is set to `1` indicating the first message in the dialogue and `target` is `0` since the first message is the only message that does not reply to any other.
-By default, the values of `dialogue_reference`, `message_id`, `target` are set. However, most interactions involve more than one message being sent as part of the interaction and potentially multiple simultaneous interactions utilising the same protocol. In those cases, the `dialogue_reference` allows different interactions to be identified as such. The `message_id` and `target` are used to keep track of messages and their replies. For instance, on receiving of a message with `message_id=1` and `target=0`, the responding agent could respond with another with `message_id=2` and `target=1` replying to the first message. In particular, `target` holds the id of the message being replied to. This can be the preceding message, or an older one.
+By default, the values of `dialogue_reference`, `message_id`, `target` are set. However, most interactions involve more than one message being sent as part of the interaction and potentially multiple simultaneous interactions utilising the same protocol. In those cases, the `dialogue_reference` allows different interactions to be identified as such. The `message_id` and `target` are used to keep track of messages and their replies. For instance, on receiving of a message with `message_id=1` and `target=0`, the responding agent could respond with another with `message_id=2` and `target=1` replying to the first message. In particular, `target` holds the id of the message being replied to. This can be the preceding message, or an older one.
## Contents
-Each message may optionally have any number of contents of varying types.
+Each message may optionally have any number of contents of varying types.
-## Dialogue rules
+## Dialogue Rules
Protocols can optionally have a dialogue module. A _dialogue_, respectively _dialogues_ object, maintains the state of a single, respectively, all dialogues associated with a protocol.
The framework provides a number of helpful classes which implement most of the logic to maintain dialogues, namely the `Dialogue` and `Dialogues` base classes.
-## Custom protocol
+## Custom Protocol
The developer can generate custom protocols with the protocol generator. This lets the developer specify the speech-acts as well as optionally the dialogue structure (e.g. roles of agents participating in a dialogue, the states a dialogue may end in, and the reply structure of the speech-acts in a dialogue).
We highly recommend you **do not** attempt to write your own protocol code; always use existing packages or the protocol generator!
-## `fetchai/default:1.1.6` protocol
+## `fetchai/default:1.1.6` Protocol
The `fetchai/default:1.1.6` protocol is meant to be implemented by every AEA. It serves AEA to AEA interaction and includes three message performatives:
@@ -90,7 +82,8 @@ class Performative(Enum):
return self.value
```
-* The `DefaultMessage` of performative `DefaultMessage.Performative.BYTES` is used to send payloads of byte strings to other AEAs. An example is:
+- The `DefaultMessage` of performative `DefaultMessage.Performative.BYTES` is used to send payloads of byte strings to other AEAs. An example is:
+
``` python
from packages.fetchai.protocols.default.message import DefaultMessage
@@ -100,7 +93,8 @@ msg = DefaultMessage(
)
```
-* The `DefaultMessage` of performative `DefaultMessage.Performative.ERROR` is used to notify other AEAs of errors in an interaction, including errors with other protocols, by including an `error_code` in the payload:
+- The `DefaultMessage` of performative `DefaultMessage.Performative.ERROR` is used to notify other AEAs of errors in an interaction, including errors with other protocols, by including an `error_code` in the payload:
+
``` python
class ErrorCode(Enum):
"""This class represents an instance of ErrorCode."""
@@ -111,7 +105,9 @@ class ErrorCode(Enum):
UNSUPPORTED_SKILL = 3
INVALID_DIALOGUE = 4
```
+
An example is:
+
``` python
msg = DefaultMessage(
performative=DefaultMessage.Performative.ERROR,
@@ -121,7 +117,8 @@ msg = DefaultMessage(
)
```
-* The `DefaultMessage` of performative `DefaultMessage.Performative.END` is used to terminate a default protocol dialogue. An example is:
+- The `DefaultMessage` of performative `DefaultMessage.Performative.END` is used to terminate a default protocol dialogue. An example is:
+
``` python
from packages.fetchai.protocols.default.message import DefaultMessage
@@ -132,7 +129,7 @@ msg = DefaultMessage(
Each AEA's `fetchai/error:0.18.5` skill utilises the `fetchai/default:1.0.0` protocol for error handling.
-## `fetchai/oef_search:1.1.6` protocol
+## `fetchai/oef_search:1.1.6` Protocol
The `fetchai/oef_search:1.1.6` protocol is used by AEAs to interact with an SOEF search node to register and unregister their own services and search for services registered by other agents.
@@ -156,11 +153,14 @@ class Performative(Enum):
We show some example messages below:
-* To register a service, we require a reference to the dialogue in string form (used to keep different dialogues apart), for instance
+- To register a service, we require a reference to the dialogue in string form (used to keep different dialogues apart), for instance
+
``` python
my_dialogue_reference = "a_unique_register_service_dialogue_reference"
```
+
and a description of the service we would like to register, for instance
+
``` python
from aea.helpers.search.models import Description
@@ -170,7 +170,9 @@ my_service_description = Description(
data_model=my_data_model,
)
```
+
where we use, for instance
+
``` python
from aea.helpers.search.generic import GenericDataModel
@@ -189,7 +191,9 @@ data_model = {
}
my_data_model = GenericDataModel(data_model_name, data_model)
```
+
We can then create the message to register this service:
+
``` python
msg = OefSearchMessage(
performative=OefSearchMessage.Performative.REGISTER_SERVICE,
@@ -198,11 +202,14 @@ msg = OefSearchMessage(
)
```
-* To unregister a service, we require a reference to the dialogue in string form, for instance
+- To unregister a service, we require a reference to the dialogue in string form, for instance
+
``` python
my_dialogue_reference = "a_unique_unregister_service_dialogue_reference"
```
+
the description of the service we would like to unregister, say `my_service_description` from above and construct the message:
+
``` python
msg = OefSearchMessage(
performative=OefSearchMessage.Performative.UNREGISTER_SERVICE,
@@ -211,7 +218,8 @@ msg = OefSearchMessage(
)
```
-* To search a service, we similarly require a reference to the dialogue in string form, and then the query we would like the search node to evaluate, for instance
+- To search a service, we similarly require a reference to the dialogue in string form, and then the query we would like the search node to evaluate, for instance
+
``` python
from aea.helpers.search.models import Constraint, ConstraintType, Query
@@ -233,7 +241,9 @@ query = Query(
model=None,
)
```
+
We can then create the message to search these services:
+
``` python
oef_msg = OefSearchMessage(
performative=OefSearchMessage.Performative.SEARCH_SERVICES,
@@ -242,9 +252,10 @@ oef_msg = OefSearchMessage(
)
```
-* The SOEF search node will respond with a message `msg` of type `OefSearchMessage` with performative `OefSearchMessage.Performative.SEARCH_RESULT`. To access the tuple of agents which match the query, simply use `msg.agents`. In particular, this will return the agent addresses matching the query. The agent address can then be used to send a message to the agent utilising the P2P agent communication network and any protocol other than `fetchai/oef_search:1.0.0`.
+- The SOEF search node will respond with a message `msg` of type `OefSearchMessage` with performative `OefSearchMessage.Performative.SEARCH_RESULT`. To access the tuple of agents which match the query, simply use `msg.agents`. In particular, this will return the agent addresses matching the query. The agent address can then be used to send a message to the agent utilising the P2P agent communication network and any protocol other than `fetchai/oef_search:1.0.0`.
+
+- If the SOEF search node encounters any errors with the messages you send, it will return an `OefSearchMessage` of performative `OefSearchMessage.Performative.OEF_ERROR` and indicate the error operation encountered:
-* If the SOEF search node encounters any errors with the messages you send, it will return an `OefSearchMessage` of performative `OefSearchMessage.Performative.OEF_ERROR` and indicate the error operation encountered:
``` python
class OefErrorOperation(Enum):
@@ -257,7 +268,7 @@ class OefErrorOperation(Enum):
OTHER = 10000
```
-## `fetchai/fipa:1.1.6` protocol
+## `fetchai/fipa:1.1.6` Protocol
This protocol provides classes and functions necessary for communication between AEAs via a variant of the FIPA Agent Communication Language.
@@ -299,12 +310,12 @@ The `fetchai/fipa:1.1.6` protocol also defines a `FipaDialogue` class which spec
For examples of the usage of the `fetchai/fipa:1.1.6` protocol check out the generic skills step by step guide.
-
-### Fipa dialogue
+### Fipa Dialogue
Below, we give an example of a dialogue between two agents. In practice; both dialogues would be maintained in the respective agent.
We first create concrete implementations of `FipaDialogue` and `FipaDialogues` for the buyer and seller:
+
``` python
from aea.common import Address
from aea.helpers.search.models import Constraint, ConstraintType, Description, Query
@@ -432,6 +443,7 @@ class SellerDialogues(FipaDialogues):
```
Next, we can imitate a dialogue between the buyer and the seller. We first instantiate the dialogues models:
+
``` python
buyer_address = "buyer_address_stub"
seller_address = "seller_address_stub"
@@ -440,6 +452,7 @@ seller_dialogues = SellerDialogues(seller_address)
```
First, the buyer creates a message destined for the seller and updates the dialogues:
+
``` python
cfp_msg = FipaMessage(
message_id=1,
@@ -453,29 +466,35 @@ cfp_msg.counterparty = seller_addr
# Extends the outgoing list of messages.
buyer_dialogue = buyer_dialogues.update(cfp_msg)
```
+
If the message has been correctly constructed, the `buyer_dialogue` will be returned, otherwise it will be `None`.
In a skill, the message could now be sent:
+
``` python
# In a skill we would do:
# self.context.outbox.put_message(message=cfp_msg)
```
However, here we simply continue with the seller:
+
``` python
# change the incoming message field & counterparty
cfp_msg.is_incoming = True
cfp_msg.counterparty = buyer_address
```
+
In the skill, the above two lines will be done by the framework; you can simply receive the message in the handler.
We update the seller's dialogues model next to generate a new dialogue:
+
``` python
# Creates a new dialogue for the seller side based on the income message.
seller_dialogue = seller_dialogues.update(cfp_msg)
```
Next, the seller can generate a proposal:
+
``` python
# Generate a proposal message to send to the buyer.
proposal = Description({"foo1": 1, "bar1": 2})
@@ -495,6 +514,7 @@ seller_dialogue.update(proposal_msg)
```
In a skill, the message could now be sent:
+
``` python
# In a skill we would do:
# self.context.outbox.put_message(message=proposal_msg)
@@ -507,6 +527,3 @@ To retrieve a dialogue for a given message, we can do the following:
``` python
retrieved_dialogue = seller_dialogues.get_dialogue(cfp_msg)
```
-
-
-
\ No newline at end of file
diff --git a/docs/query-language.md b/docs/query-language.md
index f68e412c57..0214d5a1d3 100644
--- a/docs/query-language.md
+++ b/docs/query-language.md
@@ -1,3 +1,5 @@
+# The Query Language
+
We recommend reading Defining a Data Model before reading this section.
Along with the Data Model language, the AEA framework offers the possibility to specify _queries_ defined over data models.
@@ -17,22 +19,22 @@ That is, it imposes some limitations on the values the attribute can assume.
We have different types of constraints:
-* _relation_ constraints:
+- _relation_ constraints:
- * the author of the book must be _Stephen King_
- * the publication year must be greater than 1990
+ - the author of the book must be _Stephen King_
+ - the publication year must be greater than 1990
-* _set_ constraints:
+- _set_ constraints:
- * the genre must fall into the following set of genres: _Horror_, _Science fiction_, _Non-fiction_.
+ - the genre must fall into the following set of genres: _Horror_, _Science fiction_, _Non-fiction_.
-* _range_ constraints:
+- _range_ constraints:
- * the average rating must be between 3.5 and 4.5
+ - the average rating must be between 3.5 and 4.5
-* _distance_ constraints:
+- _distance_ constraints:
- * the nearest bookshop must be within a distance from a given location.
+ - the nearest bookshop must be within a distance from a given location.
The class that implements the constraint concept is `Constraint`
In the following, we show how to define them.
@@ -43,14 +45,14 @@ There are several
The types of relation constraints are:
-* Equal: `==`
-* Not Equal: `!=`
-* Less than: `<`
-* Less than or Equal: `<=`
-* Greater than: `>`
-* Greater than or Equal: `>=`
+- Equal: `==`
+- Not Equal: `!=`
+- Less than: `<`
+- Less than or Equal: `<=`
+- Greater than: `>`
+- Greater than or Equal: `>=`
-**Examples**: using the attributes we used before:
+**Examples**: using the attributes we used before:
``` python
from aea.helpers.search.models import Constraint, ConstraintType
@@ -80,9 +82,8 @@ The _set_ is a constraint type that allows you to restrict the values of the att
There are two kind of _set_ constraints:
-* In (a set of values): `in`
-* Not in (a set of values): `not_in`
-
+- In (a set of values): `in`
+- Not in (a set of values): `not_in`
**Examples**:
@@ -100,7 +101,6 @@ Constraint("year", ConstraintType("not_in", (1990, 1995, 2000)))
The _range_ is a constraint type that allows you to restrict the values of the attribute in a given range.
-
**Examples**:
``` python
@@ -139,12 +139,12 @@ close_to_tour_eiffel.check(Description({"position": colosseum})) # gives `False
## Constraint Expressions
-The constraints above mentioned can be combined with the common logical operators (i.e. and, or and not), yielding more complex expression.
+The constraints mentioned above can be combined with the common logical operators (i.e. and, or and not), yielding more complex expression.
-In particular we can specify any conjunction/disjunction/negations of the previous constraints or composite `ConstraintExpressions`, e.g.:
+In particular, we can specify any conjunction/disjunction/negations of the previous constraints or composite `ConstraintExpressions`, e.g.:
-* books that belong to _Horror_ **and** has been published after 2000, but **not** published by _Stephen King_.
-* books whose author is **either** _J. K. Rowling_ **or** _J. R. R. Tolkien_
+- books that belong to _Horror_ **and** has been published after 2000, but **not** published by _Stephen King_.
+- books whose author is **either** _J. K. Rowling_ **or** _J. R. R. Tolkien_
The classes that implement these operators are `Not`, `And` and `Or`.
@@ -211,7 +211,7 @@ Query([
Where `book_model` is the `DataModel` object. However, the data model is
an optional parameter, but to avoid ambiguity is recommended to include it.
-### The ``check`` method
+### The ``check`` Method
The `Query` class supports a way to check whether a `Description` matches with the query. This method is called `Query.check`.
@@ -241,11 +241,10 @@ A `Query` object must satisfy some conditions in order to be instantiated.
- For every constraint expression that constitute the query, check if they are _valid with respect to the data model_.
-
A `ConstraintExpr` `c` (that is, one of `And`, `Or`, `Not`, `Constraint`) is _valid with respect to a_ `DataModel` if:
- If `c` is an instance of `And`, `Or` or `Not`, then
- every subexpression of `c` must be valid (with respect to to the data model);
+ every subexpression of `c` must be valid (with respect to the data model);
- If `c` is an instance of `Constraint`, then:
- if the constraint type is one of `<`, `<=`, `>`,
`>=`, the value in the constructor must be one of `str`, `int` or `float`.
@@ -263,4 +262,3 @@ Constraint("foo", ConstraintType("==", True))
```
Consider a `DataModel` where there is an `Attribute` `"foo"` of type `str`. Then the constraint is not compatible with the mentioned data model, because the constraint expect an equality comparison with a boolean `True`, instead of a `str`.
-
diff --git a/docs/questions-and-answers.md b/docs/questions-and-answers.md
index aa1c7643a0..db0487802f 100644
--- a/docs/questions-and-answers.md
+++ b/docs/questions-and-answers.md
@@ -1,55 +1,46 @@
-What is an AEA?
-AEA stands for "Autonomous Economic Agent". An AEA can represent an individual, organisation or object and looks after its owner's interests. AEAs act independently of constant user input and autonomously execute actions to achieve their prescribed goals. Their purpose is to create economic value for their owners.
-
-
-How do AEAs talk to each other when they do not know each other?
-For an Autonomous Economic Agent (AEA) to talk to other AEAs, it first needs to find them. Once it does, it should ensure that they both use the same protocol for communication, and if so, they then have to send messages to each other.
-
-The AEA framework, together with some of the services it provides, address all three problems. You can read more about search and discovery here, protocols here, and the Agent Communication Network (ACN) here.
-
-
-How does an AEA use blockchain?
-The AEA framework enables agents to interact with blockchains to settle transactions. Currently, the framework has native support for three different networks: Fetch.ai, Ethereum and Cosmos.
-
-You can read more about the framework's integration with the different blockchains here and gain a high level overview here.
-
-
-How does one install third party libraries?
-The framework supports the use of third-party libraries hosted on PyPI. You can directly reference the external dependencies of an AEA package (e.g. skill) in its configuration file. From inside an AEA's project directory, the install command can be used to install all the dependencies of the AEA listed in the configuration files of any of it's packages.
-
-
-How does one connect to a database?
-You have two options to connect to a database: using the built-in storage solution or using a custom ORM (object-relational mapping) library and backend.
-
-The use of the built-in storage is explained here. For a detailed example of how to use an ORM, follow the ORM guide.
-
-
-How does one connect a frontend?
-There are multiple options. The most obvious is using an HTTP server connection and creating a client that communicates with this connection.
-
-You can find a more detailed discussion here.
-
-
-Is the AEA framework ideal for agent-based modelling?
-The goal of agent-based modelling (ABM) is to study the unknown (often complex) behaviour of systems comprised of agents with known (much simpler) behaviour. ABM is a popular technique for studying biological and social systems. Despite some similarities between ABM and the AEA framework, the two have fundamentally different goals. ABM's goal is not the design of agents or solving specific practical or engineering problems. Although it would be potentially possible, it would likely be inefficient to use the AEA framework for that kind of problem.
-
-You can find more details on the application areas of the AEA framework here.
-
-
-When a new AEA is created, is the vendor folder populated with some default packages?
-All AEA projects by default hold the fetchai/default:1.1.6, fetchai/state_update:1.1.6 and fetchai/signing:1.1.6 protocols. These (as all other packages installed from the registry) are placed in the vendor folder.
-
-You can find more details about the file structure here.
-
-
-Is there a standardization for private key files?
-Currently, the private keys are stored in .txt files. This is temporary and will be improved soon.
-
-
-How to use the same protocol in different skills?
-The details of envelope/message routing by the AEA framework are discussed in this guide.
-
-
-Why does the AEA framework use its own package registry?
-AEA packages could be described as personalized plugins for the AEA runtime. They are not like a library - they have no direct use outside the context of the framework - and therefore are not suitable for distribution via PyPI.
-
+# Q&A
+
+??? question "What is an AEA?"
+ AEA stands for "Autonomous Economic Agent". An AEA can represent an individual, organisation or object and looks after its owner's interests. AEAs act independently of constant user input and autonomously execute actions to achieve their prescribed goals. Their purpose is to create economic value for their owners.
+
+??? question "How do AEAs talk to each other when they do not know each other?"
+ For an Autonomous Economic Agent (AEA) to talk to other AEAs, it first needs to find them. Once it does, it should ensure that they both use the same protocol for communication, and if so, they then have to send messages to each other.
+
+ The AEA framework, together with some of the services it provides, address all three problems. You can read more about search and discovery here, protocols here, and the Agent Communication Network (ACN) here.
+
+??? question "How does an AEA use blockchain?"
+ The AEA framework enables agents to interact with blockchains to settle transactions. Currently, the framework has native support for three different networks: _Fetch.ai_, _Ethereum_ and _Cosmos_.
+
+ You can read more about the framework's integration with the different blockchains here and gain a high level overview here.
+
+??? question "How does one install third party libraries?"
+ The framework supports the use of third-party libraries hosted on PyPI. You can directly reference the external dependencies of an AEA package (e.g. skill) in its configuration file. From inside an AEA's project directory, the `install` command can be used to install all the dependencies of the AEA which are listed in the configuration files belonging to any of its packages.
+
+??? question "How does one connect to a database?"
+ You have two options to connect to a database: using the built-in storage solution or using a custom ORM (object-relational mapping) library and backend.
+
+ The use of the built-in storage is explained here. For a detailed example of how to use an ORM, follow the ORM guide.
+
+??? question "How does one connect a frontend?"
+ There are multiple options. The most obvious is using an HTTP server connection and creating a client that communicates with this connection.
+
+ You can find a more detailed discussion here.
+
+??? question "Is the AEA framework ideal for agent-based modelling?"
+ The goal of agent-based modelling (ABM) is to study the unknown (often complex) behaviour of systems comprised of agents with known (much simpler) behaviour. ABM is a popular technique for studying biological and social systems. Despite some similarities between ABM and the AEA framework, the two have fundamentally different goals. ABM's goal is not the design of agents or solving specific practical or engineering problems. Although it would be potentially possible, it would likely be inefficient to use the AEA framework for that kind of problems.
+
+ You can find more details on the application areas of the AEA framework here.
+
+??? question "When a new AEA is created, is the `vendor` folder populated with some default packages?"
+ All AEA projects by default hold the `fetchai/default:1.1.6`, `fetchai/state_update:1.1.6` and `fetchai/signing:1.1.6` protocols. These (as all other packages installed from the registry) are placed in the `vendor` folder.
+
+ You can find more details about the file structure here.
+
+??? question "Is there a standardization for private key files?"
+ Currently, the private keys are stored in `.txt` files. This is temporary and will be improved soon.
+
+??? question "How to use the same protocol in different skills?"
+ The details of envelope/message routing by the AEA framework are discussed in this guide.
+
+??? question "Why does the AEA framework use its own package registry?"
+ AEA packages could be described as personalized plugins for the AEA runtime. They are not like a library - they have no direct use outside the context of the framework - and therefore are not suitable for distribution via PyPI.
diff --git a/docs/quickstart.md b/docs/quickstart.md
index 4931a1cfac..0e1bdf9c05 100644
--- a/docs/quickstart.md
+++ b/docs/quickstart.md
@@ -1,3 +1,5 @@
+# AEA Quick Start
+
If you want to create Autonomous Economic Agents (AEAs) that can act independently of constant user input and autonomously execute actions to achieve their objective, you can use the AEA framework.
@@ -10,94 +12,80 @@ The AEA framework can be used on `Windows`, `Ubuntu/Debian` and `MacOS`.
You need Python 3.6 or higher as well as Go 1.14.2 or higher installed.
-GCC installation is required:
-* Ubuntu: `apt-get install gcc`
-* Windows (with `choco`
+GCC installation is required:
+
+- Ubuntu: `apt-get install gcc`
+- Windows (with `choco`
installed): `choco install mingw`
-* MacOS X (with home brew): `brew install gcc`
+- MacOS X (with home brew): `brew install gcc`
-### Option 1: Manual system preparation
+### Option 1: Manual System Preparation
Install a compatible Python and Go version on your system (see this external resource for a comprehensive guide).
-Manual approach
+??? note "Manual approach:"
-The following hints can help:
+ The following hints can help:
+
+ - To install Go, follow the official guide, depending on your platform here
+ - Python is already included by default on many Linux distributions (e.g. Ubuntu), as well as MacOS. To check you have the right version, open a terminal and run:
-
-
To install Go, follow the official guide, depending on your platform here
+ ``` bash
+ python3 --version
+ ```
-
Python is already included by default on
-many Linux distributions (e.g. Ubuntu), as well as MacOS.
-To check you have the right version, open a terminal and run:
-``` bash
-python3 --version
-```
-
+ - To install Python on Windows machines, you can download a specific release here.
+ - Ubuntu/Debian systems only: install Python headers, depending on the Python version you have installed on your machine. E.g. for Python 3.7:
-
To install Python on Windows machines, you can download a specific release here.
Ubuntu/Debian systems only: install Python headers,
- depending on the Python version you have installed on your machine.
- E.g. for Python 3.7:
-``` bash
-sudo apt-get install python3.7-dev
-```
-
+ - Windows users: install tools for Visual Studio.
-
-
-### Option 2: Using an automated install script
+### Option 2: Using an 'Automated Install' Script
We provide a script to automatically install all framework dependencies and the framework itself. This means that if you follow this option, you can skip the installation step that comes later on this page.
-Automated install script approach
+??? note "The 'Automated install' script approach:"
+ On macOS or Ubuntu run the following commands to download and install:
-On MacOS or Ubuntu run the following commands to download and install:
+ ``` bash
+ curl https://raw.githubusercontent.com/fetchai/agents-aea/main/scripts/install.sh --output install.sh
+ chmod +x install.sh
+ ./install.sh
+ ```
-``` bash
-curl https://raw.githubusercontent.com/fetchai/agents-aea/main/scripts/install.sh --output install.sh
-chmod +x install.sh
-./install.sh
-```
-
-On Windows: download https://raw.githubusercontent.com/fetchai/agents-aea/main/scripts/install.ps1, then run install.ps1 with the PowerShell terminal.
-
-
+ On Windows: download https://raw.githubusercontent.com/fetchai/agents-aea/main/scripts/install.ps1, then run install.ps1 with the PowerShell terminal.
### Option 3: Using Docker
+
We also provide a Docker image with all the needed dependencies.
-Docker approach
-
-To use the image you will first have to pull it and than run it with your current local directory mounted as a docker volume. This allows you to keep your agents local while working on them from within the docker container.
-
-To pull:
-
-``` bash
-docker pull fetchai/aea-user:latest
-```
+??? note "Docker approach:"
+ To use the image, you will first have to pull it, then run it with your current local directory mounted as a docker volume. This allows you to keep your agents local while working on them from within the docker container.
-To run the image on Linux and MacOs:
+ To pull:
-``` bash
-docker run -it -v $(pwd):/agents --workdir=/agents fetchai/aea-user:latest
-```
+ ``` bash
+ docker pull fetchai/aea-user:latest
+ ```
+
+ To run the image on Linux and MacOs:
-And on Windows:
+ ``` bash
+ docker run -it -v $(pwd):/agents --workdir=/agents fetchai/aea-user:latest
+ ```
-``` bash
-docker run -it -v %cd%:/agents --workdir=/agents fetchai/aea-user:latest
-```
+ And on Windows:
-Once successfully logged into the docker container,
-you can follow the rest of the guide the same way as if not using docker.
-
-
+ ``` bash
+ docker run -it -v %cd%:/agents --workdir=/agents fetchai/aea-user:latest
+ ```
+
+ Once successfully logged into the docker container,
+ you can follow the rest of the guide the same way as if not using docker.
## Preliminaries
@@ -125,32 +113,34 @@ Once installed, create a new environment and open it (here we use Python 3.7 but
touch Pipfile && pipenv --python 3.7 && pipenv shell
```
-
## Installation
-The following installs the entire AEA package which also includes a command-line interface (CLI). (You can skip this step if you used the install script above: Option 2 .)
+The following installs the entire AEA package which also includes a command-line interface (CLI). (You can skip this step if you used the 'install script' above: Option 2 .)
``` bash
pip install aea[all]
```
If you are using `zsh` rather than `bash` type
+
``` zsh
pip install 'aea[all]'
```
If the installation steps fail, it might be a dependency issue. Make sure you have followed all the relevant system specific steps above under `System Requirements`.
-## Setup author name
+## Setup Author Name
You can set up your author name using the `init` command:
+
``` bash
aea init
```
-## Register as an AEA author (optional)
+## Register as an AEA Author (optional)
+
+AEAs are composed of components. AEAs and AEA components can be developed by anyone and pushed to the AEA registry for others to use. To publish packages to the registry, we need to register an author name:
-AEAs are composed from components. AEAs and AEA components can be developed by anyone and pushed to the AEA registry for others to use. To publish packages to the registry, we need to register an author name:
``` bash
aea register
```
@@ -158,6 +148,7 @@ aea register
This is your unique author (or developer) name in the AEA ecosystem.
You should see a similar output (with your input instead of the sample username and email):
+
``` bash
Do you have a Registry account? [y/N]: n
Create a new account on the Registry now:
@@ -177,14 +168,12 @@ v1.2.4
AEA configurations successfully initialized: {'author': 'fetchai'}
```
-
-
Note
-
If you would rather not create an account on the registry at this point, then run aea init --local instead.
-
+!!! note
+ If you would rather not create an account on the registry at this point, then run `aea init --local` instead.
-## Echo skill demo
+## Echo Skill Demo
-This is a simple demo that introduces you to the main components of an AEA.
+This is a simple demo that introduces you to the main components of an AEA.
The fastest way to have your first AEA is to fetch one that already exists!
@@ -195,43 +184,45 @@ cd my_first_aea
To learn more about the folder structure of an AEA project read on here.
-Alternatively: step by step install
+??? note "Alternatively: step by step install:"
+ **Create a new AEA**
- Create a new AEA
-
-First, create a new AEA project and enter it.
-``` bash
-aea create my_first_aea
-cd my_first_aea
-```
-
-Add the stub connection
-
-Second, add the stub connection to the project.
-``` bash
-aea add connection fetchai/stub:0.21.2
-```
-
-Add the echo skill
-
-Third, add the echo skill to the project.
-``` bash
-aea add skill fetchai/echo:0.20.5
-```
-This copies the fetchai/echo:0.20.5 skill code containing the "behaviours", and "handlers" into the project, ready to run. The identifier of the skill fetchai/echo:0.20.5 consists of the name of the author of the skill, followed by the skill name and its version.
-
+ First, create a new AEA project and enter it.
+
+ ``` bash
+ aea create my_first_aea
+ cd my_first_aea
+ ```
+
+ **Add the stub connection**
+
+ Second, add the stub connection to the project.
+
+ ``` bash
+ aea add connection fetchai/stub:0.21.2
+ ```
+
+ **Add the echo skill**
+
+ Third, add the echo skill to the project.
+
+ ``` bash
+ aea add skill fetchai/echo:0.20.5
+ ```
+
+ This copies the fetchai/echo:0.20.5 skill code containing the "behaviours", and "handlers" into the project, ready to run. The identifier of the skill fetchai/echo:0.20.5 consists of the name of the author of the skill, followed by the skill name and its version.
-### Echo skill
+### Echo Skill
Just like humans, AEAs can have _skills_ to achieve their tasks. As an agent developer, you can create skills to add to your own AEAs. You can also choose to publish your skills so others add them to their AEAs. More details on skills can be found on this page .
The above agent has an echo skill, fetched from the registry, which simply echoes any messages it receives back to its sender.
-### Communication via envelopes and messages
+### Communication via Envelopes and Messages
AEAs use envelopes containing messages for communication. To learn more, check out the next section.
-### Stub connection
+### Stub Connection
Besides skills, AEAs may have one or more _connections_ enabling them to interface with entities in the outside world. For example, an HTTP client connection allows an AEA to communicate with HTTP servers. To read more about connections see this page.
@@ -253,13 +244,13 @@ For example:
recipient_aea,sender_aea,fetchai/default:1.0.0,\x08\x01\x12\x011*\x07\n\x05hello,
```
-### Install AEA dependencies
+### Install AEA Dependencies
``` bash
aea install
```
-### Add and create a private key
+### Add and Create a Private Key
All AEAs need a private key to run. Add one now:
@@ -299,7 +290,7 @@ info: Echo Behaviour: act method called.
The framework first calls the `setup` methods in the skill's `Handler` and `Behaviour` classes in that order; after which it repeatedly calls the `act` method of `Behaviour` class. This is the main agent loop in action.
-#### Add a message to the input file
+#### Add a Message to the Input File
You can send the AEA a message wrapped in an envelope using the CLI's `interact` command.
@@ -310,7 +301,7 @@ cd my_first_aea
aea interact
```
-You can now send messages to this AEA via an interactive tool by typing anything into the prompt and hitting enter twice (once to send the message and once more to check for a response).
+You can now send messages to this AEA via an interactive tool by typing anything into the prompt and hitting enter twice (once to send the message and once more to check for a response).
Let us send `hello` to this AEA (type `hello` and press enter twice). In the original terminal, you will see the `Echo Handler` dealing with this envelope and its contained message. You should see an output similar to the one below but with a different `dialogue_reference`.
@@ -321,26 +312,23 @@ info: Echo Behaviour: act method called.
info: Echo Behaviour: act method called.
```
-Manual approach
+??? note "Manual approach:"
+ Optionally, from a different terminal and same directory (i.e. the `my_first_aea` project), you can send the AEA a message wrapped in an envelope via the input file.
-Optionally, from a different terminal and same directory (i.e. the my_first_aea project), you can send the AEA a message wrapped in an envelope via the input file.
-
-``` bash
-echo 'my_first_aea,sender_aea,fetchai/default:1.0.0,\x12\x10\x08\x01\x12\x011*\t*\x07\n\x05hello,' >> input_file
-```
-
-You will see the Echo Handler dealing with the envelope and responding with the same message to the output_file, and also decoding the Base64 encrypted message in this case.
-
-``` bash
-info: Echo Behaviour: act method called.
-Echo Handler: message=Message(sender=sender_aea,to=my_first_aea,content=b'hello',dialogue_reference=('1', ''),message_id=1,performative=bytes,target=0), sender=sender_aea
-info: Echo Behaviour: act method called.
-info: Echo Behaviour: act method called.
-```
+ ``` bash
+ echo 'my_first_aea,sender_aea,fetchai/default:1.0.0,\x12\x10\x08\x01\x12\x011*\t*\x07\n\x05hello,' >> input_file
+ ```
-Note, due to the dialogue reference having to be incremented, you can only send the above envelope once! This approach does not work in conjunction with the aea interact command.
+ You will see the Echo Handler dealing with the envelope and responding with the same message to the output_file, and also decoding the Base64 encrypted message in this case.
-
+ ``` bash
+ info: Echo Behaviour: act method called.
+ Echo Handler: message=Message(sender=sender_aea,to=my_first_aea,content=b'hello',dialogue_reference=('1', ''),message_id=1,performative=bytes,target=0), sender=sender_aea
+ info: Echo Behaviour: act method called.
+ info: Echo Behaviour: act method called.
+ ```
+
+ Note, due to the dialogue reference having to be incremented, you can only send the above envelope once! This approach does not work in conjunction with the aea interact command.
### Stop the AEA
@@ -357,103 +345,100 @@ info: Echo Handler: teardown method called.
info: Echo Behaviour: teardown method called.
```
-### Write a test for the AEA
+### Write a Test for the AEA
We can write an end-to-end test for the AEA utilising helper classes provided by the framework.
-Writing tests
-
-The following test class replicates the preceding demo and tests it's correct behaviour. The AEATestCase classes are a tool for AEA developers to write useful end-to-end tests of their AEAs.
-
-First, get the packages directory from the AEA repository (execute from the working directory which contains the my_first_aea folder):
-
-``` bash
-svn export https://github.com/fetchai/agents-aea.git/trunk/packages
-```
-
-Then write the test:
-
-``` python
-import signal
-import time
-
-from aea.common import Address
-from aea.mail.base import Envelope
-from aea.protocols.base import Message
-from aea.protocols.dialogue.base import Dialogue
-
-from packages.fetchai.protocols.default.dialogues import DefaultDialogue, DefaultDialogues
-from packages.fetchai.protocols.default.message import DefaultMessage
-from packages.fetchai.protocols.default.serialization import DefaultSerializer
-from aea.test_tools.test_cases import AEATestCase
-
-
-class TestEchoSkill(AEATestCase):
- """Test that echo skill works."""
-
- def test_echo(self):
- """Run the echo skill sequence."""
- process = self.run_agent()
- is_running = self.is_running(process)
- assert is_running, "AEA not running within timeout!"
-
- # add sending and receiving envelope from input/output files
- sender_aea = "sender_aea"
- def role_from_first_message(
- message: Message, receiver_address: Address
- ) -> Dialogue.Role:
- return DefaultDialogue.Role.AGENT
- dialogues = DefaultDialogues(sender_aea, role_from_first_message)
- message_content = b"hello"
- message = DefaultMessage(
- performative=DefaultMessage.Performative.BYTES,
- dialogue_reference=dialogues.new_self_initiated_dialogue_reference(),
- content=message_content,
- )
- sent_envelope = Envelope(
- to=self.agent_name,
- sender=sender_aea,
- protocol_id=message.protocol_id,
- message=DefaultSerializer().encode(message),
- )
-
- self.send_envelope_to_agent(sent_envelope, self.agent_name)
-
- time.sleep(2.0)
- received_envelope = self.read_envelope_from_agent(self.agent_name)
-
- assert sent_envelope.to == received_envelope.sender
- assert sent_envelope.sender == received_envelope.to
- assert sent_envelope.protocol_id == received_envelope.protocol_id
- received_message = DefaultMessage.serializer.decode(received_envelope.message)
- assert message.content == received_message.content
-
- check_strings = (
- "Echo Handler: setup method called.",
- "Echo Behaviour: setup method called.",
- "Echo Behaviour: act method called.",
- "content={}".format(message_content),
- )
- missing_strings = self.missing_from_output(process, check_strings)
- assert (
- missing_strings == []
- ), "Strings {} didn't appear in agent output.".format(missing_strings)
-
- assert (
- self.is_successfully_terminated()
- ), "Echo agent wasn't successfully terminated."
-
-```
-
-Place the above code into a file test.py in your AEA project directory (the same level as the aea-config.yaml file).
-
-To run, execute the following:
-
-``` bash
-pytest test.py
-```
-
-
+??? note "Writing tests:"
+ The following test class replicates the preceding demo and tests its correct behaviour. The `AEATestCase` classes are a tool for AEA developers to write useful end-to-end tests of their AEAs.
+
+ First, get the `packages` directory from the AEA repository (execute from the working directory which contains the my_first_aea folder):
+
+ ``` bash
+ svn export https://github.com/fetchai/agents-aea.git/trunk/packages
+ ```
+
+ Then write the test:
+
+ ``` python
+ import signal
+ import time
+
+ from aea.common import Address
+ from aea.mail.base import Envelope
+ from aea.protocols.base import Message
+ from aea.protocols.dialogue.base import Dialogue
+
+ from packages.fetchai.protocols.default.dialogues import DefaultDialogue, DefaultDialogues
+ from packages.fetchai.protocols.default.message import DefaultMessage
+ from packages.fetchai.protocols.default.serialization import DefaultSerializer
+ from aea.test_tools.test_cases import AEATestCase
+
+
+ class TestEchoSkill(AEATestCase):
+ """Test that echo skill works."""
+
+ def test_echo(self):
+ """Run the echo skill sequence."""
+ process = self.run_agent()
+ is_running = self.is_running(process)
+ assert is_running, "AEA not running within timeout!"
+
+ # add sending and receiving envelope from input/output files
+ sender_aea = "sender_aea"
+ def role_from_first_message(
+ message: Message, receiver_address: Address
+ ) -> Dialogue.Role:
+ return DefaultDialogue.Role.AGENT
+ dialogues = DefaultDialogues(sender_aea, role_from_first_message)
+ message_content = b"hello"
+ message = DefaultMessage(
+ performative=DefaultMessage.Performative.BYTES,
+ dialogue_reference=dialogues.new_self_initiated_dialogue_reference(),
+ content=message_content,
+ )
+ sent_envelope = Envelope(
+ to=self.agent_name,
+ sender=sender_aea,
+ protocol_id=message.protocol_id,
+ message=DefaultSerializer().encode(message),
+ )
+
+ self.send_envelope_to_agent(sent_envelope, self.agent_name)
+
+ time.sleep(2.0)
+ received_envelope = self.read_envelope_from_agent(self.agent_name)
+
+ assert sent_envelope.to == received_envelope.sender
+ assert sent_envelope.sender == received_envelope.to
+ assert sent_envelope.protocol_id == received_envelope.protocol_id
+ received_message = DefaultMessage.serializer.decode(received_envelope.message)
+ assert message.content == received_message.content
+
+ check_strings = (
+ "Echo Handler: setup method called.",
+ "Echo Behaviour: setup method called.",
+ "Echo Behaviour: act method called.",
+ "content={}".format(message_content),
+ )
+ missing_strings = self.missing_from_output(process, check_strings)
+ assert (
+ missing_strings == []
+ ), "Strings {} didn't appear in agent output.".format(missing_strings)
+
+ assert (
+ self.is_successfully_terminated()
+ ), "Echo agent wasn't successfully terminated."
+
+ ```
+
+ Place the above code into a file test.py in your AEA project directory (the same level as the aea-config.yaml file).
+
+ To run, execute the following:
+
+ ``` bash
+ pytest test.py
+ ```
### Delete the AEA
@@ -463,16 +448,14 @@ Delete the AEA from the parent directory (`cd ..` to go to the parent directory)
aea delete my_first_aea
```
-## Next steps
+## Next Steps
To gain an understanding of the core components of the framework, please continue to the next page:
- Core components - Part 1
-For more demos, use cases or step by step guides, please check the following:
+For more demos, use cases or step-by-step guides, please check the following:
- Generic skill use case
- Weather skill demo
- Generic step by step guide
-
-
diff --git a/docs/raspberry-set-up.md b/docs/raspberry-set-up.md
index d90033dc8c..19b00bcbfd 100644
--- a/docs/raspberry-set-up.md
+++ b/docs/raspberry-set-up.md
@@ -1,9 +1,11 @@
+# Build an AEA on a Raspberry Pi
+
This guide explains how to run an AEA inside a Raspberry Pi.
## Prerequisites
-* Raspberry Pi 4 (You can also use Raspberry Pi3 b or Raspberry Pi3 b+)
-* Internet connection (preferably wireless to minimise the number of wires connecting into your device)
+- Raspberry Pi 4 (You can also use Raspberry Pi3 b or Raspberry Pi3 b+)
+- Internet connection (preferably wireless to minimise the number of wires connecting into your device)
## Preparing the Raspberry Pi
@@ -20,7 +22,7 @@ First download the tool from this guide to set up your SD card.
When you get to the step of choosing an operating system, select the downloaded and unzipped AEA Raspberry Pi Image (`AEA_RPI.IMG`), or for a manual installation, select the latest Raspberry Pi OS.
-Once you have set up your SD card, plug it into your Raspberry Pi, connect the power and boot up.
+Once you have set up your SD card, plug it into your Raspberry Pi, connect the power and boot up.
## Booting up with the AEA Raspberry Pi Image
@@ -28,13 +30,13 @@ After booting up, you may be prompted to log in as the `aea` user and the passwo
Next, navigate to settings menu to set up your internet connection.
Your Raspberry Pi is now ready to run an AEA!
You can find some preloaded demos in the folder `~/aea/demos`.
-To run these demos, navigate to one of the subfolders and enter `aea run`.
+To run these demos, navigate to one of the sub-folders and enter `aea run`.
-## Booting up with the Raspberry Pi OS for manual installation
+## Booting up with the Raspberry Pi OS for Manual Installation
-When you first boot your Raspberry Pi, you will be prompted to enter a password for the Raspberry Pi and your WiFi password so the device can access the internet. You may also be given the option to update the operating system and software. We recommend that you let the system update. Once finished you will be prompted to restart.
+When you first boot your Raspberry Pi, you will be prompted to enter a password for the Raspberry Pi and your Wi-Fi password so the device can access the internet. You may also be given the option to update the operating system and software. We recommend that you let the system update. Once finished you will be prompted to restart.
-Even if your Raspberry Pi updated itself, we recommend that you make sure it is completely up to date using the terminal. Open a Terminal window (your Raspberry Pi might restart a few times during this process):
+Even if your Raspberry Pi updated itself, we recommend that you make sure it is completely up-to-date using the terminal. Open a Terminal window (your Raspberry Pi might restart a few times during this process):
``` bash
sudo apt update -y
@@ -42,62 +44,65 @@ sudo apt-get update
sudo apt-get dist-upgrade
```
-## Install common dependencies
+## Install Common Dependencies
``` bash
sudo apt install cmake golang -y
```
-## Install less common dependencies (optional)
+## Install Less Common Dependencies (optional)
For some of the more advanced AEAs that make use of SciPy, such as the Car Park Detector, you will need some additional dependencies.
-Install additional dependencies with the enclosed steps
-
-
+ Revert to default swap space
+
+ ``` bash
+ sudo swapoff /var/swap.1
+ sudo rm /var/swap.1
+ ```
## Install the AEA Framework
-Add to the local `PATH` environment variable (this will happen automatically the next time you login):
+Add to the local `PATH` environment variable (this will happen automatically the next time you log in):
+
``` bash
export PATH="$HOME/.local/bin:$PATH"
```
Finally, install the AEA framework from PyPI:
+
``` bash
pip install aea[all]
```
Check to make sure installation was successful:
+
``` bash
aea --version
```
-Your Raspberry Pi is now ready to run an AEA!
\ No newline at end of file
+Your Raspberry Pi is now ready to run an AEA!
diff --git a/docs/runtime-cost.md b/docs/runtime-cost.md
index 751df8fbdb..a9b6f6affe 100644
--- a/docs/runtime-cost.md
+++ b/docs/runtime-cost.md
@@ -1,13 +1,15 @@
+# Profiling
-## Measuring runtime cost
+## Measuring Runtime Cost
It is important to emphasise the fact that the AEA is a framework, so ultimately its running cost will highly depend on the number and type of components which are being run as part of a given AEA. The other cost factor is determined by the cost of running the core framework itself and how fast and efficient the framework is in interconnecting the components.
These observations can provide guidance on what to report as part of the cost of running an AEA.
Here is a list of suggestion on how to measure the cost of running an AEA:
+
- the cost of running the framework itself: by running a minimal agent with an idle loop (the default one) with no connections, skills or protocols and measuring memory usage and CPU consumption as a baseline.
-- the cost of interconnecting components: by running an a agent with a basic skill (e.g. `fetchai/echo`) and measuring memory usage and CPU consumption relative to number of messages exchanged as well as bandwidth.
+- the cost of interconnecting components: by running an agent with a basic skill (e.g. `fetchai/echo`) and measuring memory usage and CPU consumption relative to number of messages exchanged as well as bandwidth.
- the cost of basic components: dialogues memory relative to number of messages, SOEF connection baseline memory usage, P2P connection baseline memory usage, smart contract baseline memory usage
The `aea run --profiling SECONDS` command can be used to report measures in all of the above scenarios.
diff --git a/docs/scaffolding.md b/docs/scaffolding.md
index 0165340fe1..2f1f163126 100644
--- a/docs/scaffolding.md
+++ b/docs/scaffolding.md
@@ -1,4 +1,6 @@
-## Scaffold generator
+# Scaffolding Packages
+
+## Scaffold Generator
The scaffold generator builds out the directory structure required when adding new skills, protocols, contracts and connections to the AEA.
@@ -11,28 +13,25 @@ cd my_aea
Then, enter into your project directory and scaffold your project skill, protocol, or connection.
-
-### Scaffold a skill
+### Scaffold a Skill
``` bash
aea scaffold skill my_skill
```
-
-### Scaffold a protocol
+### Scaffold a Protocol
``` bash
aea scaffold protocol my_protocol
```
-
-### Scaffold a contract
+### Scaffold a Contract
``` bash
aea scaffold contract my_contract
```
-### Scaffold a connection
+### Scaffold a Connection
``` bash
aea scaffold connection my_connection
@@ -47,5 +46,3 @@ aea fingerprint [package_name] [public_id]
```
Then you are ready to run the AEA.
-
-
\ No newline at end of file
diff --git a/docs/security.md b/docs/security.md
index 23e15b24f0..ceebff2bd0 100644
--- a/docs/security.md
+++ b/docs/security.md
@@ -1,3 +1,4 @@
+# Security
The AEA framework takes every care to follow best practice around security.
@@ -11,4 +12,4 @@ The following advice will help you when writing your own code:
- Try to avoid using the `pickle` module. Pickle should never be used for agent-to-agent communication protocols.
-- By design, the framework prevents skill code from accessing private keys directly, as they are not reachable from the skill execution context through attribute getters. However, if the flag `-p` or the option `--password` are not used when generating private keys for an AEA project via the aea CLI tool, the private keys will be stored in plaintext. This allows the skills to access them via interaction with the OS file system. We recommend to always specify a password to encrypt private keys by using the flag argument.
\ No newline at end of file
+- By design, the framework prevents skill code from accessing private keys directly, as they are not reachable from the skill execution context through attribute getters. However, if the flag `-p` or the option `--password` are not used when generating private keys for an AEA project via the aea CLI tool, the private keys will be stored in plaintext. This allows the skills to access them via interaction with the OS file system. We recommend to always specify a password to encrypt private keys by using the flag argument.
diff --git a/docs/simple-oef-usage.md b/docs/simple-oef-usage.md
index 3de8a60624..fc66e10822 100644
--- a/docs/simple-oef-usage.md
+++ b/docs/simple-oef-usage.md
@@ -1,14 +1,19 @@
+# SOEF Connection
+
You can use the SOEF in the agent framework by using the SOEF connection as a package in your agent project.
-## Add the SOEF package
-Check out the CLI guide on details how to add a connection. You will want to add the `fetchai/soef:0.27.5` connection package.
+## Add the SOEF Package
+
+Check out the CLI guide on details how to add a connection. You will want to add the `fetchai/soef:0.27.5` connection package.
+
+## Register your Agent and its Services
-## Register your agent and its services
+### Register Agent Location
-### Register agent location
To register your agent's location, you have to send a message in the `fetchai/oef_search:1.0.0` protocol to the SOEF connection.
First, define a data model for location data:
+
``` python
from aea.helpers.search.models import Attribute, DataModel, Location
@@ -18,9 +23,11 @@ AGENT_LOCATION_MODEL = DataModel(
"A data model to describe location of an agent.",
)
```
+
It is important to use this exact data model, as the SOEF connection can only process specific data models.
Second, create a location object:
+
``` python
from aea.helpers.search.models import Location
@@ -28,6 +35,7 @@ agent_location = Location(52.2057092, 2.1183431)
```
Third, construct a service description instance with location and data model:
+
``` python
from aea.helpers.search.models import Description
@@ -38,6 +46,7 @@ service_description = Description(
```
Finally, construct a message and send it:
+
``` python
from packages.fetchai.protocols.oef_search.message import OefSearchMessage
@@ -51,9 +60,10 @@ In case everything is registered OK, you will not receive any message back.
If something goes wrong you will receive an error message with performative `OefSearchMessage.Performative.OEF_ERROR`.
-### Register personality pieces
+### Register Personality Pieces
To register personality pieces, you have to use a specific data model:
+
``` python
from aea.helpers.search.models import Attribute, DataModel, Location
@@ -68,6 +78,7 @@ AGENT_PERSONALITY_MODEL = DataModel(
```
An example follows:
+
``` python
service_instance = {"piece": "genus", "value": "service"}
service_description = Description(
@@ -75,9 +86,10 @@ service_description = Description(
)
```
-### Register services
+### Register Services
To set some service key and value you have to use a specific data model:
+
``` python
SET_SERVICE_KEY_MODEL = DataModel(
"set_service_key",
@@ -90,6 +102,7 @@ SET_SERVICE_KEY_MODEL = DataModel(
```
An example follows:
+
``` python
service_instance = {"key": "test", "value": "test"}
service_description = Description(
@@ -97,9 +110,10 @@ service_description = Description(
)
```
-### Remove service key
+### Remove Service Key
To remove service key have to use a specific data model:
+
``` python
REMOVE_SERVICE_KEY_MODEL = DataModel(
"remove_service_key",
@@ -109,6 +123,7 @@ REMOVE_SERVICE_KEY_MODEL = DataModel(
```
An example follows:
+
``` python
service_instance = {"key": "test"}
service_description = Description(
@@ -116,16 +131,15 @@ service_description = Description(
)
```
-
-
Note
-
Currently, the soef does not allow for multiple registrations to be combined into a single command.
-
+!!! note
+ Currently, the soef does not allow for multiple registrations to be combined into a single command.
-## Perform a search
+## Perform a Search
To perform a search for services registered you have to define a search query consisting of constraints. The location constraints is required, personality pieces or services keys constraints are optional.
An example follows:
+
``` python
from aea.helpers.search.models import (
Constraint,
@@ -159,11 +173,12 @@ message = OefSearchMessage(
)
```
-In case of error you will received a message with `OefSearchMessage.Performative.OEF_ERROR`. In case of successful search you will receive a message with performative `OefSearchMessage.Performative.SEARCH_RESULT` and the list of matched agents addresses.
+In case of error, you will receive a message with `OefSearchMessage.Performative.OEF_ERROR`. In case of successful search you will receive a message with performative `OefSearchMessage.Performative.SEARCH_RESULT` and the list of matched agents addresses.
-## Generic command
+## Generic Command
To send a generic command request to the SOEF use the following (here on the example of setting a declared name):
+
``` python
import urllib
@@ -188,4 +203,4 @@ message = OefSearchMessage(
performative=OefSearchMessage.Performative.REGISTER_SERVICE,
service_description=service_description,
)
-```
\ No newline at end of file
+```
diff --git a/docs/skill-guide.md b/docs/skill-guide.md
index 5ac8235190..c7ea699bdd 100644
--- a/docs/skill-guide.md
+++ b/docs/skill-guide.md
@@ -1,3 +1,5 @@
+# Build your First Skill - Search & Discovery
+
This guide will take you through the development of your first skill. It will teach you, how to connect the AEA to the digital world, register the AEA and search for other AEAs.
Although one can imagine scenarios where a single AEA pursues its goals in isolation without interacting with other AEAs, there is no doubt that by working together, AEAs can achieve much more. To do so, an AEA must be seen and found by other AEAs so that they can trade and do other useful things. Fetch.ai’s search-and-discovery mechanism, the simple OEF (or SOEF, for short) lets your agents register, be discovered, and find other agents. You can then negotiate using the AEA framework’s peer-to-peer network (ACN) and trade. This guide covers getting your AEA connected to the SOEF, and describing your AEA to make itself visible.
@@ -10,8 +12,6 @@ The more you describe your AEA, the easier it is for others to find it using spe
Follow the Preliminaries and Installation sections from the AEA quick start.
-
-
## Step 1: Setup
We will first create an AEA and add a scaffold skill, which we call `my_search`.
@@ -118,10 +118,8 @@ Searches are proactive and, as such, well placed in a
-
-
+!!! note
+ The import paths to agent packages, for example `packages.fetchai.skills.my_search.dialogues` above, are not actual paths. Package files always reside in your AEA's folder, either under a specific package directory (e.g. connection, protocol, skill) if the package is custom-built, or under `vendor` if it is pulled from the registry. These paths are virtual and created automatically when an AEA is run. See this page for more details.
## Step 3: Develop a Handler
@@ -264,7 +262,7 @@ class MySearchHandler(Handler):
)
```
-We create a handler which is registered for the `oef_search` protocol. Whenever it receives a search result, we log the number of agents returned in the search - the agents matching the search query - and update the counter of received searches.
+We create a handler which is registered for the `oef_search` protocol. Whenever it receives a search result, we log the number of agents returned by the search - the agents matching the search query - and update the counter of received searches.
We also implement a trivial check on the difference between the amount of search requests sent and responses received.
@@ -274,7 +272,7 @@ Also note, how we have access to other objects in the skill via `self.context`,
We place this code in `my_aea/skills/my_search/handlers.py`. Ensure you replace the `fetchai` author in this line `from packages.fetchai.skills.my_search.dialogues import (` with your author handle (run `aea init` to set or check the author name).
-## Step 4: Add dialogues model
+## Step 4: Add Dialogues Model
We have implemented a behaviour and a handler. We now implement a `Model`, in particular we implement the `Dialogue` and `Dialogues` classes. These ensure that the message flow satisfies the `fetchai/oef_search:1.0.0` protocol and keep track of the individual messages being sent and received.
@@ -326,7 +324,7 @@ class OefSearchDialogues(Model, BaseOefSearchDialogues):
We add this code in the file `my_aea/skills/my_search/my_model.py`, replacing its original content. We then rename `my_aea/skills/my_search/my_model.py` to `my_aea/skills/my_search/dialogues.py`.
-## Step 5: Create the configuration file
+## Step 5: Create the Configuration File
Based on our skill components above, we create the following configuration file.
@@ -408,19 +406,23 @@ from aea.configurations.base import PublicId
PUBLIC_ID = PublicId.from_str("fetchai/my_search:0.1.0")
```
+
Again, ensure the author field matches your own.
-## Step 6: Update fingerprint
+## Step 6: Update Fingerprint
To run an AEA with new or modified code, you need to update the fingerprint of the new/modified components. In this case, we need to fingerprint our skill:
+
``` bash
aea fingerprint skill fetchai/my_search:0.1.0
```
+
Ensure, you use the correct author name to reference your skill (here we use `fetchai` as the author.)
-## Step 7: Add the OEF protocol and connection
+## Step 7: Add the OEF Protocol and Connection
Our AEA does not have the OEF protocol yet so let's add it.
+
``` bash
aea add protocol fetchai/oef_search:1.1.6
```
@@ -428,6 +430,7 @@ aea add protocol fetchai/oef_search:1.1.6
This adds the protocol to our AEA and makes it available on the path `packages.fetchai.protocols...`.
At this point we need to add the SOEF and P2P connections to allow the AEA to communicate with the SOEF node and other AEAs, install the AEA's dependencies, and configure the AEA:
+
``` bash
aea add connection fetchai/soef:0.27.5
aea add connection fetchai/p2p_libp2p:0.27.4
@@ -442,11 +445,12 @@ aea config set --type dict agent.default_routing \
The last command will ensure that search requests are processed by the correct connection.
-## Step 8: Run a service provider AEA
+## Step 8: Run a Service Provider AEA
-In order for this AEA to find another AEA when searching, the second AEA (let's call it the service provider AEA) must exist and have been registered with the SOEF.
+In order for this AEA to find another AEA when searching, the second AEA (let's call it the service provider AEA) must exist and have been registered with the SOEF.
From a different terminal window, we fetch a finished service provider AEA and install its Python dependencies:
+
``` bash
aea fetch fetchai/simple_service_registration:0.32.4 && cd simple_service_registration && aea install && aea build
```
@@ -454,642 +458,646 @@ aea fetch fetchai/simple_service_registration:0.32.4 && cd simple_service_regist
This AEA will simply register a location service on the SOEF search node so we can search for it.
We first create the private key for the service provider AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai `Dorado` use:
+
``` bash
aea generate-key fetchai
aea add-key fetchai fetchai_private_key.txt
```
Next, create a private key used to secure the AEA's communications:
+
``` bash
aea generate-key fetchai fetchai_connection_private_key.txt
aea add-key fetchai fetchai_connection_private_key.txt --connection
```
Finally, certify the key for use by the connections that request that:
+
``` bash
aea issue-certificates
```
Then we run the AEA:
+
``` bash
aea run
```
Once you see a message of the form `To join its network use multiaddr: ['SOME_ADDRESS']` take note of the address. (Alternatively, use `aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.4 -u public_uri` to retrieve the address.) This is the entry peer address for the local agent communication network created by the `simple_service_registration` (service provider) AEA.
-Click here to see full code and guide for this AEA
-
-
-We use a TickerBehaviour to update the service registration at regular intervals. The following code is placed in behaviours.py.
-
-``` python
-from typing import Any, Optional, cast
-
-from aea.helpers.search.models import Description
-from aea.skills.behaviours import TickerBehaviour
-
-from packages.fetchai.protocols.oef_search.message import OefSearchMessage
-from packages.fetchai.skills.simple_service_registration.dialogues import (
- OefSearchDialogues,
-)
-from packages.fetchai.skills.simple_service_registration.strategy import Strategy
-
-
-DEFAULT_MAX_SOEF_REGISTRATION_RETRIES = 5
-DEFAULT_SERVICES_INTERVAL = 30.0
-
-
-class ServiceRegistrationBehaviour(TickerBehaviour):
- """This class implements a behaviour."""
-
- def __init__(self, **kwargs: Any) -> None:
- """Initialise the behaviour."""
- services_interval = kwargs.pop(
- "services_interval", DEFAULT_SERVICES_INTERVAL
- ) # type: int
- self._max_soef_registration_retries = kwargs.pop(
- "max_soef_registration_retries", DEFAULT_MAX_SOEF_REGISTRATION_RETRIES
- ) # type: int
- super().__init__(tick_interval=services_interval, **kwargs)
-
- self.failed_registration_msg = None # type: Optional[OefSearchMessage]
- self._nb_retries = 0
-
- def setup(self) -> None:
- """
- Implement the setup.
-
- :return: None
- """
- self._register_agent()
-
- def act(self) -> None:
- """
- Implement the act.
-
- :return: None
- """
- self._retry_failed_registration()
-
- def teardown(self) -> None:
- """
- Implement the task teardown.
-
- :return: None
- """
- self._unregister_service()
- self._unregister_agent()
-
- def _retry_failed_registration(self) -> None:
- """
- Retry a failed registration.
-
- :return: None
- """
- if self.failed_registration_msg is not None:
- self._nb_retries += 1
- if self._nb_retries > self._max_soef_registration_retries:
- self.context.is_active = False
- return
-
+??? note "Click here to see full code and guide for this AEA:"
+ We use a `TickerBehaviour` to update the service registration at regular intervals. The following code is placed in `behaviours.py`.
+
+ ``` python
+ from typing import Any, Optional, cast
+
+ from aea.helpers.search.models import Description
+ from aea.skills.behaviours import TickerBehaviour
+
+ from packages.fetchai.protocols.oef_search.message import OefSearchMessage
+ from packages.fetchai.skills.simple_service_registration.dialogues import (
+ OefSearchDialogues,
+ )
+ from packages.fetchai.skills.simple_service_registration.strategy import Strategy
+
+
+ DEFAULT_MAX_SOEF_REGISTRATION_RETRIES = 5
+ DEFAULT_SERVICES_INTERVAL = 30.0
+
+
+ class ServiceRegistrationBehaviour(TickerBehaviour):
+ """This class implements a behaviour."""
+
+ def __init__(self, **kwargs: Any) -> None:
+ """Initialise the behaviour."""
+ services_interval = kwargs.pop(
+ "services_interval", DEFAULT_SERVICES_INTERVAL
+ ) # type: int
+ self._max_soef_registration_retries = kwargs.pop(
+ "max_soef_registration_retries", DEFAULT_MAX_SOEF_REGISTRATION_RETRIES
+ ) # type: int
+ super().__init__(tick_interval=services_interval, **kwargs)
+
+ self.failed_registration_msg = None # type: Optional[OefSearchMessage]
+ self._nb_retries = 0
+
+ def setup(self) -> None:
+ """
+ Implement the setup.
+
+ :return: None
+ """
+ self._register_agent()
+
+ def act(self) -> None:
+ """
+ Implement the act.
+
+ :return: None
+ """
+ self._retry_failed_registration()
+
+ def teardown(self) -> None:
+ """
+ Implement the task teardown.
+
+ :return: None
+ """
+ self._unregister_service()
+ self._unregister_agent()
+
+ def _retry_failed_registration(self) -> None:
+ """
+ Retry a failed registration.
+
+ :return: None
+ """
+ if self.failed_registration_msg is not None:
+ self._nb_retries += 1
+ if self._nb_retries > self._max_soef_registration_retries:
+ self.context.is_active = False
+ return
+
+ oef_search_dialogues = cast(
+ OefSearchDialogues, self.context.oef_search_dialogues
+ )
+ oef_search_msg, _ = oef_search_dialogues.create(
+ counterparty=self.failed_registration_msg.to,
+ performative=self.failed_registration_msg.performative,
+ service_description=self.failed_registration_msg.service_description,
+ )
+ self.context.outbox.put_message(message=oef_search_msg)
+ self.context.logger.info(
+ f"Retrying registration on SOEF. Retry {self._nb_retries} out of {self._max_soef_registration_retries}."
+ )
+
+ self.failed_registration_msg = None
+
+ def _register(self, description: Description, logger_msg: str) -> None:
+ """
+ Register something on the SOEF.
+
+ :param description: the description of what is being registered
+ :param logger_msg: the logger message to print after the registration
+
+ :return: None
+ """
oef_search_dialogues = cast(
OefSearchDialogues, self.context.oef_search_dialogues
)
oef_search_msg, _ = oef_search_dialogues.create(
- counterparty=self.failed_registration_msg.to,
- performative=self.failed_registration_msg.performative,
- service_description=self.failed_registration_msg.service_description,
+ counterparty=self.context.search_service_address,
+ performative=OefSearchMessage.Performative.REGISTER_SERVICE,
+ service_description=description,
)
self.context.outbox.put_message(message=oef_search_msg)
- self.context.logger.info(
- f"Retrying registration on SOEF. Retry {self._nb_retries} out of {self._max_soef_registration_retries}."
+ self.context.logger.info(logger_msg)
+
+ def _register_agent(self) -> None:
+ """
+ Register the agent's location.
+
+ :return: None
+ """
+ strategy = cast(Strategy, self.context.strategy)
+ description = strategy.get_location_description()
+ self._register(description, "registering agent on SOEF.")
+
+ def register_service(self) -> None:
+ """
+ Register the agent's service.
+
+ :return: None
+ """
+ strategy = cast(Strategy, self.context.strategy)
+ description = strategy.get_register_service_description()
+ self._register(description, "registering agent's service on the SOEF.")
+
+ def register_genus(self) -> None:
+ """
+ Register the agent's personality genus.
+
+ :return: None
+ """
+ strategy = cast(Strategy, self.context.strategy)
+ description = strategy.get_register_personality_description()
+ self._register(
+ description, "registering agent's personality genus on the SOEF."
)
-
- self.failed_registration_msg = None
-
- def _register(self, description: Description, logger_msg: str) -> None:
- """
- Register something on the SOEF.
-
- :param description: the description of what is being registered
- :param logger_msg: the logger message to print after the registration
-
- :return: None
- """
- oef_search_dialogues = cast(
- OefSearchDialogues, self.context.oef_search_dialogues
- )
- oef_search_msg, _ = oef_search_dialogues.create(
- counterparty=self.context.search_service_address,
- performative=OefSearchMessage.Performative.REGISTER_SERVICE,
- service_description=description,
- )
- self.context.outbox.put_message(message=oef_search_msg)
- self.context.logger.info(logger_msg)
-
- def _register_agent(self) -> None:
- """
- Register the agent's location.
-
- :return: None
- """
- strategy = cast(Strategy, self.context.strategy)
- description = strategy.get_location_description()
- self._register(description, "registering agent on SOEF.")
-
- def register_service(self) -> None:
- """
- Register the agent's service.
-
- :return: None
- """
- strategy = cast(Strategy, self.context.strategy)
- description = strategy.get_register_service_description()
- self._register(description, "registering agent's service on the SOEF.")
-
- def register_genus(self) -> None:
- """
- Register the agent's personality genus.
-
- :return: None
- """
- strategy = cast(Strategy, self.context.strategy)
- description = strategy.get_register_personality_description()
- self._register(
- description, "registering agent's personality genus on the SOEF."
- )
-
- def register_classification(self) -> None:
- """
- Register the agent's personality classification.
-
- :return: None
- """
- strategy = cast(Strategy, self.context.strategy)
- description = strategy.get_register_classification_description()
- self._register(
- description, "registering agent's personality classification on the SOEF."
- )
-
- def _unregister_service(self) -> None:
- """
- Unregister service from the SOEF.
-
- :return: None
- """
- strategy = cast(Strategy, self.context.strategy)
- description = strategy.get_unregister_service_description()
- oef_search_dialogues = cast(
- OefSearchDialogues, self.context.oef_search_dialogues
- )
- oef_search_msg, _ = oef_search_dialogues.create(
- counterparty=self.context.search_service_address,
- performative=OefSearchMessage.Performative.UNREGISTER_SERVICE,
- service_description=description,
- )
- self.context.outbox.put_message(message=oef_search_msg)
- self.context.logger.info("unregistering service from SOEF.")
-
- def _unregister_agent(self) -> None:
- """
- Unregister agent from the SOEF.
-
- :return: None
- """
- strategy = cast(Strategy, self.context.strategy)
- description = strategy.get_location_description()
- oef_search_dialogues = cast(
- OefSearchDialogues, self.context.oef_search_dialogues
- )
- oef_search_msg, _ = oef_search_dialogues.create(
- counterparty=self.context.search_service_address,
- performative=OefSearchMessage.Performative.UNREGISTER_SERVICE,
- service_description=description,
- )
- self.context.outbox.put_message(message=oef_search_msg)
- self.context.logger.info("unregistering agent from SOEF.")
-```
-
-We create a Model type strategy class and place it in strategy.py. We use a generic data model to register the service. As part of the registration we register a location and a key pair describing our service.
-
-``` python
-from typing import Any
-
-from aea.exceptions import enforce
-from aea.helpers.search.generic import (
- AGENT_LOCATION_MODEL,
- AGENT_PERSONALITY_MODEL,
- AGENT_REMOVE_SERVICE_MODEL,
- AGENT_SET_SERVICE_MODEL,
-)
-from aea.helpers.search.models import Description, Location
-from aea.skills.base import Model
-
-
-DEFAULT_LOCATION = {"longitude": 0.1270, "latitude": 51.5194}
-DEFAULT_SERVICE_DATA = {"key": "seller_service", "value": "generic_service"}
-DEFAULT_PERSONALITY_DATA = {"piece": "genus", "value": "data"}
-DEFAULT_CLASSIFICATION = {"piece": "classification", "value": "seller"}
-
-
-class Strategy(Model):
- """This class defines a strategy for the agent."""
-
- def __init__(self, **kwargs: Any) -> None:
- """
- Initialize the strategy of the agent.
-
- :return: None
- """
- location = kwargs.pop("location", DEFAULT_LOCATION)
- self._agent_location = {
- "location": Location(
- latitude=location["latitude"], longitude=location["longitude"]
+
+ def register_classification(self) -> None:
+ """
+ Register the agent's personality classification.
+
+ :return: None
+ """
+ strategy = cast(Strategy, self.context.strategy)
+ description = strategy.get_register_classification_description()
+ self._register(
+ description, "registering agent's personality classification on the SOEF."
)
- }
- self._set_personality_data = kwargs.pop(
- "personality_data", DEFAULT_PERSONALITY_DATA
- )
- enforce(
- len(self._set_personality_data) == 2
- and "piece" in self._set_personality_data
- and "value" in self._set_personality_data,
- "personality_data must contain keys `key` and `value`",
- )
- self._set_classification = kwargs.pop("classification", DEFAULT_CLASSIFICATION)
- enforce(
- len(self._set_classification) == 2
- and "piece" in self._set_classification
- and "value" in self._set_classification,
- "classification must contain keys `key` and `value`",
- )
- self._set_service_data = kwargs.pop("service_data", DEFAULT_SERVICE_DATA)
- enforce(
- len(self._set_service_data) == 2
- and "key" in self._set_service_data
- and "value" in self._set_service_data,
- "service_data must contain keys `key` and `value`",
- )
- self._remove_service_data = {"key": self._set_service_data["key"]}
- super().__init__(**kwargs)
-
- def get_location_description(self) -> Description:
- """
- Get the location description.
-
- :return: a description of the agent's location
- """
- description = Description(
- self._agent_location, data_model=AGENT_LOCATION_MODEL,
- )
- return description
-
- def get_register_service_description(self) -> Description:
- """
- Get the register service description.
-
- :return: a description of the offered services
- """
- description = Description(
- self._set_service_data, data_model=AGENT_SET_SERVICE_MODEL,
- )
- return description
-
- def get_register_personality_description(self) -> Description:
- """
- Get the register personality description.
-
- :return: a description of the personality
- """
- description = Description(
- self._set_personality_data, data_model=AGENT_PERSONALITY_MODEL,
- )
- return description
-
- def get_register_classification_description(self) -> Description:
- """
- Get the register classification description.
-
- :return: a description of the classification
- """
- description = Description(
- self._set_classification, data_model=AGENT_PERSONALITY_MODEL,
- )
- return description
-
- def get_unregister_service_description(self) -> Description:
- """
- Get the unregister service description.
-
- :return: a description of the to be removed service
- """
- description = Description(
- self._remove_service_data, data_model=AGENT_REMOVE_SERVICE_MODEL,
- )
- return description
-```
-
-We create a Model type dialogue class and place it in dialogues.py. These classes ensure that the message flow satisfies the fetchai/oef_search:1.0.0 protocol and keep track of the individual messages being sent and received.
-
-``` python
-from typing import Any
-
-from aea.protocols.base import Address, Message
-from aea.protocols.dialogue.base import Dialogue as BaseDialogue
-from aea.skills.base import Model
-
-from packages.fetchai.protocols.oef_search.dialogues import (
- OefSearchDialogue as BaseOefSearchDialogue,
-)
-from packages.fetchai.protocols.oef_search.dialogues import (
- OefSearchDialogues as BaseOefSearchDialogues,
-)
-
-
-OefSearchDialogue = BaseOefSearchDialogue
-
-
-class OefSearchDialogues(Model, BaseOefSearchDialogues):
- """This class keeps track of all oef_search dialogues."""
-
- def __init__(self, **kwargs: Any) -> None:
- """
- Initialize dialogues.
-
- :param agent_address: the address of the agent for whom dialogues are maintained
- :return: None
- """
- Model.__init__(self, **kwargs)
-
- def role_from_first_message( # pylint: disable=unused-argument
- message: Message, receiver_address: Address
- ) -> BaseDialogue.Role:
- """Infer the role of the agent from an incoming/outgoing first message
-
- :param message: an incoming/outgoing first message
- :param receiver_address: the address of the receiving agent
- :return: The role of the agent
+
+ def _unregister_service(self) -> None:
"""
- return BaseOefSearchDialogue.Role.AGENT
-
- BaseOefSearchDialogues.__init__(
- self,
- self_address=str(self.skill_id),
- role_from_first_message=role_from_first_message,
- )
-
-```
-
-Finally, we have a handler, placed in handlers.py. The handler deals with handling any error messages which might occur during service registration:
-
-``` python
-from typing import Optional, cast
-
-from aea.configurations.base import PublicId
-from aea.protocols.base import Message
-from aea.skills.base import Handler
-
-from packages.fetchai.protocols.oef_search.message import OefSearchMessage
-from packages.fetchai.skills.simple_service_registration.behaviours import (
- ServiceRegistrationBehaviour,
-)
-from packages.fetchai.skills.simple_service_registration.dialogues import (
- OefSearchDialogue,
- OefSearchDialogues,
-)
-
-
-class OefSearchHandler(Handler):
- """This class implements an OEF search handler."""
-
- SUPPORTED_PROTOCOL = OefSearchMessage.protocol_id # type: Optional[PublicId]
-
- def setup(self) -> None:
- """Call to setup the handler."""
-
- def handle(self, message: Message) -> None:
- """
- Implement the reaction to a message.
-
- :param message: the message
- :return: None
- """
- oef_search_msg = cast(OefSearchMessage, message)
-
- # recover dialogue
- oef_search_dialogues = cast(
- OefSearchDialogues, self.context.oef_search_dialogues
- )
- oef_search_dialogue = cast(
- Optional[OefSearchDialogue], oef_search_dialogues.update(oef_search_msg)
- )
- if oef_search_dialogue is None:
- self._handle_unidentified_dialogue(oef_search_msg)
- return
-
- # handle message
- if oef_search_msg.performative == OefSearchMessage.Performative.SUCCESS:
- self._handle_success(oef_search_msg, oef_search_dialogue)
- elif oef_search_msg.performative == OefSearchMessage.Performative.OEF_ERROR:
- self._handle_error(oef_search_msg, oef_search_dialogue)
- else:
- self._handle_invalid(oef_search_msg, oef_search_dialogue)
-
- def teardown(self) -> None:
- """
- Implement the handler teardown.
-
- :return: None
- """
-
- def _handle_unidentified_dialogue(self, oef_search_msg: OefSearchMessage) -> None:
- """
- Handle an unidentified dialogue.
-
- :param msg: the message
- """
- self.context.logger.info(
- "received invalid oef_search message={}, unidentified dialogue.".format(
- oef_search_msg
+ Unregister service from the SOEF.
+
+ :return: None
+ """
+ strategy = cast(Strategy, self.context.strategy)
+ description = strategy.get_unregister_service_description()
+ oef_search_dialogues = cast(
+ OefSearchDialogues, self.context.oef_search_dialogues
)
- )
-
- def _handle_success(
- self,
- oef_search_success_msg: OefSearchMessage,
- oef_search_dialogue: OefSearchDialogue,
- ) -> None:
- """
- Handle an oef search message.
-
- :param oef_search_success_msg: the oef search message
- :param oef_search_dialogue: the dialogue
- :return: None
- """
- self.context.logger.info(
- "received oef_search success message={} in dialogue={}.".format(
- oef_search_success_msg, oef_search_dialogue
+ oef_search_msg, _ = oef_search_dialogues.create(
+ counterparty=self.context.search_service_address,
+ performative=OefSearchMessage.Performative.UNREGISTER_SERVICE,
+ service_description=description,
)
- )
- target_message = cast(
- OefSearchMessage,
- oef_search_dialogue.get_message_by_id(oef_search_success_msg.target),
- )
- if (
- target_message.performative
- == OefSearchMessage.Performative.REGISTER_SERVICE
- ):
- description = target_message.service_description
- data_model_name = description.data_model.name
- registration_behaviour = cast(
- ServiceRegistrationBehaviour, self.context.behaviours.service,
+ self.context.outbox.put_message(message=oef_search_msg)
+ self.context.logger.info("unregistering service from SOEF.")
+
+ def _unregister_agent(self) -> None:
+ """
+ Unregister agent from the SOEF.
+
+ :return: None
+ """
+ strategy = cast(Strategy, self.context.strategy)
+ description = strategy.get_location_description()
+ oef_search_dialogues = cast(
+ OefSearchDialogues, self.context.oef_search_dialogues
)
- if "location_agent" in data_model_name:
- registration_behaviour.register_service()
- elif "set_service_key" in data_model_name:
- registration_behaviour.register_genus()
- elif (
- "personality_agent" in data_model_name
- and description.values["piece"] == "genus"
- ):
- registration_behaviour.register_classification()
- elif (
- "personality_agent" in data_model_name
- and description.values["piece"] == "classification"
- ):
- self.context.logger.info(
- "the agent, with its genus and classification, and its service are successfully registered on the SOEF."
+ oef_search_msg, _ = oef_search_dialogues.create(
+ counterparty=self.context.search_service_address,
+ performative=OefSearchMessage.Performative.UNREGISTER_SERVICE,
+ service_description=description,
+ )
+ self.context.outbox.put_message(message=oef_search_msg)
+ self.context.logger.info("unregistering agent from SOEF.")
+ ```
+
+ We create a Model type strategy class and place it in strategy.py. We use a generic data model to register the service. As part of the registration we register a location and a key pair describing our service.
+
+ ``` python
+ from typing import Any
+
+ from aea.exceptions import enforce
+ from aea.helpers.search.generic import (
+ AGENT_LOCATION_MODEL,
+ AGENT_PERSONALITY_MODEL,
+ AGENT_REMOVE_SERVICE_MODEL,
+ AGENT_SET_SERVICE_MODEL,
+ )
+ from aea.helpers.search.models import Description, Location
+ from aea.skills.base import Model
+
+
+ DEFAULT_LOCATION = {"longitude": 0.1270, "latitude": 51.5194}
+ DEFAULT_SERVICE_DATA = {"key": "seller_service", "value": "generic_service"}
+ DEFAULT_PERSONALITY_DATA = {"piece": "genus", "value": "data"}
+ DEFAULT_CLASSIFICATION = {"piece": "classification", "value": "seller"}
+
+
+ class Strategy(Model):
+ """This class defines a strategy for the agent."""
+
+ def __init__(self, **kwargs: Any) -> None:
+ """
+ Initialize the strategy of the agent.
+
+ :return: None
+ """
+ location = kwargs.pop("location", DEFAULT_LOCATION)
+ self._agent_location = {
+ "location": Location(
+ latitude=location["latitude"], longitude=location["longitude"]
)
+ }
+ self._set_personality_data = kwargs.pop(
+ "personality_data", DEFAULT_PERSONALITY_DATA
+ )
+ enforce(
+ len(self._set_personality_data) == 2
+ and "piece" in self._set_personality_data
+ and "value" in self._set_personality_data,
+ "personality_data must contain keys `key` and `value`",
+ )
+ self._set_classification = kwargs.pop("classification", DEFAULT_CLASSIFICATION)
+ enforce(
+ len(self._set_classification) == 2
+ and "piece" in self._set_classification
+ and "value" in self._set_classification,
+ "classification must contain keys `key` and `value`",
+ )
+ self._set_service_data = kwargs.pop("service_data", DEFAULT_SERVICE_DATA)
+ enforce(
+ len(self._set_service_data) == 2
+ and "key" in self._set_service_data
+ and "value" in self._set_service_data,
+ "service_data must contain keys `key` and `value`",
+ )
+ self._remove_service_data = {"key": self._set_service_data["key"]}
+ super().__init__(**kwargs)
+
+ def get_location_description(self) -> Description:
+ """
+ Get the location description.
+
+ :return: a description of the agent's location
+ """
+ description = Description(
+ self._agent_location, data_model=AGENT_LOCATION_MODEL,
+ )
+ return description
+
+ def get_register_service_description(self) -> Description:
+ """
+ Get the register service description.
+
+ :return: a description of the offered services
+ """
+ description = Description(
+ self._set_service_data, data_model=AGENT_SET_SERVICE_MODEL,
+ )
+ return description
+
+ def get_register_personality_description(self) -> Description:
+ """
+ Get the register personality description.
+
+ :return: a description of the personality
+ """
+ description = Description(
+ self._set_personality_data, data_model=AGENT_PERSONALITY_MODEL,
+ )
+ return description
+
+ def get_register_classification_description(self) -> Description:
+ """
+ Get the register classification description.
+
+ :return: a description of the classification
+ """
+ description = Description(
+ self._set_classification, data_model=AGENT_PERSONALITY_MODEL,
+ )
+ return description
+
+ def get_unregister_service_description(self) -> Description:
+ """
+ Get the unregister service description.
+
+ :return: a description of the to be removed service
+ """
+ description = Description(
+ self._remove_service_data, data_model=AGENT_REMOVE_SERVICE_MODEL,
+ )
+ return description
+ ```
+
+ We create a Model type dialogue class and place it in dialogues.py. These classes ensure that the message flow satisfies the fetchai/oef_search:1.0.0 protocol and keep track of the individual messages being sent and received.
+
+ ``` python
+ from typing import Any
+
+ from aea.protocols.base import Address, Message
+ from aea.protocols.dialogue.base import Dialogue as BaseDialogue
+ from aea.skills.base import Model
+
+ from packages.fetchai.protocols.oef_search.dialogues import (
+ OefSearchDialogue as BaseOefSearchDialogue,
+ )
+ from packages.fetchai.protocols.oef_search.dialogues import (
+ OefSearchDialogues as BaseOefSearchDialogues,
+ )
+
+
+ OefSearchDialogue = BaseOefSearchDialogue
+
+
+ class OefSearchDialogues(Model, BaseOefSearchDialogues):
+ """This class keeps track of all oef_search dialogues."""
+
+ def __init__(self, **kwargs: Any) -> None:
+ """
+ Initialize dialogues.
+
+ :param agent_address: the address of the agent for whom dialogues are maintained
+ :return: None
+ """
+ Model.__init__(self, **kwargs)
+
+ def role_from_first_message( # pylint: disable=unused-argument
+ message: Message, receiver_address: Address
+ ) -> BaseDialogue.Role:
+ """Infer the role of the agent from an incoming/outgoing first message
+
+ :param message: an incoming/outgoing first message
+ :param receiver_address: the address of the receiving agent
+ :return: The role of the agent
+ """
+ return BaseOefSearchDialogue.Role.AGENT
+
+ BaseOefSearchDialogues.__init__(
+ self,
+ self_address=str(self.skill_id),
+ role_from_first_message=role_from_first_message,
+ )
+
+ ```
+
+ Finally, we have a handler, placed in handlers.py. The handler deals with handling any error messages which might occur during service registration:
+
+ ``` python
+ from typing import Optional, cast
+
+ from aea.configurations.base import PublicId
+ from aea.protocols.base import Message
+ from aea.skills.base import Handler
+
+ from packages.fetchai.protocols.oef_search.message import OefSearchMessage
+ from packages.fetchai.skills.simple_service_registration.behaviours import (
+ ServiceRegistrationBehaviour,
+ )
+ from packages.fetchai.skills.simple_service_registration.dialogues import (
+ OefSearchDialogue,
+ OefSearchDialogues,
+ )
+
+
+ class OefSearchHandler(Handler):
+ """This class implements an OEF search handler."""
+
+ SUPPORTED_PROTOCOL = OefSearchMessage.protocol_id # type: Optional[PublicId]
+
+ def setup(self) -> None:
+ """Call to setup the handler."""
+
+ def handle(self, message: Message) -> None:
+ """
+ Implement the reaction to a message.
+
+ :param message: the message
+ :return: None
+ """
+ oef_search_msg = cast(OefSearchMessage, message)
+
+ # recover dialogue
+ oef_search_dialogues = cast(
+ OefSearchDialogues, self.context.oef_search_dialogues
+ )
+ oef_search_dialogue = cast(
+ Optional[OefSearchDialogue], oef_search_dialogues.update(oef_search_msg)
+ )
+ if oef_search_dialogue is None:
+ self._handle_unidentified_dialogue(oef_search_msg)
+ return
+
+ # handle message
+ if oef_search_msg.performative == OefSearchMessage.Performative.SUCCESS:
+ self._handle_success(oef_search_msg, oef_search_dialogue)
+ elif oef_search_msg.performative == OefSearchMessage.Performative.OEF_ERROR:
+ self._handle_error(oef_search_msg, oef_search_dialogue)
else:
- self.context.logger.warning(
- f"received soef SUCCESS message as a reply to the following unexpected message: {target_message}"
+ self._handle_invalid(oef_search_msg, oef_search_dialogue)
+
+ def teardown(self) -> None:
+ """
+ Implement the handler teardown.
+
+ :return: None
+ """
+
+ def _handle_unidentified_dialogue(self, oef_search_msg: OefSearchMessage) -> None:
+ """
+ Handle an unidentified dialogue.
+
+ :param msg: the message
+ """
+ self.context.logger.info(
+ "received invalid oef_search message={}, unidentified dialogue.".format(
+ oef_search_msg
)
-
- def _handle_error(
- self,
- oef_search_error_msg: OefSearchMessage,
- oef_search_dialogue: OefSearchDialogue,
- ) -> None:
- """
- Handle an oef search message.
-
- :param oef_search_error_msg: the oef search message
- :param oef_search_dialogue: the dialogue
- :return: None
- """
- self.context.logger.info(
- "received oef_search error message={} in dialogue={}.".format(
- oef_search_error_msg, oef_search_dialogue
)
- )
- target_message = cast(
- OefSearchMessage,
- oef_search_dialogue.get_message_by_id(oef_search_error_msg.target),
- )
- if (
- target_message.performative
- == OefSearchMessage.Performative.REGISTER_SERVICE
- ):
- registration_behaviour = cast(
- ServiceRegistrationBehaviour, self.context.behaviours.service,
+
+ def _handle_success(
+ self,
+ oef_search_success_msg: OefSearchMessage,
+ oef_search_dialogue: OefSearchDialogue,
+ ) -> None:
+ """
+ Handle an oef search message.
+
+ :param oef_search_success_msg: the oef search message
+ :param oef_search_dialogue: the dialogue
+ :return: None
+ """
+ self.context.logger.info(
+ "received oef_search success message={} in dialogue={}.".format(
+ oef_search_success_msg, oef_search_dialogue
+ )
)
- registration_behaviour.failed_registration_msg = target_message
-
- def _handle_invalid(
- self, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue
- ) -> None:
- """
- Handle an oef search message.
-
- :param oef_search_msg: the oef search message
- :param oef_search_dialogue: the dialogue
- :return: None
- """
- self.context.logger.warning(
- "cannot handle oef_search message of performative={} in dialogue={}.".format(
- oef_search_msg.performative, oef_search_dialogue,
+ target_message = cast(
+ OefSearchMessage,
+ oef_search_dialogue.get_message_by_id(oef_search_success_msg.target),
)
- )
-```
-
-The associated skill.yaml is:
-
-``` yaml
-name: simple_service_registration
-author: fetchai
-version: 0.20.0
-type: skill
-description: The simple service registration skills is a skill to register a service.
-license: Apache-2.0
-aea_version: '>=1.0.0, <2.0.0'
-fingerprint:
- README.md: QmUgCcR7sDBQeeCBRKwDT7tPBTi3t4zSibyEqR3xdQUKmh
- __init__.py: QmZd48HmYDr7FMxNaVeGfWRvVtieEdEV78hd7h7roTceP2
- behaviours.py: QmQHf6QL5aBtLJ34D2tdcbjJLbzom9gaA3HWgRn3rWyigM
- dialogues.py: QmTT9dvFhWt6qvxjwBfMFDTrgEtgWbvgANYafyRg2BXwcR
- handlers.py: QmZqPt8toGbJgTT6NZBLxjkusrQCZ8GmUEwcmqZ1sd7DpG
- strategy.py: QmVXfQpk4cjDw576H2ELE12tEiN5brPkwvffvcTeMbsugA
-fingerprint_ignore_patterns: []
-connections: []
-contracts: []
-protocols:
-- fetchai/oef_search:1.1.6
-skills: []
-behaviours:
- service:
- args:
- max_soef_registration_retries: 5
- services_interval: 30
- class_name: ServiceRegistrationBehaviour
-handlers:
- oef_search:
- args: {}
- class_name: OefSearchHandler
-models:
- oef_search_dialogues:
- args: {}
- class_name: OefSearchDialogues
- strategy:
- args:
- classification:
- piece: classification
- value: seller
- location:
- latitude: 51.5194
- longitude: 0.127
- personality_data:
- piece: genus
- value: data
- service_data:
- key: seller_service
- value: generic_service
- class_name: Strategy
-dependencies: {}
-is_abstract: false
-```
-
-
+ if (
+ target_message.performative
+ == OefSearchMessage.Performative.REGISTER_SERVICE
+ ):
+ description = target_message.service_description
+ data_model_name = description.data_model.name
+ registration_behaviour = cast(
+ ServiceRegistrationBehaviour, self.context.behaviours.service,
+ )
+ if "location_agent" in data_model_name:
+ registration_behaviour.register_service()
+ elif "set_service_key" in data_model_name:
+ registration_behaviour.register_genus()
+ elif (
+ "personality_agent" in data_model_name
+ and description.values["piece"] == "genus"
+ ):
+ registration_behaviour.register_classification()
+ elif (
+ "personality_agent" in data_model_name
+ and description.values["piece"] == "classification"
+ ):
+ self.context.logger.info(
+ "the agent, with its genus and classification, and its service are successfully registered on the SOEF."
+ )
+ else:
+ self.context.logger.warning(
+ f"received soef SUCCESS message as a reply to the following unexpected message: {target_message}"
+ )
+
+ def _handle_error(
+ self,
+ oef_search_error_msg: OefSearchMessage,
+ oef_search_dialogue: OefSearchDialogue,
+ ) -> None:
+ """
+ Handle an oef search message.
+
+ :param oef_search_error_msg: the oef search message
+ :param oef_search_dialogue: the dialogue
+ :return: None
+ """
+ self.context.logger.info(
+ "received oef_search error message={} in dialogue={}.".format(
+ oef_search_error_msg, oef_search_dialogue
+ )
+ )
+ target_message = cast(
+ OefSearchMessage,
+ oef_search_dialogue.get_message_by_id(oef_search_error_msg.target),
+ )
+ if (
+ target_message.performative
+ == OefSearchMessage.Performative.REGISTER_SERVICE
+ ):
+ registration_behaviour = cast(
+ ServiceRegistrationBehaviour, self.context.behaviours.service,
+ )
+ registration_behaviour.failed_registration_msg = target_message
+
+ def _handle_invalid(
+ self, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue
+ ) -> None:
+ """
+ Handle an oef search message.
+
+ :param oef_search_msg: the oef search message
+ :param oef_search_dialogue: the dialogue
+ :return: None
+ """
+ self.context.logger.warning(
+ "cannot handle oef_search message of performative={} in dialogue={}.".format(
+ oef_search_msg.performative, oef_search_dialogue,
+ )
+ )
+ ```
+
+ The associated skill.yaml is:
+
+ ``` yaml
+ name: simple_service_registration
+ author: fetchai
+ version: 0.20.0
+ type: skill
+ description: The simple service registration skills is a skill to register a service.
+ license: Apache-2.0
+ aea_version: '>=1.0.0, <2.0.0'
+ fingerprint:
+ README.md: QmUgCcR7sDBQeeCBRKwDT7tPBTi3t4zSibyEqR3xdQUKmh
+ __init__.py: QmZd48HmYDr7FMxNaVeGfWRvVtieEdEV78hd7h7roTceP2
+ behaviours.py: QmQHf6QL5aBtLJ34D2tdcbjJLbzom9gaA3HWgRn3rWyigM
+ dialogues.py: QmTT9dvFhWt6qvxjwBfMFDTrgEtgWbvgANYafyRg2BXwcR
+ handlers.py: QmZqPt8toGbJgTT6NZBLxjkusrQCZ8GmUEwcmqZ1sd7DpG
+ strategy.py: QmVXfQpk4cjDw576H2ELE12tEiN5brPkwvffvcTeMbsugA
+ fingerprint_ignore_patterns: []
+ connections: []
+ contracts: []
+ protocols:
+ - fetchai/oef_search:1.1.6
+ skills: []
+ behaviours:
+ service:
+ args:
+ max_soef_registration_retries: 5
+ services_interval: 30
+ class_name: ServiceRegistrationBehaviour
+ handlers:
+ oef_search:
+ args: {}
+ class_name: OefSearchHandler
+ models:
+ oef_search_dialogues:
+ args: {}
+ class_name: OefSearchDialogues
+ strategy:
+ args:
+ classification:
+ piece: classification
+ value: seller
+ location:
+ latitude: 51.5194
+ longitude: 0.127
+ personality_data:
+ piece: genus
+ value: data
+ service_data:
+ key: seller_service
+ value: generic_service
+ class_name: Strategy
+ dependencies: {}
+ is_abstract: false
+ ```
## Step 9: Run the Search AEA
First, create the private key for the search AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai `Dorado` use:
+
``` bash
aea generate-key fetchai
aea add-key fetchai fetchai_private_key.txt
```
Next, create a private key used to secure the AEA's communications:
+
``` bash
aea generate-key fetchai fetchai_connection_private_key.txt
aea add-key fetchai fetchai_connection_private_key.txt --connection
```
Finally, certify the key for use by the connections that request that:
+
``` bash
aea issue-certificates
```
Then, in the search AEA, run this command (replace `SOME_ADDRESS` with the correct value as described above):
+
``` bash
aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
'{
@@ -1100,6 +1108,7 @@ aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
"public_uri": "127.0.0.1:9001"
}'
```
+
This allows the search AEA to connect to the same local agent communication network as the service registration AEA.
We can then launch our AEA.
@@ -1112,8 +1121,7 @@ We can see that the AEA sends search requests to the SOE
We stop the AEA with `CTRL + C`.
-## Next steps
-
+## Next Steps
### Recommended
@@ -1121,8 +1129,6 @@ We recommend you continue with the next step in the 'Getting Started' series:
- Core components (Part 2)
-### Relevant deep-dives
-
- This guide goes through a more elaborate scenario than the one on this page, where after finding each other, the two AEAs negotiate and trade via a ledger.
+### Relevant Deep-Dives
-
\ No newline at end of file
+ This guide goes through a more elaborate scenario than the one on this page, where after finding each other, the two AEAs negotiate and trade via a ledger.
diff --git a/docs/skill-testing.md b/docs/skill-testing.md
index 55bd92e7cc..535449770a 100644
--- a/docs/skill-testing.md
+++ b/docs/skill-testing.md
@@ -1,10 +1,12 @@
-In this guide we describe some of the tools the framework offers for testing skills.
+# Testing Skills
-## The `BaseSkillTestCase` class
+In this guide, we describe some of the tools the framework offers for testing skills.
-The framework offers a `BaseSkillTestCase` class which you can subclass and write your test cases with.
+## The `BaseSkillTestCase` Class
-Let us assume you want to test the `my_behaviour` behaviour of a `CustomSkill` skill you have developed.
+The framework offers a `BaseSkillTestCase` class which you can subclass and write your test cases with.
+
+Let us assume you want to test the `my_behaviour` behaviour of a `CustomSkill` skill you have developed.
You can create a `TestMyBehaviour` class which inherits `BaseSkillTestCase` as below:
@@ -52,7 +54,7 @@ In the above, we make the `my_behaviour` behaviour object accessible for every t
### Skill and Skill Context
-The skill object itself is exposed via a property. So you can access the skill object by `self.skill` and by extension all of its attributes. This crucially includes the complete `skill_context`. This means that for example, all of the components of the skill (e.g. behaviours, handlers, models) can be accessed via the skill context.
+The skill object itself is exposed via a property. So you can access the skill object by `self.skill` and by extension all of its attributes. This crucially includes the complete `skill_context`. This means that for example, every component of the skill (e.g. behaviours, handlers, models) can be accessed via the skill context.
In the above code snippet, `my_behavior` is accessed and exposed as a class attribute. Note accessing the skill context is slightly different in the above because it is a class method. If this was a test method, you could access the behaviour via `self.skill.skill_context.behaviours.my_behaviour`.
@@ -85,19 +87,19 @@ agent_context = AgentContext(
Some of the useful objects you can access in your test class for the loaded skill are below:
-* `self.skill.skill_context.agent_address`: this is the agent identity the skill uses and is set to `"test_agent_address"`.
-* `self.skill.skill_context.search_service_address`: this is the address of the search service and is set to `"dummy_search_service_address"`.
-* `self.skill.skill_context.skill_id`: this is the id of the skill.
-* `self.skill.skill_context.decision_maker_address`: this is the address of the decision maker and is set to `"dummy_decision_maker_address"`.
+- `self.skill.skill_context.agent_address`: this is the agent identity the skill uses and is set to `"test_agent_address"`.
+- `self.skill.skill_context.search_service_address`: this is the address of the search service and is set to `"dummy_search_service_address"`.
+- `self.skill.skill_context.skill_id`: this is the id of the skill.
+- `self.skill.skill_context.decision_maker_address`: this is the address of the decision maker and is set to `"dummy_decision_maker_address"`.
### Some Useful `BaseSkillTestCase` Methods
There are a number of methods that `BaseSkillTestCase` offers to make testing skills easier. Some of these are mentioned below. For the rest, consult the API for `BaseSkillTestCase`:
-* `self.get_quantity_in_outbox()`: gives you the number of messages which are in the outbox. After running a part of the skill which is expected to send messages, you can use this method to assert the correct number of messages are indeed sent.
-* `self.get_message_from_outbox()`: gives you the last message in the outbox. Together with the above, you can use this method to grab the last message sent by the skill code you tested and check this is indeed the expected message.
-* `self.message_has_attributes(actual_message: Message, message_type: Type[Message], **kwargs,)`: you can use this method in tandem with the above method to check that a message has the attributes you expect it to have. You have to supply it with the actual message (e.g. using `self.get_message_from_outbox()`), specify its expected type (e.g. `FipaMessage`), and any other attribute you expect the message to have (e.g. `message_id` is 1) may be provided via keyword arguments.
-* `self.build_incoming_message`: this is an especially useful method to test handlers. Since handlers handle incoming messages, you can create an incoming message using this method to feed it to the handler and test its execution.
+- `self.get_quantity_in_outbox()`: gives you the number of messages which are in the outbox. After running a part of the skill which is expected to send messages, you can use this method to assert the correct number of messages are indeed sent.
+- `self.get_message_from_outbox()`: gives you the last message in the outbox. Together with the above, you can use this method to grab the last message sent by the skill code you tested and check this is indeed the expected message.
+- `self.message_has_attributes(actual_message: Message, message_type: Type[Message], **kwargs,)`: you can use this method in tandem with the above method to check that a message has the attributes you expect it to have. You have to supply it with the actual message (e.g. using `self.get_message_from_outbox()`), specify its expected type (e.g. `FipaMessage`), and any other attribute you expect the message to have (e.g. `message_id` is 1) may be provided via keyword arguments.
+- `self.build_incoming_message`: this is an especially useful method to test handlers. Since handlers handle incoming messages, you can create an incoming message using this method to feed it to the handler and test its execution.
#### Checking Logger Output
@@ -115,8 +117,8 @@ mock_logger.assert_any_call(logging.INFO, "some_logger_message")
In the above, we mock the logger before running `my_behaviour`'s `act()` method and check that the string `"some_logger_message"` is indeed passed to the logger.
-## Next steps
+## Next Steps
You can consult the `fetchai/generic_buyer` and `fetchai/generic_seller` skills and their associated tests here to study how `BaseSkillTestCase` can help you in testing your skills.
-You can also refer to the API to study the different methods `BaseSkillTestCase` makes available to make testing your skills easier.
+You can also refer to the API to study the different methods `BaseSkillTestCase` makes available to make testing your skills easier.
diff --git a/docs/skill.md b/docs/skill.md
index 3d21023e4a..ad85c9267f 100644
--- a/docs/skill.md
+++ b/docs/skill.md
@@ -1,15 +1,17 @@
+# Skills
+
`Skills` are the core focus of the framework's extensibility as they implement business logic to deliver economic value for the AEA. They are self-contained capabilities that AEAs can dynamically take on board, in order to expand their effectiveness in different situations.
A skill encapsulates implementations of the three abstract base classes `Handler`, `Behaviour`, `Model`, and is closely related with the abstract base class `Task`:
-* `Handler`: each skill has zero, one or more `Handler` objects, each responsible for the registered messaging protocol. Handlers implement AEAs' **reactive** behaviour. If the AEA understands the protocol referenced in a received `Envelope`, the `Handler` reacts appropriately to the corresponding message. Each `Handler` is responsible for only one protocol. A `Handler` is also capable of dealing with internal messages (see next section).
-* `Behaviour`: zero, one or more `Behaviours` encapsulate actions which further the AEAs goal and are initiated by internals of the AEA, rather than external events. Behaviours implement AEAs' **pro-activeness**. The framework provides a number of abstract base classes implementing different types of behaviours (e.g. cyclic/one-shot/finite-state-machine/etc.).
-* `Model`: zero, one or more `Models` that inherit from the `Model` class. `Models` encapsulate custom objects which are made accessible to any part of a skill via the `SkillContext`.
-* `Task`: zero, one or more `Tasks` encapsulate background work internal to the AEA. `Task` differs from the other three in that it is not a part of skills, but `Task`s are declared in or from skills if a packaging approach for AEA creation is used.
+- `Handler`: each skill has zero, one or more `Handler` objects, each responsible for the registered messaging protocol. Handlers implement AEAs' **reactive** behaviour. If the AEA understands the protocol referenced in a received `Envelope`, the `Handler` reacts appropriately to the corresponding message. Each `Handler` is responsible for only one protocol. A `Handler` is also capable of dealing with internal messages (see next section).
+- `Behaviour`: zero, one or more `Behaviours` encapsulate actions which further the AEAs goal and are initiated by internals of the AEA, rather than external events. Behaviours implement AEAs' **pro-activeness**. The framework provides a number of abstract base classes implementing different types of behaviours (e.g. cyclic/one-shot/finite-state-machine/etc.).
+- `Model`: zero, one or more `Models` that inherit from the `Model` class. `Models` encapsulate custom objects which are made accessible to any part of a skill via the `SkillContext`.
+- `Task`: zero, one or more `Tasks` encapsulate background work internal to the AEA. `Task` differs from the other three in that it is not a part of skills, but `Task`s are declared in or from skills if a packaging approach for AEA creation is used.
-A skill can read (parts of) the state of the the AEA (as summarised in the `AgentContext`), and suggest actions to the AEA according to its specific logic. As such, more than one skill could exist per protocol, competing with each other in suggesting to the AEA the best course of actions to take. In technical terms this means skills are horizontally arranged.
+A skill can read (parts of) the state of the AEA (as summarised in the `AgentContext`), and propose actions to the AEA according to its specific logic. As such, more than one skill could exist per protocol, competing with each other in suggesting to the AEA the best course of actions to take. In technical terms this means skills are horizontally arranged.
For instance, an AEA who is trading goods, could subscribe to more than one skill, where each skill corresponds to a different trading strategy. The skills could then read the preference and ownership state of the AEA, and independently suggest profitable transactions.
@@ -17,7 +19,7 @@ The framework places no limits on the complexity of skills. They can implement s
The framework provides one default skill, called `error`. Additional skills can be added as packages.
-## Independence of skills
+## Independence of Skills
Skills are `horizontally layered`, that is they run independently of each other. They also cannot access each other's state.
@@ -33,15 +35,15 @@ For example, in the `ErrorHandler(Handler)` class, the code often grabs a refere
``` python
self.context.outbox.put_message(message=reply)
-```
+```
Moreover, you can read/write to the _agent context namespace_ by accessing the attribute `SkillContext.namespace`.
Importantly, however, a skill does not have access to the context of another skill or protected AEA components like the `DecisionMaker`.
-## What to code
+## What to Code
-Each of the skill classes has three methods that must be implemented. All of them include a `setup()` and `teardown()` method which the developer must implement.
+Each of the skill classes has three methods that must be implemented. All of them include a `setup()` and `teardown()` method which the developer must implement.
Then there is a specific method that the framework requires for each class.
@@ -51,7 +53,7 @@ There can be none, one or more `Handler` class per skill.
`Handler` classes can receive `Message` objects of one protocol type only. However, `Handler` classes can send `Envelope` objects of any type of protocol they require.
-* `handle(self, message: Message)`: is where the skill receives a `Message` of the specified protocol and decides what to do with it.
+- `handle(self, message: Message)`: is where the skill receives a `Message` of the specified protocol and decides what to do with it.
A handler can be registered in one way:
@@ -71,7 +73,7 @@ Conceptually, a `Behaviour` class contains the business logic specific to initi
There can be one or more `Behaviour` classes per skill. The developer must create a subclass from the abstract class `Behaviour` to create a new `Behaviour`.
-* `act(self)`: is how the framework calls the `Behaviour` code.
+- `act(self)`: is how the framework calls the `Behaviour` code.
A behaviour can be registered in two ways:
@@ -82,14 +84,13 @@ The framework supports different types of behaviours:
- `OneShotBehaviour`: this behaviour is executed only once.
- `TickerBehaviour`: the `act()` method is called every `tick_interval`. E.g. if the `TickerBehaviour` subclass is instantiated
-
+
There is another category of behaviours, called `CompositeBehaviour`:
-- `SequenceBehaviour`: a sequence of `Behaviour` classes, executed
+- `SequenceBehaviour`: a sequence of `Behaviour` classes, executed
one after the other.
- `FSMBehaviour`: a state machine of `State` behaviours. A state is in charge of scheduling the next state.
-
If your behaviour fits one of the above, we suggest subclassing your
behaviour class with that behaviour class. Otherwise, you
can always subclass the general-purpose `Behaviour` class.
@@ -123,6 +124,7 @@ self.context.new_behaviours.put(HelloWorldBehaviour(name="hello_world", skill_co
```
Or, equivalently to the previous two code blocks:
+
``` python
def hello():
print("Hello, World!")
@@ -131,9 +133,9 @@ self.context.new_behaviours.put(OneShotBehaviour(act=hello, name="hello_world",
```
The callable passed to the `act` parameter is equivalent to the implementation
-of the `act` method described above.
+of the `act` method described above.
-The framework is then in charge of registering the behaviour and scheduling it
+The framework is then in charge of registering the behaviour and scheduling it
for execution.
### `tasks.py`
@@ -142,22 +144,23 @@ Conceptually, a `Task` is where the developer codes any internal tasks the AEA r
There can be one or more `Task` classes per skill. The developer subclasses abstract class `Task` to create a new `Task`.
-* `execute(self)`: is how the framework calls a `Task`.
+- `execute(self)`: is how the framework calls a `Task`.
The `Task` class implements the functor pattern.
-An instance of the `Task` class can be invoked as if it
+An instance of the `Task` class can be invoked as if it
were an ordinary function. Once completed, it will store the
result in the property `result`. Raises error if the task has not been executed yet,
or an error occurred during computation.
We suggest using the `task_manager`, accessible through the skill context,
-to manage long-running tasks. The task manager uses `multiprocessing` to
-schedule tasks, so be aware that the changes on the task object will
+to manage long-running tasks. The task manager uses `multiprocessing` to
+schedule tasks, so be aware that the changes on the task object will
not be updated.
Here's an example:
In `tasks.py`:
+
``` python
from aea.skills.tasks import Task
@@ -193,6 +196,7 @@ class LongTask(Task):
```
In `behaviours.py`:
+
``` python
from aea.skills.behaviours import TickerBehaviour
@@ -228,17 +232,19 @@ class MyBehaviour(TickerBehaviour):
The developer might want to add other classes on the context level which are shared equally across the `Handler`, `Behaviour` and `Task` classes. To this end, the developer can subclass an abstract `Model`. These models are made available on the context level upon initialization of the AEA.
Say, the developer has a class called `SomeModel`
+
``` python
class SomeModel(Model):
...
```
Then, an instance of this class is available on the context level like so:
+
``` python
some_model = self.context.some_model
-```
+```
-### Skill configuration
+### Skill Configuration
Each skill has a `skill.yaml` configuration file which lists all `Behaviour`, `Handler`, and `Task` objects pertaining to the skill.
@@ -265,22 +271,20 @@ protocols:
- fetchai/default:1.1.6
```
-
-## Error skill
+## Error Skill
All AEAs have a default `error` skill that contains error handling code for a number of scenarios:
-* Received envelopes with unsupported protocols
-* Received envelopes with unsupported skills (i.e. protocols for which no handler is registered)
-* Envelopes with decoding errors
-* Invalid messages with respect to the registered protocol
+- Received envelopes with unsupported protocols
+- Received envelopes with unsupported skills (i.e. protocols for which no handler is registered)
+- Envelopes with decoding errors
+- Invalid messages with respect to the registered protocol
The error skill relies on the `fetchai/default:1.0.0` protocol which provides error codes for the above.
+## Custom Error Handler
-## Custom Error handler
-
-The framework implements a default `ErrorHandler`.
+The framework implements a default `ErrorHandler`.
You can implement your own and mount it. The easiest way to do this is to run the following command to scaffold a custom `ErrorHandler`:
``` bash
@@ -288,7 +292,4 @@ aea scaffold error-handler
```
Now you will see a file called `error_handler.py` in the AEA project root.
-You can then implement your own custom logic to process messages.
-
-
-
+You can then implement your own custom logic to process messages.
diff --git a/docs/standalone-transaction.md b/docs/standalone-transaction.md
index 64a141d2a4..e4eda05418 100644
--- a/docs/standalone-transaction.md
+++ b/docs/standalone-transaction.md
@@ -1,7 +1,10 @@
+# Create Stand-Alone Transaction
+
In this guide, we will generate some wealth for the Fetch.ai testnet and create a standalone transaction. After the completion of the transaction, we get the transaction digest. With this we can search for the transaction on the block explorer
This guide requires the `aea-ledger-fetchai` plug-in installed in your Python environment:
-```bash
+
+``` bash
pip install aea-ledger-fetchai
```
@@ -24,7 +27,7 @@ FETCHAI_PRIVATE_KEY_FILE_1 = "fetchai_private_key_1.txt"
FETCHAI_PRIVATE_KEY_FILE_2 = "fetchai_private_key_2.txt"
```
-## Create the private keys
+## Create the Private Keys
``` python
# Create a private keys
@@ -36,7 +39,7 @@ FETCHAI_PRIVATE_KEY_FILE_2 = "fetchai_private_key_2.txt"
)
```
-## Create the wallets
+## Create the Wallets
Once we created the private keys we need to generate the wallets.
@@ -46,10 +49,11 @@ Once we created the private keys we need to generate the wallets.
wallet_2 = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_2})
```
-## Generate wealth
+## Generate Wealth
Since we want to send funds from `wallet_1` to `wallet_2`, we need to generate some wealth for the `wallet_1`. We can
do this with the following code
+
``` python
# Generate some wealth
try_generate_testnet_wealth(
@@ -57,7 +61,7 @@ do this with the following code
)
```
-## Send transaction
+## Send Transaction
Finally, we create a transaction that sends the funds to the `wallet_2`
@@ -87,75 +91,74 @@ Finally, we create a transaction that sends the funds to the `wallet_2`
logger.info("The transaction digest is {}".format(transaction_digest))
```
-Stand-alone transaction full code
-
-``` python
-import logging
-
-from aea_ledger_fetchai import FetchAICrypto
-
-from aea.crypto.helpers import create_private_key, try_generate_testnet_wealth
-from aea.crypto.ledger_apis import LedgerApis
-from aea.crypto.wallet import Wallet
-
-
-logger = logging.getLogger("aea")
-logging.basicConfig(level=logging.INFO)
-
-FETCHAI_PRIVATE_KEY_FILE_1 = "fetchai_private_key_1.txt"
-FETCHAI_PRIVATE_KEY_FILE_2 = "fetchai_private_key_2.txt"
-
-
-def run():
- """Run demo."""
-
- # Create a private keys
- create_private_key(
- FetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_1
- )
- create_private_key(
- FetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_2
- )
-
- # Set up the wallets
- wallet_1 = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_1})
- wallet_2 = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_2})
-
- # Generate some wealth
- try_generate_testnet_wealth(
- FetchAICrypto.identifier, wallet_1.addresses[FetchAICrypto.identifier]
- )
-
- logger.info(
- "Sending amount to {}".format(wallet_2.addresses.get(FetchAICrypto.identifier))
- )
-
- # Create the transaction and send it to the ledger.
- tx_nonce = LedgerApis.generate_tx_nonce(
- FetchAICrypto.identifier,
- wallet_2.addresses.get(FetchAICrypto.identifier),
- wallet_1.addresses.get(FetchAICrypto.identifier),
- )
- transaction = LedgerApis.get_transfer_transaction(
- identifier=FetchAICrypto.identifier,
- sender_address=wallet_1.addresses.get(FetchAICrypto.identifier),
- destination_address=wallet_2.addresses.get(FetchAICrypto.identifier),
- amount=1,
- tx_fee=1,
- tx_nonce=tx_nonce,
- )
- signed_transaction = wallet_1.sign_transaction(
- FetchAICrypto.identifier, transaction
- )
- transaction_digest = LedgerApis.send_signed_transaction(
- FetchAICrypto.identifier, signed_transaction
- )
-
- logger.info("Transaction complete.")
- logger.info("The transaction digest is {}".format(transaction_digest))
-
-
-if __name__ == "__main__":
- run()
-```
-
+??? note "Stand-alone transaction full code:"
+
+ ``` python
+ import logging
+
+ from aea_ledger_fetchai import FetchAICrypto
+
+ from aea.crypto.helpers import create_private_key, try_generate_testnet_wealth
+ from aea.crypto.ledger_apis import LedgerApis
+ from aea.crypto.wallet import Wallet
+
+
+ logger = logging.getLogger("aea")
+ logging.basicConfig(level=logging.INFO)
+
+ FETCHAI_PRIVATE_KEY_FILE_1 = "fetchai_private_key_1.txt"
+ FETCHAI_PRIVATE_KEY_FILE_2 = "fetchai_private_key_2.txt"
+
+
+ def run():
+ """Run demo."""
+
+ # Create a private keys
+ create_private_key(
+ FetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_1
+ )
+ create_private_key(
+ FetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_2
+ )
+
+ # Set up the wallets
+ wallet_1 = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_1})
+ wallet_2 = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_2})
+
+ # Generate some wealth
+ try_generate_testnet_wealth(
+ FetchAICrypto.identifier, wallet_1.addresses[FetchAICrypto.identifier]
+ )
+
+ logger.info(
+ "Sending amount to {}".format(wallet_2.addresses.get(FetchAICrypto.identifier))
+ )
+
+ # Create the transaction and send it to the ledger.
+ tx_nonce = LedgerApis.generate_tx_nonce(
+ FetchAICrypto.identifier,
+ wallet_2.addresses.get(FetchAICrypto.identifier),
+ wallet_1.addresses.get(FetchAICrypto.identifier),
+ )
+ transaction = LedgerApis.get_transfer_transaction(
+ identifier=FetchAICrypto.identifier,
+ sender_address=wallet_1.addresses.get(FetchAICrypto.identifier),
+ destination_address=wallet_2.addresses.get(FetchAICrypto.identifier),
+ amount=1,
+ tx_fee=1,
+ tx_nonce=tx_nonce,
+ )
+ signed_transaction = wallet_1.sign_transaction(
+ FetchAICrypto.identifier, transaction
+ )
+ transaction_digest = LedgerApis.send_signed_transaction(
+ FetchAICrypto.identifier, signed_transaction
+ )
+
+ logger.info("Transaction complete.")
+ logger.info("The transaction digest is {}".format(transaction_digest))
+
+
+ if __name__ == "__main__":
+ run()
+ ```
diff --git a/docs/step-one.md b/docs/step-one.md
index f5e818e7fe..bb6b5bdabe 100644
--- a/docs/step-one.md
+++ b/docs/step-one.md
@@ -1,14 +1,12 @@
+# Ways to Build an AEA
+
There are a number of ways to build an AEA:
-
-
To start with, we recommended you build an AEA project step-by-step with the CLI tool as demonstrated in the quick start guide and described here.
-
Using the CLI aea fetch command, pull in an already built project and run as is or extend it to your needs.
-
The last option is to build an AEA programmatically as described here.
-
+
+- To start with, we recommended you build an AEA project step-by-step with the CLI tool as demonstrated in the quick start guide and described here.
+- Using the CLI `aea fetch` command, pull in an already built project and run as is or extend it to your needs.
+- The last option is to build an AEA programmatically as described here.
Sometimes, an AEA is more than is required for the task at hand. In particular, an AEA is much more than just an agent. In those cases, we suggest you have a look at the following two guides:
-
-
the AEA vs Agents guide shows the difference between an agent and an AEA in code,
-
the Use multiplexer standalone guide shows how to use the multiplexer on its own to receive and send envelopes.
-
-
\ No newline at end of file
+- the AEA vs Agents guide shows the difference between an agent and an AEA in code,
+- the Use multiplexer standalone guide shows how to use the multiplexer on its own to receive and send envelopes.
diff --git a/docs/tac-skills-contract.md b/docs/tac-skills-contract.md
index 909369e23c..f8f2173ef5 100644
--- a/docs/tac-skills-contract.md
+++ b/docs/tac-skills-contract.md
@@ -1,13 +1,15 @@
+# TAC Skills Ledger-Based
+
The AEA TAC - trading agent competition - skills demonstrate an interaction between multiple AEAs in a game.
There are two types of AEAs:
-* The `tac_controller` which coordinates the game.
-* The `tac_participant` AEAs which compete in the game. The `tac_participant` AEAs trade tokens with each other to maximize their utility.
+- The `tac_controller` which coordinates the game.
+- The `tac_participant` AEAs which compete in the game. The `tac_participant` AEAs trade tokens with each other to maximize their utility.
## Discussion
-This demo shows how agents negotiate autonomously with each other while they pursue their goals by participating in the Trading Agents Competition (TAC).
+This demo shows how agents negotiate autonomously with each other while they pursue their goals by participating in the Trading Agents Competition (TAC).
The demo can be run against Fetchai or Ethereum ledger.
Transactions are validated on an ERC1155 smart contract on the Fetchai Dorado or a local Ganache Ethereum testnet.
@@ -18,13 +20,15 @@ In the following video we discuss the framework and TAC in more detail:
## Communication
There are two types of interactions:
+
- between the controller and participants (game management communication)
- between the participants (negotiations)
-### Registration communication
-This diagram shows the communication between the various entities during the registration phase.
+### Registration Communication
-
+This diagram shows the communication between the various entities during the registration phase.
+
+``` mermaid
sequenceDiagram
participant Agent_2
participant Agent_1
@@ -50,12 +54,13 @@ This diagram shows the communication between the various entities during the reg
deactivate Agent_2
deactivate Search
deactivate Controller
-
+```
-### Transaction communication
-This diagram shows the communication between two AEAs and a controller. In this case, we have a `Seller_Agent` which is set up as a seller (and registers itself as such with the controller during the registration phase). We also have the `Searching_Agent` which is set up to search for sellers.
+### Transaction Communication
-
+This diagram shows the communication between two AEAs and a controller. In this case, we have a `Seller_Agent` which is set up as a seller (and registers itself as such with the controller during the registration phase). We also have the `Searching_Agent` which is set up to search for sellers.
+
+``` mermaid
sequenceDiagram
participant Buyer_Agent
participant Seller_Agent
@@ -83,72 +88,68 @@ This diagram shows the communication between two AEAs and a controller. In this
deactivate Seller_Agent
deactivate Search
deactivate Controller
-
-
+```
In the above case, the proposal received contains a set of goods to sell and an associated price. The buyer AEA needs to determine if this is a good deal for them, and if so, it accepts.
-There is an equivalent diagram for seller AEAs set up to search for buyers and their interaction with AEAs which are registered as buyers. In that scenario, the proposal will instead be a list of goods that the buyer wishes to buy and the price it is willing to pay for them.
-
+There is an equivalent diagram for seller AEAs set up to search for buyers and their interaction with AEAs which are registered as buyers. In that scenario, the proposal will instead be a list of goods that the buyer wishes to buy and the price it is willing to pay for them.
-## Preparation instructions
+## Preparation Instructions
### Dependencies
Follow the Preliminaries and Installation sections from the AEA quick start.
-## Demo instructions (Fetchai):
+## Demo Instructions (Fetchai)
Follow this instruction to run TAC against the fetch.ai Dorado testnet.
-### Fetch TAC controller AEA
+### Fetch TAC Controller AEA
In the root directory, fetch the controller AEA:
-``` bash
-aea fetch fetchai/tac_controller_contract:0.32.4
-cd tac_controller_contract
-aea install
-aea build
-```
-Alternatively, create from scratch.
-
-
-The following steps create the controller from scratch:
``` bash
-aea create tac_controller_contract
+aea fetch fetchai/tac_controller_contract:0.32.4
cd tac_controller_contract
-aea add connection fetchai/p2p_libp2p:0.27.4
-aea add connection fetchai/soef:0.27.5
-aea add connection fetchai/ledger:0.21.4
-aea add skill fetchai/tac_control_contract:0.27.5
-aea config set --type dict agent.dependencies \
-'{
- "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"},
- "aea-ledger-ethereum": {"version": "<2.0.0,>=1.0.0"}
-}'
-aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
-aea config set agent.default_ledger fetchai
-aea config set vendor.fetchai.connections.soef.config.chain_identifier fetchai_v2_misc
-aea config set --type bool vendor.fetchai.skills.tac_control.is_abstract true
-aea config set --type dict agent.default_routing \
-'{
- "fetchai/contract_api:1.1.6": "fetchai/ledger:0.21.4",
- "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
- "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
-}'
-aea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \
-'[{"identifier": "acn", "ledger_id": "fetchai", "not_after": "2023-01-01", "not_before": "2022-01-01", "public_key": "fetchai", "save_path": ".certs/conn_cert.txt"}]'
aea install
aea build
```
-
-
-
-### Fetch the TAC participant AEAs
+??? note "Alternatively, create from scratch:"
+ The following steps create the controller from scratch:
+
+ ``` bash
+ aea create tac_controller_contract
+ cd tac_controller_contract
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/tac_control_contract:0.27.5
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"},
+ "aea-ledger-ethereum": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
+ aea config set agent.default_ledger fetchai
+ aea config set vendor.fetchai.connections.soef.config.chain_identifier fetchai_v2_misc
+ aea config set --type bool vendor.fetchai.skills.tac_control.is_abstract true
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/contract_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
+ }'
+ aea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \
+ '[{"identifier": "acn", "ledger_id": "fetchai", "not_after": "2023-01-01", "not_before": "2022-01-01", "public_key": "fetchai", "save_path": ".certs/conn_cert.txt"}]'
+ aea install
+ aea build
+ ```
+
+### Fetch the TAC Participant AEAs
In separate terminals, in the root directory, fetch at least two participants:
+
``` bash
aea fetch fetchai/tac_participant_contract:0.22.4 --alias tac_participant_one
cd tac_participant_one
@@ -161,110 +162,111 @@ aea install
aea build
```
-Alternatively, create from scratch.
-
-
-
-### Add keys for all AEAs
+??? note "Alternatively, create from scratch:"
+ In a separate terminal, in the root directory, create at least two tac participant AEAs:
+
+ ``` bash
+ aea create tac_participant_one
+ aea create tac_participant_two
+ ```
+
+ Build participant one:
+
+ ``` bash
+ cd tac_participant_one
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/tac_participation:0.25.5
+ aea add skill fetchai/tac_negotiation:0.29.5
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"},
+ "aea-ledger-ethereum": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
+ aea config set agent.default_ledger fetchai
+ aea config set vendor.fetchai.connections.soef.config.chain_identifier fetchai_v2_misc
+ aea config set vendor.fetchai.skills.tac_participation.models.game.args.is_using_contract 'True' --type bool
+ aea config set vendor.fetchai.skills.tac_negotiation.models.strategy.args.is_contract_tx 'True' --type bool
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/contract_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
+ }'
+ aea config set --type dict agent.decision_maker_handler \
+ '{
+ "dotted_path": "aea.decision_maker.gop:DecisionMakerHandler",
+ "file_path": null
+ }'
+ aea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \
+ '''[{"identifier": "acn", "ledger_id": "fetchai", "message_format": "'{public_key}'", "not_after": "2023-01-01", "not_before": "2022-01-01", "public_key": "fetchai", "save_path": ".certs/conn_cert.txt"}]'''
+ aea install
+ aea build
+ ```
+
+ Then, build participant two:
+
+ ``` bash
+ cd tac_participant_two
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/tac_participation:0.25.5
+ aea add skill fetchai/tac_negotiation:0.29.5
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"},
+ "aea-ledger-ethereum": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
+ aea config set agent.default_ledger fetchai
+ aea config set vendor.fetchai.connections.soef.config.chain_identifier fetchai_v2_misc
+ aea config set vendor.fetchai.skills.tac_participation.models.game.args.is_using_contract 'True' --type bool
+ aea config set vendor.fetchai.skills.tac_negotiation.models.strategy.args.is_contract_tx 'True' --type bool
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/contract_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
+ }'
+ aea config set --type dict agent.decision_maker_handler \
+ '{
+ "dotted_path": "aea.decision_maker.gop:DecisionMakerHandler",
+ "file_path": null
+ }'
+ aea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \
+ '''[{"identifier": "acn", "ledger_id": "fetchai", "message_format": "'{public_key}'", "not_after": "2023-01-01", "not_before": "2022-01-01", "public_key": "fetchai", "save_path": ".certs/conn_cert.txt"}]'''
+ aea install
+ aea build
+ ```
+
+### Add Keys for All AEAs
For every AEA in the competition (controller and participants):
First generate and add a private key:
+
``` bash
aea generate-key fetchai
aea add-key fetchai fetchai_private_key.txt
```
Then create and add a separate private key for secure communication:
+
``` bash
aea generate-key fetchai fetchai_connection_private_key.txt
aea add-key fetchai fetchai_connection_private_key.txt --connection
```
Finally, certify the key for use by the connections that request that:
+
``` bash
aea issue-certificates
```
-### Update the game parameters in the controller
+### Update the Game Parameters in the Controller
In the tac controller project, get and set the registration start time (set it to at least 5 minutes in the future):
@@ -274,23 +276,23 @@ aea config set vendor.fetchai.skills.tac_control_contract.models.parameters.args
```
To set the registration time, you may find handy the following command:
+
``` bash
aea config set vendor.fetchai.skills.tac_control_contract.models.parameters.args.registration_start_time "$(date -d "5 minutes" +'%d %m %Y %H:%M')"
```
-
-### Update the connection parameters
+### Update the Connection Parameters
Update the connection parameters of the TAC participants to allow them to connect to the same local agent communication network as the TAC controller.
First, retrieve controller's local ACN address by running the following in the controller agent's project terminal:
-```bash
+``` bash
aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.4 -u public_uri
```
-
Then, in participant one, run this command (replace `SOME_ADDRESS` with the value you retrieved above):
+
``` bash
aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
'{
@@ -303,6 +305,7 @@ aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
```
Do the same in participant two (beware of the different port numbers):
+
``` bash
aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
'{
@@ -314,11 +317,11 @@ aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
}'
```
-## Fund agents' accounts
+## Fund Agents' Accounts
Retrieve the address of each agent (in each terminal):
-```bash
+``` bash
aea get-address fetchai
```
@@ -333,13 +336,15 @@ aea get-wealth fetchai
### Run the AEAs
First, launch the `tac_contract_controller` then the participants by executing the following from their respective terminals:
+
``` bash
aea run
```
-The CLI tool supports launching several agents at once.
+The CLI tool supports launching several agents at once.
For example, assuming you followed the tutorial, you
can launch both TAC participant agents as follows from the root directory (ensure you run the controller agent first as above):
+
``` bash
aea launch tac_participant_one tac_participant_two
```
@@ -351,64 +356,63 @@ in the same process.
### Cleaning up
When you're finished, delete your AEAs:
+
``` bash
aea delete tac_controller_contract
aea delete tac_participant_one
aea delete tac_participant_two
```
-## Demo instructions (Ethereum):
+## Demo Instructions (Ethereum)
Follow this instruction to run TAC against a local Ganache Ethereum test-net.
-### Create TAC controller AEA
+### Create TAC Controller AEA
In the root directory, fetch the controller AEA:
-``` bash
-aea fetch fetchai/tac_controller_contract:0.32.4
-cd tac_controller_contract
-aea install
-aea build
-```
-Alternatively, create from scratch.
-
-
-The following steps create the controller from scratch:
``` bash
-aea create tac_controller_contract
+aea fetch fetchai/tac_controller_contract:0.32.4
cd tac_controller_contract
-aea add connection fetchai/p2p_libp2p:0.27.4
-aea add connection fetchai/soef:0.27.5
-aea add connection fetchai/ledger:0.21.4
-aea add skill fetchai/tac_control_contract:0.27.5
-aea config set --type dict agent.dependencies \
-'{
- "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"},
- "aea-ledger-ethereum": {"version": "<2.0.0,>=1.0.0"}
-}'
-aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
-aea config set agent.default_ledger ethereum
-aea config set vendor.fetchai.connections.soef.config.chain_identifier ethereum
-aea config set --type bool vendor.fetchai.skills.tac_control.is_abstract true
-aea config set --type dict agent.default_routing \
-'{
- "fetchai/contract_api:1.1.6": "fetchai/ledger:0.21.4",
- "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
- "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
-}'
-aea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \
-'[{"identifier": "acn", "ledger_id": "ethereum", "not_after": "2023-01-01", "not_before": "2022-01-01", "public_key": "fetchai", "save_path": ".certs/conn_cert.txt"}]'
aea install
aea build
```
-
-
-
-### Fetch the TAC participant AEAs
+??? note "Alternatively, create from scratch:"
+ The following steps create the controller from scratch:
+
+ ``` bash
+ aea create tac_controller_contract
+ cd tac_controller_contract
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/tac_control_contract:0.27.5
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"},
+ "aea-ledger-ethereum": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
+ aea config set agent.default_ledger ethereum
+ aea config set vendor.fetchai.connections.soef.config.chain_identifier ethereum
+ aea config set --type bool vendor.fetchai.skills.tac_control.is_abstract true
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/contract_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
+ }'
+ aea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \
+ '[{"identifier": "acn", "ledger_id": "ethereum", "not_after": "2023-01-01", "not_before": "2022-01-01", "public_key": "fetchai", "save_path": ".certs/conn_cert.txt"}]'
+ aea install
+ aea build
+ ```
+
+### Fetch the TAC Participant AEAs
In separate terminals, in the root directory, fetch at least two participants:
+
``` bash
aea fetch fetchai/tac_participant_contract:0.22.4 --alias tac_participant_one
cd tac_participant_one
@@ -421,121 +425,122 @@ aea install
aea build
```
-Alternatively, create from scratch.
-
-
-
-### Configure the agents to use Ethereum
+??? note "Alternatively, create from scratch:"
+ In a separate terminal, in the root directory, create at least two tac participant AEAs:
+
+ ``` bash
+ aea create tac_participant_one
+ aea create tac_participant_two
+ ```
+
+ Build participant one:
+
+ ``` bash
+ cd tac_participant_one
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/tac_participation:0.25.5
+ aea add skill fetchai/tac_negotiation:0.29.5
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"},
+ "aea-ledger-ethereum": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
+ aea config set agent.default_ledger ethereum
+ aea config set vendor.fetchai.connections.soef.config.chain_identifier ethereum
+ aea config set vendor.fetchai.skills.tac_participation.models.game.args.is_using_contract 'True' --type bool
+ aea config set vendor.fetchai.skills.tac_negotiation.models.strategy.args.is_contract_tx 'True' --type bool
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/contract_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
+ }'
+ aea config set --type dict agent.decision_maker_handler \
+ '{
+ "dotted_path": "aea.decision_maker.gop:DecisionMakerHandler",
+ "file_path": null
+ }'
+ aea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \
+ '''[{"identifier": "acn", "ledger_id": "ethereum", "message_format": "'{public_key}'", "not_after": "2023-01-01", "not_before": "2022-01-01", "public_key": "fetchai", "save_path": ".certs/conn_cert.txt"}]'''
+ aea install
+ aea build
+ ```
+
+ Then, build participant two:
+
+ ``` bash
+ cd tac_participant_two
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/tac_participation:0.25.5
+ aea add skill fetchai/tac_negotiation:0.29.5
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"},
+ "aea-ledger-ethereum": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
+ aea config set agent.default_ledger ethereum
+ aea config set vendor.fetchai.connections.soef.config.chain_identifier ethereum
+ aea config set vendor.fetchai.skills.tac_participation.models.game.args.is_using_contract 'True' --type bool
+ aea config set vendor.fetchai.skills.tac_negotiation.models.strategy.args.is_contract_tx 'True' --type bool
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/contract_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
+ }'
+ aea config set --type dict agent.decision_maker_handler \
+ '{
+ "dotted_path": "aea.decision_maker.gop:DecisionMakerHandler",
+ "file_path": null
+ }'
+ aea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \
+ '''[{"identifier": "acn", "ledger_id": "ethereum", "message_format": "'{public_key}'", "not_after": "2023-01-01", "not_before": "2022-01-01", "public_key": "fetchai", "save_path": ".certs/conn_cert.txt"}]'''
+ aea install
+ aea build
+ ```
+
+### Configure the Agents to Use Ethereum
Run the following in every AEA's terminal:
-```bash
+``` bash
aea config set agent.default_ledger ethereum
json=$(printf '[{"identifier": "acn", "ledger_id": "ethereum", "not_after": "2023-01-01", "not_before": "2022-01-01", "public_key": "fetchai", "message_format": "{public_key}", "save_path": ".certs/conn_cert.txt"}]')
aea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests "$json"
aea config set vendor.fetchai.connections.soef.config.chain_identifier ethereum
```
-### Add keys for all AEAs
+### Add Keys for All AEAs
For every AEA in the competition (controller and participants):
First generate and add a private key:
+
``` bash
aea generate-key ethereum
aea add-key ethereum ethereum_private_key.txt
```
Then create and add a separate private key for secure communication:
+
``` bash
aea generate-key fetchai fetchai_connection_private_key.txt
aea add-key fetchai fetchai_connection_private_key.txt --connection
```
Finally, certify the key for use by the connections that request that:
+
``` bash
aea issue-certificates
```
-### Update the game parameters in the controller
+### Update the Game Parameters in the Controller
In the tac controller project, get and set the registration start time (set it to at least 5 minutes in the future):
@@ -545,22 +550,23 @@ aea config set vendor.fetchai.skills.tac_control_contract.models.parameters.args
```
To set the registration time, you may find handy the following command:
+
``` bash
aea config set vendor.fetchai.skills.tac_control_contract.models.parameters.args.registration_start_time "$(date -d "5 minutes" +'%d %m %Y %H:%M')"
```
-
-### Update the connection parameters
+### Update the Connection Parameters
Update the connection parameters of the TAC participants to allow them to connect to the same local agent communication network as the TAC controller.
First, retrieve controller's local ACN address by running the following in the controller agent's project terminal:
-```bash
+``` bash
aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.4 -u public_uri
```
Then, in participant one, run this command (replace `SOME_ADDRESS` with the value you retrieved above):
+
``` bash
aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
'{
@@ -573,6 +579,7 @@ aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
```
Do the same in participant two (beware of the different port numbers):
+
``` bash
aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
'{
@@ -584,9 +591,10 @@ aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
}'
```
-## Fund agents' accounts
+## Fund Agents' Accounts
Run a local Ganache Ethereum test-net with funds for the addresses of the three AEAs in this demo:
+
``` bash
docker run -p 8545:8545 trufflesuite/ganache-cli:latest --verbose --gasPrice=0 --gasLimit=0x1fffffffffffff --account="$(cat tac_controller_contract/ethereum_private_key.txt),1000000000000000000000" --account="$(cat tac_participant_one/ethereum_private_key.txt),1000000000000000000000" --account="$(cat tac_participant_two/ethereum_private_key.txt),1000000000000000000000"
```
@@ -602,13 +610,15 @@ You should get `1000000000000000000000`.
### Run the AEAs
First, launch the `tac_contract_controller` then the participants by executing the following from their respective terminals:
+
``` bash
aea run
```
-The CLI tool supports launching several agents at once.
+The CLI tool supports launching several agents at once.
For example, assuming you followed the tutorial, you
can launch both TAC participant agents as follows from the root directory (ensure you run the controller agent first as above):
+
``` bash
aea launch tac_participant_one tac_participant_two
```
@@ -620,6 +630,7 @@ in the same process.
### Cleaning up
When you're finished, delete your AEAs:
+
``` bash
aea delete tac_controller_contract
aea delete tac_participant_one
diff --git a/docs/tac-skills.md b/docs/tac-skills.md
index 2154767d12..69284df352 100644
--- a/docs/tac-skills.md
+++ b/docs/tac-skills.md
@@ -1,25 +1,28 @@
+# TAC Skills
+
The AEA TAC - trading agent competition - skills demonstrate an interaction between multiple AEAs in a game.
There are two types of AEAs:
-* The `tac_controller` which coordinates the game.
-* The `tac_participant` AEAs which compete in the game. The `tac_participant` AEAs trade tokens with each other to maximize their utility.
+- The `tac_controller` which coordinates the game.
+- The `tac_participant` AEAs which compete in the game. The `tac_participant` AEAs trade tokens with each other to maximize their utility.
## Discussion
-The scope of this specific demo is to demonstrate how the agents negotiate autonomously with each other while they pursue their goals by playing a game of TAC. Another AEA has the role of the controller and it's responsible for calculating the revenue for each participant and if the transaction messages are valid. Transactions are settled with the controller agent rather than against a public ledger.
+The scope of this specific demo is to demonstrate how the agents negotiate autonomously with each other while they pursue their goals by playing a game of TAC. Another AEA has the role of the controller, responsible for calculating the revenue for each participant and checking if the transaction messages are valid. Transactions are settled with the controller agent rather than against a public ledger.
## Communication
There are two types of interactions:
+
- between the participants and the controller, the game communication
- between the participants, the negotiation
-### Registration communication
+### Registration Communication
-This diagram shows the communication between the various entities during the registration phase.
+This diagram shows the communication between the various entities during the registration phase.
-
+``` mermaid
sequenceDiagram
participant Agent_2
participant Agent_1
@@ -46,13 +49,13 @@ This diagram shows the communication between the various entities during the reg
deactivate Agent_2
deactivate Search
deactivate Controller
-
+```
-### Transaction communication
+### Transaction Communication
This diagram shows the communication between two AEAs and the controller. In this case, we have an AEA in the role of the seller, referred to as `Seller_Agent`. We also have an AEA in the role of the buyer, referred to as `Buyer_Agent`. During a given TAC, an AEA can be in both roles simultaneously in different bilateral interactions.
-
+``` mermaid
sequenceDiagram
participant Buyer_Agent
participant Seller_Agent
@@ -80,22 +83,21 @@ This diagram shows the communication between two AEAs and the controller. In thi
deactivate Seller_Agent
deactivate Search
deactivate Controller
-
-
+```
In the above case, the proposal received contains a set of good which the seller wishes to sell and a cost of them. The buyer AEA needs to determine if this is a good deal for them and if so, it accepts.
-There is an equivalent diagram for seller AEAs set up to search for buyers and their interaction with AEAs which are registered as buyers. In that scenario, the proposal will instead, be a list of goods that the buyer wishes to buy and the price it is willing to pay for them.
+There is an equivalent diagram for seller AEAs set up to search for buyers and their interaction with AEAs which are registered as buyers. In that scenario, the proposal will instead, be a list of goods that the buyer wishes to buy and the price it is willing to pay for them.
-## Option 1: AEA Manager approach
+## Option 1: AEA Manager Approach
-Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.
+Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.
-### Preparation instructions
+### Preparation Instructions
Install the AEA Manager.
-### Demo instructions
+### Demo Instructions
The following steps assume you have launched the AEA Manager Desktop app.
@@ -109,88 +111,88 @@ The following steps assume you have launched the AEA Manager Desktop app.
5. Run the `controller` AEA. Navigate to its logs and copy the multiaddress displayed. Stop the `controller`.
-5. Navigate to the settings of `participant_1` and under `components > connection >` `fetchai/p2p_libp2p:0.22.0` update as follows (make sure to replace the placeholder with the multiaddress):
-``` bash
-{
- "delegate_uri": "127.0.0.1:11001",
- "entry_peers": ["REPLACE_WITH_MULTI_ADDRESS_HERE"],
- "local_uri": "127.0.0.1:9001",
- "log_file": "libp2p_node.log",
- "public_uri": "127.0.0.1:9001"
-}
-```
+6. Navigate to the settings of `participant_1` and under `components > connection >` `fetchai/p2p_libp2p:0.22.0` update as follows (make sure to replace the placeholder with the multiaddress):
-6. Navigate to the settings of `participant_2` and under `components > connection >` `fetchai/p2p_libp2p:0.22.0` update as follows (make sure to replace the placeholder with the multiaddress):
-``` bash
-{
- "delegate_uri": "127.0.0.1:11002",
- "entry_peers": ["REPLACE_WITH_MULTI_ADDRESS_HERE"],
- "local_uri": "127.0.0.1:9002",
- "log_file": "libp2p_node.log",
- "public_uri": "127.0.0.1:9002"
-}
-```
+ ``` bash
+ {
+ "delegate_uri": "127.0.0.1:11001",
+ "entry_peers": ["REPLACE_WITH_MULTI_ADDRESS_HERE"],
+ "local_uri": "127.0.0.1:9001",
+ "log_file": "libp2p_node.log",
+ "public_uri": "127.0.0.1:9001"
+ }
+ ```
+
+7. Navigate to the settings of `participant_2` and under `components > connection >` `fetchai/p2p_libp2p:0.22.0` update as follows (make sure to replace the placeholder with the multiaddress):
+
+ ``` bash
+ {
+ "delegate_uri": "127.0.0.1:11002",
+ "entry_peers": ["REPLACE_WITH_MULTI_ADDRESS_HERE"],
+ "local_uri": "127.0.0.1:9002",
+ "log_file": "libp2p_node.log",
+ "public_uri": "127.0.0.1:9002"
+ }
+ ```
-7. You may add more participants by repeating steps 3 (with an updated name) and 6 (bumping the port numbers. See the difference between steps 5 and 6).
+8. You may add more participants by repeating steps 3 (with an updated name) and 6 (bumping the port numbers. See the difference between steps 5 and 6).
-8. Run the `controller`, then `participant_1` and `participant_2` (and any other participants you added).
+9. Run the `controller`, then `participant_1` and `participant_2` (and any other participants you added).
In the `controller`'s log, you should see the details of the transactions participants submit as well as changes in their scores and holdings. In participants' logs, you should see the agents trading.
-
-## Option 2: CLI approach
+## Option 2: CLI Approach
Follow this approach when using the `aea` CLI.
-## Preparation instructions
+## Preparation Instructions
### Dependencies
Follow the Preliminaries and Installation sections from the AEA quick start.
-## Demo instructions:
+## Demo Instructions
-### Create TAC controller AEA
+### Create TAC Controller AEA
In the root directory, fetch the controller AEA:
-``` bash
-aea fetch fetchai/tac_controller:0.30.4
-cd tac_controller
-aea install
-aea build
-```
-
-Alternatively, create from scratch.
-
-The following steps create the controller from scratch:
``` bash
-aea create tac_controller
+aea fetch fetchai/tac_controller:0.30.4
cd tac_controller
-aea add connection fetchai/p2p_libp2p:0.27.4
-aea add connection fetchai/soef:0.27.5
-aea add connection fetchai/ledger:0.21.4
-aea add skill fetchai/tac_control:0.25.5
-aea config set --type dict agent.dependencies \
-'{
- "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"}
-}'
-aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
-aea config set agent.default_ledger fetchai
-aea config set --type dict agent.default_routing \
-'{
- "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
-}'
aea install
aea build
```
-
-
-
-### Create the TAC participant AEAs
+??? note "Alternatively, create from scratch:"
+
+ The following steps create the controller from scratch:
+
+ ``` bash
+ aea create tac_controller
+ cd tac_controller
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/tac_control:0.25.5
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
+ aea config set agent.default_ledger fetchai
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
+ }'
+ aea install
+ aea build
+ ```
+
+### Create the TAC Participant AEAs
In a separate terminal, in the root directory, fetch at least two participants:
+
``` bash
aea fetch fetchai/tac_participant:0.32.4 --alias tac_participant_one
cd tac_participant_one
@@ -202,94 +204,96 @@ cd tac_participant_two
aea build
```
-Alternatively, create from scratch.
-
-
-
-### Add keys for all AEAs
+??? note "Alternatively, create from scratch:"
+
+ In a separate terminal, in the root directory, create at least two tac participant AEAs:
+
+ ``` bash
+ aea create tac_participant_one
+ aea create tac_participant_two
+ ```
+
+ Build participant one:
+
+ ``` bash
+ cd tac_participant_one
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/tac_participation:0.25.5
+ aea add skill fetchai/tac_negotiation:0.29.5
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
+ aea config set agent.default_ledger fetchai
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
+ }'
+ aea config set --type dict agent.decision_maker_handler \
+ '{
+ "dotted_path": "aea.decision_maker.gop:DecisionMakerHandler",
+ "file_path": null
+ }'
+ aea install
+ aea build
+ ```
+
+ Then, build participant two:
+
+ ``` bash
+ cd tac_participant_two
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/tac_participation:0.25.5
+ aea add skill fetchai/tac_negotiation:0.29.5
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
+ aea config set agent.default_ledger fetchai
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
+ }'
+ aea config set --type dict agent.decision_maker_handler \
+ '{
+ "dotted_path": "aea.decision_maker.gop:DecisionMakerHandler",
+ "file_path": null
+ }'
+ aea install
+ aea build
+ ```
+
+### Add Keys for All AEAs
Create the private key for the AEA for Fetch.ai `Dorado`:
+
``` bash
aea generate-key fetchai
aea add-key fetchai fetchai_private_key.txt
```
Next, create a private key used to secure the AEA's communications:
+
``` bash
aea generate-key fetchai fetchai_connection_private_key.txt
aea add-key fetchai fetchai_connection_private_key.txt --connection
```
Finally, certify the key for use by the connections that request that:
+
``` bash
aea issue-certificates
```
-### Update the game parameters in the controller
+### Update the Game Parameters in the Controller
Navigate to the tac controller project, then use the command line to get and set the start time (set it to at least two minutes in the future):
@@ -299,11 +303,12 @@ aea config set vendor.fetchai.skills.tac_control.models.parameters.args.registra
```
To set the registration time, you may find handy the following command:
+
``` bash
aea config set vendor.fetchai.skills.tac_control.models.parameters.args.registration_start_time "$(date -d "2 minutes" +'%d %m %Y %H:%M')"
```
-### Update the connection parameters
+### Update the Connection Parameters
Briefly run the controller AEA:
@@ -314,6 +319,7 @@ aea run
Once you see a message of the form `To join its network use multiaddr 'SOME_ADDRESS'` take note of the address. (Alternatively, use `aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.4 -u public_uri` to retrieve the address.)
Then, in the participant one, run this command (replace `SOME_ADDRESS` with the correct value as described above):
+
``` bash
aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
'{
@@ -326,6 +332,7 @@ aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
```
Do the same in participant two (beware of the different port numbers):
+
``` bash
aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
'{
@@ -339,10 +346,10 @@ aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
This allows the TAC participants to connect to the same local agent communication network as the TAC controller.
-
### Run the AEAs
First, launch the `tac_controller`:
+
``` bash
aea run
```
@@ -352,6 +359,7 @@ at once.
For example, assuming you followed the tutorial, you
can launch both the TAC agents as follows from the root directory:
+
``` bash
aea launch tac_participant_one tac_participant_two
```
@@ -363,8 +371,9 @@ in the same process.
### Cleaning up
When you're finished, delete your AEAs:
+
``` bash
aea delete tac_controller
aea delete tac_participant_one
aea delete tac_participant_two
-```
\ No newline at end of file
+```
diff --git a/docs/tac.md b/docs/tac.md
index 74b60dc6ac..bc060cbd7d 100644
--- a/docs/tac.md
+++ b/docs/tac.md
@@ -1,16 +1,19 @@
-The original TAC has its own repo.
+# TAC External App
-Follow the instructions below to build and run the TAC demo.
+!!! note
+ This app is no longer maintained.
+
+The original TAC has its own repo.
+Follow the instructions below to build and run the TAC demo.
## Requirements
Make sure you are running Docker and Docker Compose.
+## Quick Start
-## Quick start
-
-Clone the repo to include sub-modules.
+Clone the repo to include submodules.
``` bash
git clone git@github.com:fetchai/agents-tac.git --recursive && cd agents-tac
@@ -24,7 +27,6 @@ which pipenv
If you don't have it, install it. Instructions are here.
-
Create and launch a virtual environment.
``` bash
@@ -37,13 +39,12 @@ Install the dependencies.
pipenv install
```
-
Install the package.
+
``` bash
python setup.py install
```
-
Run the launch script. This may take a while.
``` bash
@@ -58,7 +59,7 @@ In the Environment tab, make sure you have the `tac_controller` environment sele
-## Alternative build and run
+## Alternative Build and Run
In a new terminal window, clone the repo, build the sandbox, and launch it.
@@ -79,7 +80,7 @@ python templates/v1/basic.py --name my_agent --dashboard
Click through to the controller GUI.
-## Possible gotchas
+## Possible Gotchas
Stop all running containers before restart.
@@ -93,7 +94,3 @@ To remove all images, run the following command.
# mac
docker ps -q | xargs docker stop ; docker system prune -a
```
-
-
-
-
\ No newline at end of file
diff --git a/docs/thermometer-skills.md b/docs/thermometer-skills.md
index 679eaaac19..8dac120a67 100644
--- a/docs/thermometer-skills.md
+++ b/docs/thermometer-skills.md
@@ -1,7 +1,9 @@
-The AEA thermometer skills demonstrate an interaction between two AEAs, one purchasing temperature data from the other.
+# Thermometer Skills
-* The provider of thermometer data (the `thermometer`).
-* The buyer of thermometer data (the `thermometer_client`).
+The AEA thermometer skills demonstrate an interaction between two AEAs, one purchasing temperature data from the other.
+
+- The provider of thermometer data (the `thermometer`).
+- The buyer of thermometer data (the `thermometer_client`).
## Discussion
@@ -9,9 +11,9 @@ This demo aims to demonstrate how to create a very simple AEA with the usage of
## Communication
-This diagram shows the communication between the various entities as data is successfully sold by the thermometer AEA to the client AEA.
+This diagram shows the communication between the various entities as data is successfully sold by the thermometer AEA to the client AEA.
-
+``` mermaid
sequenceDiagram
participant Search
participant Client_AEA
@@ -39,19 +41,17 @@ This diagram shows the communication between the various entities as data is suc
deactivate Search
deactivate Thermometer_AEA
deactivate Blockchain
-
-
-
+```
-## Option 1: AEA Manager approach
+## Option 1: AEA Manager Approach
-Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.
+Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.
-### Preparation instructions
+### Preparation Instructions
Install the AEA Manager.
-### Demo instructions
+### Demo Instructions
The following steps assume you have launched the AEA Manager Desktop app.
@@ -64,144 +64,145 @@ The following steps assume you have launched the AEA Manager Desktop app.
4. Run the `my_thermometer_aea` AEA. Navigate to its logs and copy the multiaddress displayed.
5. Navigate to the settings of the `my_thermometer_client` and under `components > connection >` `fetchai/p2p_libp2p:0.22.0` update as follows (make sure to replace the placeholder with the multiaddress):
-``` bash
-{
- "delegate_uri": "127.0.0.1:11001",
- "entry_peers": ["REPLACE_WITH_MULTI_ADDRESS_HERE"],
- "local_uri": "127.0.0.1:9001",
- "log_file": "libp2p_node.log",
- "public_uri": "127.0.0.1:9001"
-}
-```
+
+ ``` bash
+ {
+ "delegate_uri": "127.0.0.1:11001",
+ "entry_peers": ["REPLACE_WITH_MULTI_ADDRESS_HERE"],
+ "local_uri": "127.0.0.1:9001",
+ "log_file": "libp2p_node.log",
+ "public_uri": "127.0.0.1:9001"
+ }
+ ```
6. Run the `my_thermometer_client`.
In the AEA's logs, you should see the agent trading successfully.
-
-## Option 2: CLI approach
+## Option 2: CLI Approach
Follow this approach when using the `aea` CLI.
-### Preparation instructions
-
+### Preparation Instructions
+
#### Dependencies
Follow the Preliminaries and Installation sections from the AEA quick start.
-### Demo instructions
+### Demo Instructions
A demo to run the thermometer scenario with a true ledger transaction This demo assumes the buyer trusts the seller AEA to send the data upon successful payment.
-#### Create thermometer AEA
+#### Create Thermometer AEA
First, fetch the thermometer AEA:
-``` bash
-aea fetch fetchai/thermometer_aea:0.30.4 --alias my_thermometer_aea
-cd my_thermometer_aea
-aea install
-aea build
-```
-
-Alternatively, create from scratch.
-
-
-
-#### Add keys for the thermometer AEA
+??? note "Alternatively, create from scratch:"
+ The following steps create the thermometer client from scratch:
+
+ ``` bash
+ aea create my_thermometer_client
+ cd my_thermometer_client
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/thermometer_client:0.26.5
+ aea install
+ aea build
+ aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
+ }'
+ ```
+
+#### Add Keys for the Thermometer AEA
First, create the private key for the thermometer AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai `Dorado` use:
+
``` bash
aea generate-key fetchai
aea add-key fetchai fetchai_private_key.txt
```
Next, create a private key used to secure the AEA's communications:
+
``` bash
aea generate-key fetchai fetchai_connection_private_key.txt
aea add-key fetchai fetchai_connection_private_key.txt --connection
```
Finally, certify the key for use by the connections that request that:
+
``` bash
aea issue-certificates
```
-#### Add keys and generate wealth for the thermometer client AEA
+#### Add Keys and Generate Wealth for the Thermometer Client AEA
The thermometer client needs to have some wealth to purchase the thermometer information.
First, create the private key for the thermometer client AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai use:
+
``` bash
aea generate-key fetchai
aea add-key fetchai fetchai_private_key.txt
```
Then, create some wealth for your thermometer client based on the network you want to transact with. On the Fetch.ai `testnet` network:
+
``` bash
aea generate-wealth fetchai
```
Next, create a private key used to secure the AEA's communications:
+
``` bash
aea generate-key fetchai fetchai_connection_private_key.txt
aea add-key fetchai fetchai_connection_private_key.txt --connection
```
Finally, certify the key for use by the connections that request that:
+
``` bash
aea issue-certificates
```
@@ -219,6 +220,7 @@ aea run
Once you see a message of the form `To join its network use multiaddr 'SOME_ADDRESS'` take note of the address. (Alternatively, use `aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.4 -u public_uri` to retrieve the address.) This is the entry peer address for the local agent communication network created by the thermometer AEA.
Then, in the thermometer client, run this command (replace `SOME_ADDRESS` with the correct value as described above):
+
``` bash
aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
'{
@@ -229,9 +231,11 @@ aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \
"public_uri": "127.0.0.1:9001"
}'
```
+
This allows the thermometer client to connect to the same local agent communication network as the thermometer AEA.
Then run the thermometer client AEA:
+
``` bash
aea run
```
@@ -247,5 +251,3 @@ cd ..
aea delete my_thermometer_aea
aea delete my_thermometer_client
```
-
-
\ No newline at end of file
diff --git a/docs/trust.md b/docs/trust.md
index b7d20f5d92..1bccb3428a 100644
--- a/docs/trust.md
+++ b/docs/trust.md
@@ -1,11 +1,11 @@
+# Trust Minimisation
+
AEA applications have different requirements for _trustlessness_ or _trust minimisation_.
For example, using the AEA weather skills demo _without_ ledger payments means that the client has to trust the weather station to send the weather data it purchased and that this data is in fact valid. Similarly, the weather station must trust that the client somehow sends the payment amount to which they agreed.
-A step up, if you run the weather skills demo with a ledger (e.g. Fetch.ai or Ethereum) then the client must still trust that the weather station sends valid data. However, all payment transactions are executed via the public ledger. This means the weather station no longer needs to trust the client for payment and can verify whether the transactions take place on the public ledger.
+A step-up, if you run the weather skills demo with a ledger (e.g. Fetch.ai or Ethereum) then the client must still trust that the weather station sends valid data. However, all payment transactions are executed via the public ledger. This means the weather station no longer needs to trust the client for payment and can verify whether the transactions take place on the public ledger.
We can further minimise trust requirements by incorporating a third party as an arbitrator or escrow implemented in a smart contract to further reduce trust requirements. However, in the current weather skills demo, there are limits to trustlessness as the station ultimately offers unverifiable data.
Another example of minimising trust, is applications with (non-fungible) token transactions involving atomic swaps where trustlessness is clearly satisfied (e.g. in the TAC demo).
-
-
diff --git a/docs/upgrading.md b/docs/upgrading.md
index 78fc5f6940..7fc1ae037f 100644
--- a/docs/upgrading.md
+++ b/docs/upgrading.md
@@ -1,3 +1,5 @@
+# Upgrading
+
This page provides some tips on how to upgrade AEA projects between different versions of the AEA framework. For full release notes check the AEA repo.
The primary tool for upgrading AEA projects is the `aea upgrade` command in the CLI.
@@ -20,7 +22,6 @@ Update the packages to the latest versions (especially protocols).
Regenerate your own written protocols (protocol generator was updated)
-
## `v1.1.1` to `v1.2.0`
Ensure you update the plugins to their latest version (fetchai and cosmos plugins are changed in this release)
@@ -89,7 +90,7 @@ Take special care when upgrading to `v0.11.0`. We introduced several breaking ch
We removed the CLI GUI. It was not used by anyone as far as we know and needs to be significantly improved. Soon we will release the AEA Manager App to make up for this.
-### Message routing
+### Message Routing
Routing has been completely revised and simplified. The new message routing logic is described here.
@@ -99,7 +100,7 @@ When upgrading take the following steps:
- For component-to-component communication: there is now only one single way to route component to component (skill to skill, skill to connection, connection to skill) messages, this is by specifying the component id in string form in the `sender`/`to` field. The `EnvelopeContext` can no longer be used, messages are routed based on their target (`to` field). Ensure that dialogues in skills set the `skill_id` as the `self_address` (in connections they need to set the `connection_id`).
-### Agent configuration and ledger plugins
+### Agent Configuration and Ledger Plugins
Agent configuration files have a new optional field, `dependencies`, analogous to `dependencies` field in other AEA packages. The default value is the empty object `{}`. The field will be made mandatory in the next release.
@@ -110,7 +111,8 @@ Crypto modules have been extracted and released as independent plug-ins, release
- Cosmos crypto classes have been released in the `aea-ledger-cosmos` package.
If an AEA project, or an AEA package, makes use of crypto functionalities, it will be needed to add the above packages as PyPI dependencies with version specifiers ranging from the latest minor and the latest minor + 1 (excluded). E.g. if the latest version if `0.1.0`, the version specifier should be `<0.2.0,>=0.1.0`:
-```yaml
+
+``` yaml
dependencies:
aea-ledger-cosmos:
version: <2.0.0,>=1.0.0
@@ -119,6 +121,7 @@ dependencies:
aea-ledger-fetchai:
version: <2.0.0,>=1.0.0
```
+
The version specifier sets are important, as these plug-ins, at version `0.1.0`, depend on a specific range of the `aea` package.
Then, running `aea install` inside the AEA project should install them in the current Python environment.
@@ -131,7 +134,7 @@ No backwards incompatible changes for skill and connection development.
## `v0.9.2` to `v0.10.0`
-Skill development sees no backward incompatible changes.
+Skill development sees no backward incompatible changes.
Connection development requires updating the keyword arguments of the constructor: the new `data_dir` argument must be defined.
@@ -208,11 +211,11 @@ from aea.configurations.base import PublicId
PUBLIC_ID = PublicId.from_str("author/name:0.1.0")
```
+
- The `fetchai/http` protocol's `bodyy` field has been renamed to `body`.
- Skills can now specify `connections` as dependencies in the configuration YAML.
-
## `v0.6.2` to `v0.6.3`
A new `upgrade` command is introduced to upgrade agent projects and components to their latest versions on the registry. To use the command first upgrade the AEA PyPI package to the latest version, then enter your project and run `aea upgrade`. The project's vendor dependencies will be updated where possible.
@@ -227,11 +230,12 @@ The `soef` connection and `oef_search` protocol have backward incompatible chang
## `v0.5.4` to `v0.6.0`
-### `Dialogue` and `Dialogues` API updates
+### `Dialogue` and `Dialogues` API Updates
The dialogue and dialogues APIs have changed significantly. The constructor is different for both classes and there are now four primary methods for the developer:
- `Dialogues.create`: this method is used to create a new dialogue and message:
+
``` python
cfp_msg, fipa_dialogue = fipa_dialogues.create(
counterparty=opponent_address,
@@ -239,26 +243,32 @@ cfp_msg, fipa_dialogue = fipa_dialogues.create(
query=query,
)
```
+
The method will raise if the provided arguments are inconsistent.
- `Dialogues.create_with_message`: this method is used to create a new dialogue from a message:
+
``` python
fipa_dialogue = fipa_dialogues.create_with_message(
counterparty=opponent_address,
initial_message=cfp_msg
)
```
+
The method will raise if the provided arguments are inconsistent.
- `Dialogues.update`: this method is used to handle messages passed by the framework:
+
``` python
fipa_dialogue = fipa_dialogues.update(
message=cfp_msg
)
```
+
The method will return a valid dialogue if it is a valid message, otherwise it will return `None`.
- `Dialogue.reply`: this method is used to reply within a dialogue:
+
``` python
proposal_msg = fipa_dialogue.reply(
performative=FipaMessage.Performative.PROPOSE,
@@ -266,23 +276,24 @@ proposal_msg = fipa_dialogue.reply(
proposal=proposal,
)
```
+
The method will raise if the provided arguments are inconsistent.
The new methods significantly reduce the lines of code needed to maintain a dialogue. They also make it easier for the developer to construct valid dialogues and messages.
-### `FetchAICrypto` - default crypto
+### `FetchAICrypto` - Default Crypto
The `FetchAICrypto` has been upgraded to the default crypto. Update your `default_ledger` to `fetchai`.
-### Private key file naming
+### Private Key File Naming
The private key files are now consistently named with the `ledger_id` followed by `_private_key.txt` (e.g. `fetchai_private_key.txt`). Rename your existing files to match this pattern.
-### Type in package YAML
+### Type in Package YAML
The package YAML files now contain a type field. This must be added for the loading mechanism to work properly.
-### Moved address type
+### Moved Address Type
The address type has moved to `aea.common`. The import paths must be updated.
@@ -321,65 +332,63 @@ Connections are now added via
-
+- Message sending in the skills has been updated. In the past you had to construct messages, then serialize them and place them in an envelope:
+
+ ``` python
+ cfp_msg = FipaMessage(...)
+ self.context.outbox.put_message(
+ to=opponent_addr,
+ sender=self.context.agent_address,
+ protocol_id=FipaMessage.protocol_id,
+ message=FipaSerializer().encode(cfp_msg),
+ )
+ # or
+ cfp_msg = FipaMessage(...)
+ envelope = Envelope(
+ to=opponent_addr,
+ sender=self.context.agent_address,
+ protocol_id=FipaMessage.protocol_id,
+ message=FipaSerializer().encode(cfp_msg),
+ )
+ self.context.outbox.put(envelope)
+ ```
+
+ Now this has been simplified to:
+
+ ``` python
+ cfp_msg = FipaMessage(...)
+ cfp_msg.counterparty = opponent_addr
+ self.context.outbox.put_message(message=cfp_msg)
+ ```
+
+ You must update your skills as the old implementation is no longer supported.
+
+- Connection constructors have been simplified. In the past you had to implement both the `__init__` as well as the `from_config` methods of a Connection. Now you only have to implement the `__init__` method which by default at load time now receives the following keyword arguments: `configuration: ConnectionConfig, identity: Identity, crypto_store: CryptoStore`. See for example in the scaffold connection:
+
+ ``` python
+ class MyScaffoldConnection(Connection):
+ """Proxy to the functionality of the SDK or API."""
+
+ connection_id = PublicId.from_str("fetchai/scaffold:0.1.0")
+
+ def __init__(
+ self,
+ configuration: ConnectionConfig,
+ identity: Identity,
+ crypto_store: CryptoStore,
+ ):
+ """
+ Initialize a connection to an SDK or API.
+
+ :param configuration: the connection configuration.
+ :param crypto_store: object to access the connection crypto objects.
+ :param identity: the identity object.
+ """
+ super().__init__(
+ configuration=configuration, crypto_store=crypto_store, identity=identity
+ )
+ ```
+
+ As a result of this feature, you are now able to pass key-pairs to your connections via the `CryptoStore`.
+
+ You must update your connections as the old implementation is no longer supported.
diff --git a/docs/version.md b/docs/version.md
index e41a3bd1ec..a44a6123ec 100644
--- a/docs/version.md
+++ b/docs/version.md
@@ -1,6 +1,8 @@
+# Version
+
The latest version of the Python implementation of the AEA Framework:
-.
+.
The framework is under rapid development with frequent breaking changes in the run-up to version `1.0` which is due in Q1 2021.
diff --git a/docs/vision.md b/docs/vision.md
index 11cce3e5a4..a7384e2c64 100644
--- a/docs/vision.md
+++ b/docs/vision.md
@@ -1,19 +1,19 @@
+# Vision
+
Our vision is that the AEA framework enables businesses of all sizes, from single independent developers to large corporations and consortiums, to create and deploy agent-based solutions in different domains, thus contributing to and advancing a decentralized agent economy as envisaged by Fetch.ai.
-## Open source technology for everyone
+## Open Source Technology for Everyone
-We are creating infrastructure for developers to build their own agent-based solutions.
+We are creating infrastructure for developers to build their own agent-based solutions.
AEA users include, amongst others:
-* Data scientists
-* Economists
-* Researchers (Artificial Intelligence, Machine Learning, Multi-Agent Systems)
-* Engineers
-* Machine learning experts
-* Independent developers
-* Students and academics
-* Crypto connoisseurs and enthusiasts
-* Web developers
-
-
\ No newline at end of file
+- Data scientists
+- Economists
+- Researchers (Artificial Intelligence, Machine Learning, Multi-Agent Systems)
+- Engineers
+- Machine learning experts
+- Independent developers
+- Students and academics
+- Crypto connoisseurs and enthusiasts
+- Web developers
diff --git a/docs/wealth.md b/docs/wealth.md
index 3ee782d841..6c009f073b 100644
--- a/docs/wealth.md
+++ b/docs/wealth.md
@@ -1,41 +1,47 @@
+# Generating Wealth
To fund an AEA for testing on a test-net you need to request some test tokens from a faucet.
First, make sure you have installed the crypto plugin
of the target test-net. E.g. for Fetch.AI:
+
``` bash
pip install aea-ledger-fetchai
```
And for Ethereum:
+
``` bash
pip install aea-ledger-ethereum
```
Add a private key to the agent
+
``` bash
aea generate-key fetchai
aea add-key fetchai fetchai_private_key.txt
```
+
or
+
``` bash
aea generate-key ethereum
aea add-key ethereum ethereum_private_key.txt
```
-
-
Note
-
If you already have keys in your project, the commands will prompt you for confirmation whether or not to replace the existing keys.
-
-
+!!! note
+ If you already have keys in your project, the commands prompt you to confirm whether to replace the existing keys.
-## Using a faucet website
+## Using a Faucet Website
First, print the address:
+
``` bash
aea get-address fetchai
```
-or
+
+or
+
``` bash
aea get-address ethereum
```
@@ -43,10 +49,13 @@ aea get-address ethereum
This will print the address to the console. Copy the address into the clipboard and request test tokens from the faucet here for Fetch.ai or here for Ethereum. It will take a while for the tokens to become available.
Second, after some time, check the wealth associated with the address:
+
``` bash
aea get-wealth fetchai
```
+
or
+
``` bash
aea get-wealth ethereum
```
@@ -54,18 +63,16 @@ aea get-wealth ethereum
## Using the CLI
Simply generate wealth via the CLI:
+
``` bash
aea generate-wealth fetchai
```
-or
+
+or
+
``` bash
aea generate-wealth ethereum
```
-
-
Note
-
This approach can be unreliable for non-fetchai test nets.
-
-
-
-
+!!! note
+ This approach can be unreliable for non-fetchai test nets.
diff --git a/docs/weather-skills.md b/docs/weather-skills.md
index 1ce4784be7..0192b2db96 100644
--- a/docs/weather-skills.md
+++ b/docs/weather-skills.md
@@ -1,7 +1,9 @@
+# Weather Skills
+
The AEA weather skills demonstrate an interaction between two AEAs.
-* The provider of weather data (the `weather_station`).
-* The buyer of weather data (the `weather_client`).
+- The provider of weather data (the `weather_station`).
+- The buyer of weather data (the `weather_client`).
## Discussion
@@ -13,9 +15,9 @@ You can use this AEA as an example of how to read data from a database and adver
## Communication
-This diagram shows the communication between the various entities as data is successfully sold by the weather station AEA to the client.
+This diagram shows the communication between the various entities as data is successfully sold by the weather station AEA to the client.
-
+``` mermaid
sequenceDiagram
participant Search
participant Client_AEA
@@ -42,20 +44,18 @@ This diagram shows the communication between the various entities as data is suc
deactivate Client_AEA
deactivate Search
deactivate Weather_AEA
- deactivate Blockchain
-
-
-
+ deactivate Blockchain
+```
-## Option 1: AEA Manager approach
+## Option 1: AEA Manager Approach
-Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.
+Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.
-### Preparation instructions
+### Preparation Instructions
Install the AEA Manager.
-### Demo instructions
+### Demo Instructions
The following steps assume you have launched the AEA Manager Desktop app.
@@ -68,155 +68,154 @@ The following steps assume you have launched the AEA Manager Desktop app.
4. Run the `my_weather_station` AEA. Navigate to its logs and copy the multiaddress displayed.
5. Navigate to the settings of the `my_weather_client` and under `components > connection >` `fetchai/p2p_libp2p:0.22.0` update as follows (make sure to replace the placeholder with the multiaddress):
-``` bash
-{
- "delegate_uri": "127.0.0.1:11001",
- "entry_peers": ["REPLACE_WITH_MULTI_ADDRESS_HERE"],
- "local_uri": "127.0.0.1:9001",
- "log_file": "libp2p_node.log",
- "public_uri": "127.0.0.1:9001"
-}
-```
+
+ ``` bash
+ {
+ "delegate_uri": "127.0.0.1:11001",
+ "entry_peers": ["REPLACE_WITH_MULTI_ADDRESS_HERE"],
+ "local_uri": "127.0.0.1:9001",
+ "log_file": "libp2p_node.log",
+ "public_uri": "127.0.0.1:9001"
+ }
+ ```
6. Run the `my_weather_client`.
In the AEA's logs, you should see the agent trading successfully.
-
-## Option 2: CLI approach
+## Option 2: CLI Approach
Follow this approach when using the `aea` CLI.
-### Preparation instructions
+### Preparation Instructions
#### Dependencies
Follow the Preliminaries and Installation sections from the AEA quick start.
-### Demo instructions:
+### Demo Instructions
A demo to run the same scenario but with a true ledger transaction on Fetch.ai `testnet` or Ethereum `ropsten` network. This demo assumes the buyer
trusts the seller AEA to send the data upon successful payment.
-#### Create the weather station
+#### Create the Weather Station
First, fetch the AEA that will provide weather measurements:
-``` bash
-aea fetch fetchai/weather_station:0.32.4 --alias my_weather_station
-cd my_weather_station
-aea install
-aea build
-```
-Alternatively, create from scratch.
-
-
-The following steps create the weather station from scratch:
``` bash
-aea create my_weather_station
+aea fetch fetchai/weather_station:0.32.4 --alias my_weather_station
cd my_weather_station
-aea add connection fetchai/p2p_libp2p:0.27.4
-aea add connection fetchai/soef:0.27.5
-aea add connection fetchai/ledger:0.21.4
-aea add skill fetchai/weather_station:0.27.5
-aea config set --type dict agent.dependencies \
-'{
- "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"}
-}'
-aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
-aea config set --type dict agent.default_routing \
-'{
- "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
- "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
-}'
aea install
aea build
```
-
-
-
-
-#### Create the weather client
+??? note "Alternatively, create from scratch:"
+ The following steps create the weather station from scratch:
+
+ ``` bash
+ aea create my_weather_station
+ cd my_weather_station
+ aea add connection fetchai/p2p_libp2p:0.27.4
+ aea add connection fetchai/soef:0.27.5
+ aea add connection fetchai/ledger:0.21.4
+ aea add skill fetchai/weather_station:0.27.5
+ aea config set --type dict agent.dependencies \
+ '{
+ "aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"}
+ }'
+ aea config set agent.default_connection fetchai/p2p_libp2p:0.27.4
+ aea config set --type dict agent.default_routing \
+ '{
+ "fetchai/ledger_api:1.1.6": "fetchai/ledger:0.21.4",
+ "fetchai/oef_search:1.1.6": "fetchai/soef:0.27.5"
+ }'
+ aea install
+ aea build
+ ```
+
+#### Create the Weather Client
In another terminal, fetch the AEA that will query the weather station:
-``` bash
-aea fetch fetchai/weather_client:0.33.4 --alias my_weather_client
-cd my_weather_client
-aea install
-aea build
-```
-
-Alternatively, create from scratch.
-