Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion auto_dev/behaviours/scaffolder.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
from aea.protocols.generator.base import ProtocolGenerator

from auto_dev.fmt import Formatter
from auto_dev.enums import BehaviourTypes
from auto_dev.utils import currenttz, get_logger, remove_prefix, camel_to_snake, snake_to_camel
from auto_dev.fsm.fsm import FsmSpec
from auto_dev.constants import DEFAULT_TZ, DEFAULT_ENCODING, JINJA_TEMPLATE_FOLDER
from auto_dev.exceptions import UserInputError
from auto_dev.protocols.scaffolder import PROTOBUF_TO_PYTHON, ProtocolScaffolder, read_protocol, parse_protobuf_type
Expand Down Expand Up @@ -129,6 +131,13 @@ def __init__(
self.auto_confirm = auto_confirm
self.env = Environment(loader=FileSystemLoader(JINJA_TEMPLATE_FOLDER), autoescape=False) # noqa

@property
def scaffold(self):
"""Scaffold the protocol."""
return (
self._scaffold_simple_fsm if self.behaviour_type is BehaviourTypes.simple_fsm else self._scaffold_protocol
)

@property
def template(self) -> Any:
"""Get the template."""
Expand All @@ -152,7 +161,37 @@ def _validate_selection(self, target_speech_acts, speech_acts):
)
return target_speech_acts

def scaffold(self, target_speech_acts=None) -> None:
def _scaffold_simple_fsm(
self,
) -> None:
"""Scaffold the simple fsm behaviour from a fsm class."""

fsm_spec = FsmSpec.from_yaml(Path(self.protocol_specification_path).read_text())

all_states = fsm_spec.states
states_not_in_initial_or_final = [
state for state in all_states if state not in fsm_spec.final_states + [fsm_spec.default_start_state]
]

transitions: list = []

for key, destination in fsm_spec.transition_func.items():
source, event = key[1:-1].split(", ")
transitions.append({"source": source, "event": event, "destination": destination})

output = self.template.render(
fsm_spec=fsm_spec,
class_name=snake_to_camel(fsm_spec.label).capitalize(),
states=fsm_spec.states,
default_start_state=fsm_spec.default_start_state,
final_states=fsm_spec.final_states,
events=fsm_spec.alphabet_in,
remaining_states=states_not_in_initial_or_final,
transitions=transitions,
)
print(output)

def _scaffold_protocol(self, target_speech_acts=None) -> None:
"""Scaffold the protocol."""
protocol_specification = read_protocol(self.protocol_specification_path)
raw_classes, all_dummy_data, enums = self._get_definition_of_custom_types(protocol=protocol_specification)
Expand Down Expand Up @@ -248,6 +287,8 @@ def get_py_type_and_args(arg, arg_type, type_map):
if py_type not in DEFAULT_TYPE_MAP:
if py_type.startswith("Optional"):
DEFAULT_TYPE_MAP[py_type] = None
elif py_type.startswith("Dict"):
DEFAULT_TYPE_MAP[py_type] = {}
else:
raise ValueError(f"Type {py_type} not found in the default type map.")

Expand Down
4 changes: 4 additions & 0 deletions auto_dev/commands/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,10 @@ def render_metadata(metadata, verbose=False) -> bool:
if component_status == "NOT MINTED":
click.echo(f"\n{component_id} is not minted. Please mint it first.")
return False

image_hash = metadata["image"][7:]
click.echo(f"View image at: https://gateway.autonolas.tech/ipfs/{image_hash}")
click.echo(f"View code at: https://gateway.autonolas.tech/ipfs/{metadata['code_uri'][7:]}")
click.echo("\nAll dependencies are minted. You can mint this component now.")

