Description
The pyasyncagent
package provides a python wrapper for asynchronous agents as part of TheWorldAvatar project. It is a python equivalent of uk.ac.cam.cares.jps.base.agent.AsynAgent.java
but based on Flask application behind a gunicorn server, inspired by the Example: Python agent. pyasyncagent
uses py4jps>=1.0.15
to access asynchronous derivation operations provided in uk.ac.cam.cares.jps.base.derivation.DerivationClient.java
. For technical details, below are a few useful links:
Installation
For development and testing reasons, follow below instructions to get a copy of the project up and running on your local system.
Virtual environment setup
It is highly recommended to install pyasyncagent
packages in a virtual environment. The following steps can be taken to build a virtual environment:
(Windows)
$ python -m venv <venv_name>
$ <venv_name>\Scripts\activate.bat
(<venv_name>) $
(Linux)
$ python3 -m venv <venv_name>
$ source <venv_name>/bin/activate
(<venv_name>) $
The above commands will create and activate the virtual environment <venv_name>
in the current directory.
Installation via pip
The following command can be used to install the pyasyncagent
package and agentlogging
package. This is a workaround as PyPI does NOT allow install_requires direct links, so we could NOT add package agentlogging from 'agentlogging @ git+https://github.com/cambridge-cares/TheWorldAvatar@develop#subdirectory=Agents/utils/python-utils' as dependency, therefore, in order to make semi-automated release process working, we here introduce a workaround to install agentlogging to the virtual environment but NOT as dependency in the setup.py of pyasyncagent
. A long term solution could be that we publish agentlogging
in PyPI as well.
(<venv_name>) $ pip install pyasyncagent
(<venv_name>) $ pip install "git+https://github.com/cambridge-cares/TheWorldAvatar@develop#subdirectory=Agents/utils/python-utils"
How to use
Develop asynchronous derivation agent
When creating a new asynchronous derivation python agent, it's strongly advised to stick to the folder structure below:
Recommended python asynchronous agent folder layout
.
├── ... # other project files (README, LICENSE, etc..)
├── src # project source files
│ ├── agent # module that contains the agent logic
│ │ ├── __init__.py
│ │ └── your_agent.py # your asynchronous agent
│ ├── conf # module that specifies the configuration
│ │ ├── __init__.py
│ │ ├── agent_props.json # json file that specifies the agent properties
│ │ └── agent_props.py # read the property json
│ ├── data_model # module that contains the dataclasses for concepts
│ │ ├── __init__.py
│ │ └── your_onto.py # dataclasses for your ontology concepts
│ ├── kg_operations # module that handles knowledge graph operations
│ │ ├── __init__.py
│ │ └── your_sparql.py # sparql query and update strings for your agent
│ ├── other_modules # other necessary modules for your agent
│ │ ├── __init__.py
│ │ ├── module1.py
│ │ └── module2.py
│ └── entry_point.py # agent entry point
└── tests # tests files
Example code
your_agent.py
from pyasyncagent import AsyncAgent
from kg_operations import YourSparqlClient
from data_model import YOUR_CONCEPT
class YourAgent(AsyncAgent):
def setupJob(self, agentInputs) -> list:
self.sparql_client = YourSparqlClient(
self.kgUrl, self.kgUrl, self.kgUser, self.kgPassword
)
response = self.sparql_client.your_sparql_query(agentInputs[YOUR_CONCEPT])
self.logger.info("YourAgent has done something.")
return []
agent_props.json
{
"KNOWLEDGE_GRAPH": {
"SPARQL_QUERY_ENDPOINT": "http://www.example.com/blazegraph/namespace/your_repo/sparql",
"SPARQL_UPDATE_ENDPOINT": "http://www.example.com/blazegraph/namespace/your_repo/sparql",
"KG_USERNAME": null,
"KG_PASSWORD": null
},
"ONTOAGENT": {
"ONTOAGENT_SERVICE": "http://www.example.com/resource/agents/Service__YourAgent#Service"
},
"DERIVATION_CLIENT": {
"PERIODIC_TIMESCALE": 120,
"DERIVATION_INSTANCE_BASE_URL": "http://www.example.com/triplestore/your_repo/"
}
}
PLEASE NEVER COMMIT YOUR KG_USERNAME AND KG_PASSWORD TO GITHUB. A BETTER WAY OF ENCODING CREDENTIALS WILL BE PROVIDED IN THE NEXT RELEASE.
agent_props.py
For an example, please checkout: doeagent_properties.py
your_onto.py
from pydantic.dataclasses import dataclass
from typing import Optional
YOUR_CONCEPT = 'https://www.example.com/triplestore/repository/Ontology.owl#YOUR_CONCEPT'
@dataclass
class YourOtherConcept():
instance_iri: str
@dataclass
class YourConcept():
instance_iri: str
example_object_relationship_to: Optional[YourOtherConcept] = None
your_sparql.py
from pyasyncagent import PySparqlClient
from data_model import YourConcept
class YourSparqlClient(PySparqlClient):
def your_sparql_query(self, your_instance_iri: str) -> YourConcept:
query = """SELECT ?target WHERE {# your sparql query logic to get the information}"""
response = self.performQuery(query)
new_target_instance = YourConcept(response[0]['target'])
return new_target_instance
def your_sparql_update(self, your_instance_iri: str):
update = """INSERT DATA {# your sparql update logic to insert the triples}"""
self.performUpdate(update)
entry_point.py
from conf import AgentConfig
from agent import YourAgent
agent_config = AgentConfig(str(Path(__file__).absolute().parent) + '/conf/agent_props.json')
app = YourAgent(agent_config.ONTOAGENT_SERVICE, agent_config.PERIODIC_TIMESCALE, agent_config.DERIVATION_INSTANCE_BASE_URL, agent_config.SPARQL_QUERY_ENDPOINT)
if __name__ == '__main__':
app.run()
You may refer to DoEAgent for a concrete implementation of the above suggested folder structure based on pyasyncagent
. The design of pyasyncagent
is continually evolving, and as the project grows, we hope to make it more accessible to developers and users.
New features and package release
Developers who add new features to the python agent wrapper handle the distribution of package pyasyncagent
on PyPI and Test-PyPI. If you want to add new features that suit your project and release the package independently, i.e. become a developer/maintainer, please contact the repository's administrator to indicate your interest.
The release procedure is currently semi-automated and requires a few items:
- Your Test-PyPI and PyPI account and password
- The version number x.x.x for the release
- Clone of
TheWorldAvatar
repository on your local machine - Docker-desktop is installed and running on your local machine
- You have access to the docker.cmclinnovations.com registry on your local machine, for more information regarding the registry, see: https://github.com/cambridge-cares/TheWorldAvatar/wiki/Docker%3A-Image-registry
Please create and checkout to a new branch from your feature branch once you are happy with the feature and above details are ready. The release process can then be started by using the commands below, depending on the operating system you're using. (REMEMBER TO CHANGE THE CORRECT VALUES IN THE COMMANDS BELOW!)
(Windows)
$ cd \absolute_path_to\TheWorldAvatar\JPS_BASE_LIB\python_async_agent
$ release_pyasyncagent_to_pypi.sh -v x.x.x
(Linux)
$ cd /absolute_path_to/TheWorldAvatar/JPS_BASE_LIB/python_async_agent
$ ./release_pyasyncagent_to_pypi.sh -v x.x.x
Please follow the instructions presented in the console once the process has begun. If everything goes well, change the version number in JPS_BASE_LIB/python_async_agent/release_pyasyncagent_to_pypi.sh
to the one you used for the script release.
echo "./release_pyasyncagent_to_pypi.sh -v 0.0.3 - release version 0.0.3"
The changes mentioned above should then be committed with the changes performed automatically during the release process, specifically in python script JPS_BASE_LIB/python_async_agent/pyasyncagent/__init__.py
__version__ = "0.0.3"
and JPS_BASE_LIB/python_async_agent/setup.py
version='0.0.3',
Finally, merge the release branch back to the feature branch and make a Pull Request for the feature branch to be merged back into the develop
branch.
Authors
Jiaru Bai (jb2197@cam.ac.uk)