Skip to content

Commit

Permalink
feat: support experiments & (breaking) interface change
Browse files Browse the repository at this point in the history
Current intended use is through namespaces rather than func names. Each entity has their own name space, e.g. agents or environments, which have limited scope functions.

Experiments support means allowing agents interact with environments on the same data plane.

A few more crazy minor changes and we're aiming for major release.
  • Loading branch information
laszukdawid committed Jul 11, 2021
1 parent 83af885 commit e32457b
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 123 deletions.
2 changes: 1 addition & 1 deletion agents_bar/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
from agents_bar.remote_agent import *
from agents_bar.utils import *

__version__ = '0.3.0'
__version__ = '0.4.0'
__author__ = "Dawid Laszuk"

77 changes: 49 additions & 28 deletions agents_bar/agents.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from agents_bar.utils import response_raise_error_if_any
from typing import Dict, List, Optional

from agents_bar.client import Client

AGENTS_PREFIX = "/agents"

def get_agents(client: Client) -> List[Dict]:

def get_many(client: Client) -> List[Dict]:
"""Gets agents belonging to authenticated user.
Parameters:
Expand All @@ -13,12 +16,11 @@ def get_agents(client: Client) -> List[Dict]:
List of agents.
"""
response = client.get('/agents/')
if not response.ok:
response.raise_for_status()
response = client.get(f'{AGENTS_PREFIX}/')
response_raise_error_if_any(response)
return response.json()

def get_agent(client: Client, agent_name: str) -> Dict:
def get(client: Client, agent_name: str) -> Dict:
"""Get indepth information about a specific agent.
Parameters:
Expand All @@ -29,12 +31,11 @@ def get_agent(client: Client, agent_name: str) -> Dict:
Details of an agent.
"""
response = client.get(f'/agents/{agent_name}')
if not response.ok:
response.raise_for_status()
response = client.get(f'{AGENTS_PREFIX}/{agent_name}')
response_raise_error_if_any(response)
return response.json()

def create_agent(client: Client, config: Dict) -> Dict:
def create(client: Client, config: Dict) -> Dict:
"""Creates an agent with specified configuration.
Parameters:
Expand All @@ -45,12 +46,11 @@ def create_agent(client: Client, config: Dict) -> Dict:
Details of an agent.
"""
response = client.post('/agents/', data=config)
if not response.ok:
response.raise_for_status()
response = client.post(f'{AGENTS_PREFIX}/', data=config)
response_raise_error_if_any(response)
return response.json()

def delete_agent(client: Client, agent_name: str) -> bool:
def delete(client: Client, agent_name: str) -> bool:
"""Deletes specified agent.
Parameters:
Expand All @@ -61,13 +61,12 @@ def delete_agent(client: Client, agent_name: str) -> bool:
Whether agent was delete. True if an agent was delete, False if there was no such agent.
"""
response = client.delete('/agents/' + agent_name)
if not response.ok:
response.raise_for_status()
response = client.delete(f'{AGENTS_PREFIX}/' + agent_name)
response_raise_error_if_any(response)
return response.status_code == 202


def get_agent_loss(client: Client, agent_name: str) -> Dict:
def get_loss(client: Client, agent_name: str) -> Dict:
"""Recent loss metrics.
Parameters:
Expand All @@ -78,19 +77,41 @@ def get_agent_loss(client: Client, agent_name: str) -> Dict:
Loss metrics in a dictionary.
"""
response = client.get(f'/agents/{agent_name}/loss')
if not response.ok:
response.raise_for_status()
response = client.get(f'{AGENTS_PREFIX}/{agent_name}/loss')
response_raise_error_if_any(response)
return response.json()