deps_ids_numeric = sorted(map(int, mint_status.keys()))
Expand Down
18 changes: 14 additions & 4 deletions auto_dev/commands/scaffold.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,13 +213,19 @@ def handler(ctx, spec_file, public_id, new_skill, auto_confirm) -> int:
@click.option("--auto-confirm", is_flag=True, default=False, help="Auto confirm all actions")
@click.option(
"--behaviour-type",
type=click.Choice([BehaviourTypes.metrics]),
type=click.Choice([f.value for f in (BehaviourTypes.metrics, BehaviourTypes.simple_fsm)]),
required=True,
help="The type of behaviour to generate.",
default=BehaviourTypes.metrics,
)
@click.pass_context
def behaviour(ctx, spec_file, behaviour_type, auto_confirm, target_speech_acts) -> None:
def behaviour(
ctx,
spec_file,
behaviour_type,
auto_confirm,
target_speech_acts,
) -> None:
"""
Generate an AEA handler from an OpenAPI 3 specification.

Expand All @@ -233,7 +239,11 @@ def behaviour(ctx, spec_file, behaviour_type, auto_confirm, target_speech_acts)
verbose = ctx.obj["VERBOSE"]

scaffolder = BehaviourScaffolder(
spec_file, behaviour_type=behaviour_type, logger=logger, verbose=verbose, auto_confirm=auto_confirm
spec_file,
behaviour_type=BehaviourTypes[behaviour_type],
logger=logger,
verbose=verbose,
auto_confirm=auto_confirm,
)
scaffolder.scaffold(
target_speech_acts=target_speech_acts,
Expand Down Expand Up @@ -341,4 +351,4 @@ def dao(ctx, auto_confirm) -> None:


if __name__ == "__main__":
cli() # pylint: disable=no-value-for-parameter
cli() # pylint: disable=no-value-for-parameter
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ classifiers = [
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
]
packages = [{{ include = "packages", from = "" }},]

[tool.poetry.dependencies]
python = ">=3.9,<3.12"
Expand All @@ -28,7 +29,6 @@ open-aea-test-autonomy = "==0.16.1"
open-autonomy = "==0.16.1"
autonomy-dev = {{extras = ["all"], version = ">=0.2.64,<=0.2.78"}}


[tool.poetry.dev-dependencies]


Expand Down
94 changes: 94 additions & 0 deletions auto_dev/data/templates/behaviours/simple_fsm.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
#
# Copyright 2023 {{author}}
# Copyright 2023 valory-xyz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ------------------------------------------------------------------------------

"""This package contains a behaviour that autogenerated from the protocol `{{protocol_name}}`."""

import sys
from abc import ABC
from typing import Optional, Any
from aea.skills.behaviours import FSMBehaviour, State
from enum import Enum

# Define states
{% for state in states %}
class {{ state }}(State):
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
self._is_done = False # Initially, the state is not done

def act(self) -> None:
print("{{ state }}: Performing action")
self._is_done = True
self._event = {{class_name}}Events.DONE

def is_done(self) -> bool:
return self._is_done

@property
def event(self) -> Optional[str]:
return self._event

{% endfor %}


class {{class_name}}Events(Enum):
{%for event in events %}
{{event}} = '{{event}}'{% endfor %}


class {{class_name}}FsmBehaviour(FSMBehaviour):
"""This class implements a simple Finite State Machine behaviour."""

def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
self.register_state('{{default_start_state.lower()}}', {{default_start_state}}(**kwargs), True)
{% for state in final_states%}
self.register_state('{{state.lower()}}', {{state}}(**kwargs)){% endfor %}
{% if remaining_states %}
{% for state in remaining_states%}
self.register_state('{{state.lower()}}', {{state}}(**kwargs)) {% endfor %}{% endif %}
{% for transition in transitions%}
self.register_transition(
source='{{transition.source.lower()}}',
event={{class_name}}Events.{{transition.event}},
destination='{{transition.destination.lower()}}'
){% endfor %}


def setup(self) -> None:
"""Implement the setup."""
self.context.logger.info("Setting up {{class_name}} FSM behaviour.")


def teardown(self) -> None:
"""Implement the teardown."""
self.context.logger.info("Tearing down {{class_name}} FSM behaviour.")

def act(self) -> None:
"""Implement the act."""
super().act()
if self.current is None:
self.context.logger.info("No state to act on.")
self.terminate()

def terminate(self) -> None:
"""Implement the termination."""
print("Terminating the agent.")
sys.exit(0)
1 change: 1 addition & 0 deletions auto_dev/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,4 @@ class BehaviourTypes(Enum):
"""Behaviour types enum."""

metrics = "metrics"
simple_fsm = "simple_fsm"
Loading