def agent_step(client, agent_name: str, step: Dict) -> None:
response = client.post(f"/agents/{agent_name}/step", step)
if not response.ok:
response.raise_for_status()
def step(client, agent_name: str, step: Dict) -> None:
"""Steps forward in agents learning mechanism.
This method is used to provide learning data, like recent observations and rewards,
and trigger learning mechanism. *Note* that majority of agents perform `step` after
every environment iteration (`step`).
Parameters:
client (Client): Authenticated client.
agent_name (str): Name of agent.
step (Dict): Data required for the agent to use in learning.
Likely that's `observation`, `next_observation`, `reward`, `action` values and `done` flag.
"""
response = client.post(f"{AGENTS_PREFIX}/{agent_name}/step", step)
response_raise_error_if_any(response)
return

def agent_act(client, agent_name: str, obs: Dict, params: Optional[Dict] = None) -> Dict:
response = client.post(f"/agents/{agent_name}/act", obs, params=params)
if not response.ok:
response.raise_for_status()
def act(client, agent_name: str, obs: Dict, params: Optional[Dict] = None) -> Dict:
"""Asks agent about its action on provided observation.
Parameters:
client (Client): Authenticated client.
agent_name (str): Name of agent.
obs (Dict): Observation from current environment state.
params (Optional dict): Anything useful for the agent to learn, e.g. epsilon greedy value.
Returns:
Dictionary container actions.
"""
response = client.post(f"{AGENTS_PREFIX}/{agent_name}/act", obs, params=params)
response_raise_error_if_any(response)
return response.json()
69 changes: 45 additions & 24 deletions agents_bar/environments.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from typing import Any, Dict, List

from agents_bar.client import Client
from agents_bar.utils import response_raise_error_if_any

ENV_PREFIX = "/environments"

def environment_get_all(client: Client) -> List[Dict]:

def get_many(client: Client) -> List[Dict]:
"""Gets environments belonging to authenticated user.
Parameters:
Expand All @@ -13,12 +16,10 @@ def environment_get_all(client: Client) -> List[Dict]:
List of environments.
"""
response = client.get('/env/')
if not response.ok:
response.raise_for_status()
response = client.get(f"{ENV_PREFIX}/")
return response.json()

def environment_get(client: Client, env_name: str) -> Dict:
def get(client: Client, env_name: str) -> Dict:
"""Get indepth information about a specific environment.
Parameters:
Expand All @@ -29,12 +30,11 @@ def environment_get(client: Client, env_name: str) -> Dict:
Details of an environment.
"""
response = client.get(f'/env/{env_name}')
if not response.ok:
response.raise_for_status()
response = client.get(f'{ENV_PREFIX}/{env_name}')
response_raise_error_if_any(response)
return response.json()

def environment_create(client: Client, config: Dict) -> Dict:
def create(client: Client, config: Dict) -> Dict:
"""Creates an environment with specified configuration.
Parameters:
Expand All @@ -45,12 +45,11 @@ def environment_create(client: Client, config: Dict) -> Dict:
Details of an environment.
"""
response = client.post('/env/', data=config)
if not response.ok:
response.raise_for_status()
response = client.post(f'{ENV_PREFIX}/', data=config)
response_raise_error_if_any(response)
return response.json()

def environment_delete(client: Client, env_name: str) -> bool:
def delete(client: Client, env_name: str) -> bool:
"""Deletes specified environment.
Parameters:
Expand All @@ -61,12 +60,11 @@ def environment_delete(client: Client, env_name: str) -> bool:
Whether environment was delete. True if an environment was delete, False if there was no such environment.
"""
response = client.delete('/env/' + env_name)
if not response.ok:
response.raise_for_status()
response = client.delete(f'{ENV_PREFIX}/{env_name}')
response_raise_error_if_any(response)
return response.status_code == 202

def environment_reset(client: Client, env_name: str) -> List[float]:
def reset(client: Client, env_name: str) -> List[float]:
"""Resets the environment to starting position.
Parameters:
Expand All @@ -77,12 +75,12 @@ def environment_reset(client: Client, env_name: str) -> List[float]:
Environment state in the starting position.
"""
response = client.post(f"/env/{env_name}/reset")
if not response.ok:
response.raise_for_status()
response = client.post(f"{ENV_PREFIX}/{env_name}/reset")
response_raise_error_if_any(response)
return response.json()

def environment_step(client: Client, env_name: str, step) -> Dict[str, Any]:

def step(client: Client, env_name: str, step) -> Dict[str, Any]:
"""Steps the environment based on provided data.
Parameters:
Expand All @@ -94,7 +92,30 @@ def environment_step(client: Client, env_name: str, step) -> Dict[str, Any]:
Environment state after taking provided actions. Consists of "observation", "reward", "done" and "info".
"""
response = client.post(f"/env/{env_name}/step", data=step)
if not response.ok:
response.raise_for_status()
response = client.post(f"{ENV_PREFIX}/{env_name}/step", data=step)
response_raise_error_if_any(response)
return response.json()


def commit(client: Client, env_name: str) -> Dict[str, Any]:
"""Commits last provided data. Must be proceeded by environment `step`.
Useful when environment requires many agents or when agent is allowed to make mistakes.
Parameters:
client (Client): Authenticated client.
env_name (str): Name of the environment
Returns:
Data about the state the environment has transtioned into.
This should be the same as when using `step` with `commit=True`.
"""
response = client.post(f"{ENV_PREFIX}/{env_name}/commit")
response_raise_error_if_any(response)
return response.json()

def info(client: Client, env_name: str) -> Dict[str, Any]:
response = client.get(f"{ENV_PREFIX}/{env_name}/info")
response_raise_error_if_any(response)
return response.json()
102 changes: 102 additions & 0 deletions agents_bar/experiments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import dataclasses
from typing import Dict, List, Optional

from agents_bar.client import Client
from agents_bar.types import ExperimentCreate
from agents_bar.utils import response_raise_error_if_any

EXP_PREFIX = "/experiments"


def get_many(client: Client) -> List[Dict]:
"""Gets experiments that belong to an authenticated user.
Parameters:
client (Client): Authenticated client.
Returns:
List of experiments.
"""
response = client.get(f"{EXP_PREFIX}/")
return response.json()

def get(client: Client, exp_name: str) -> Dict:
"""Get indepth information about a specific experiment.
Parameters:
client (Client): Authenticated client.
exp_name (str): Name of experiment.
Returns:
Details of an experiment.
"""
response = client.get(f'{EXP_PREFIX}/{exp_name}')
response_raise_error_if_any(response)
return response.json()

def create(client: Client, experiment_create: ExperimentCreate) -> Dict:
"""Creates an experiment with specified configuration.
Parameters:
client (Client): Authenticated client.
config (dict): Configuration of an experiment.
Returns:
Details of an experiment.
"""
response = client.post(f'{EXP_PREFIX}/', data=dataclasses.asdict(experiment_create))
response_raise_error_if_any(response)
return response.json()

def delete(client: Client, exp_name: str) -> bool:
"""Deletes specified experiment.
Parameters:
client (Client): Authenticated client.
exp_name (str): Name of the experiment.
Returns:
Whether experiment was delete. True if an experiment was delete, False if there was no such experiment.
"""
response = client.delete(f'{EXP_PREFIX}/{exp_name}')
response_raise_error_if_any(response)
return response.status_code == 202

def reset(client: Client, exp_name: str) -> str:
"""Resets the experiment to starting position.
Doesn't affect Agent nor Environment. Only resets values related to the Experiment,
like keeping score of last N episodes or managing Epislon value.
Parameters:
client (Client): Authenticated client.
exp_name (str): Name of the experiment.
Returns:
Confirmation on reset experiment.
"""
response = client.post(f"{EXP_PREFIX}/{exp_name}/reset")
response_raise_error_if_any(response)
return response.json()


def start(client: Client, exp_name: str, config: Optional[Dict] = None) -> str:
"""Starts experiment, i.e. communication between selected Agent and Env entities.
Parameters:
client (Client): Authenticated client.
exp_name (str): Name of the experiment.
Returns:
Information about started experiment.
"""
config = config or {}
response = client.post(f"{EXP_PREFIX}/{exp_name}/start", data=config)
response_raise_error_if_any(response)
return response.text
Loading

0 comments on commit e32457b

Please sign in to comment.