Guidelines

To create an app using NeuralAutomata, we need to follow the hierarchy of app -> page -> states

App

In Python implementation of NeuralAutomata, the app is implemented by AutomataGradio class, since the UI is achieved by gradio. For example:

from neuralautomata.renderers import GradioRenderer

automata = AutomataGradio(
    name="duolingo_demo",
    start_at="login_page",
    event_listeners={
        'login_page:signup_btn.click': "signup_page",
        'signup_page:login_btn.click': 'login_page',
    },
    states={
        "login_page": login_page,
        "signup_page": signup_page,
        "setup_language_page": setup_language_page,
        "learn_page": learn_page,
        "quiz_image_page": quiz_image_page,
        "quiz_explain_page": quiz_explain_page,
        "quiz_translation_page": quiz_translation_page,
        "summary_page": summary_page,
    }
)
GradioRenderer.render(automata)

the GradioRender.renderfunction will launch the gradio app.

Page

We need to define the state transfer inside each page. It is recommended to draw a graph of the states before writing the code. The AutomataPage class is inherited from the NestedState with an extra attribute called layout which is helpful to control how the UI components display in the page. Please refer to here for detailed grammar of the layout.

States

It is crucial to understand how to arrange the states inside a page to make it work properly. As a NestedState, the execution of AutomataPage would start from the state defined by start_at and perform state transfer according to the next_state property of each state, and stop when the next_state of a state is None or the current state name equals the end_at attribute. When the state machine is halted, we can use event listeners to jump to another state and resume the automata. A common case is that when we want the user to input some information (e.g., via Textbox, Dropdown, etc):

page= AutomataPage(
    name="test_page",
    start_at="first_state",
    end_at="end_state",
    next_state="next_page",
    layout=None,
    event_listeners={
        "confirm_btn.click": {
            "goto": {
                "next_state": "input_state",
            },
        }
    },
    states={
        "first_state": TaskState(
            ...,
            next_state=None, # the automata will halt here
        ),
        "input_state": TaskState(
            name="signup_state",
            inputs={
                "username": Textbox(
                    value="",
                    placeholder="username",
                    show_label=False,
                ),
                "password": Textbox(
                    value="",
                    text_type="password",
                    placeholder="password",
                    show_label=False,
                ),
                "confirm_btn": Button(
                    value="Signup",
                    interactive=True,
                ),
            },
            next_state="end_state"
        ),
        "end_state": TaskState(
            ...
        ),
    } 
)

in the example above, the automata will halt after the execution of first_state, and when the user click the confirm_btn it will jump to input_state and resume the automata.

It is a common usage that a page will stop at the very beginning to wait for user inputs. We then introduce a new attribute for NestedState called use_idle_state. When use_idle_state=True, an idle state (the next_state of which is None) is created and the nested state will start from it and then halt to wait for user inputs.

Context

The context in NeuralAutomata means the existing variables in the current scope. Context (or ctx in code) is defined in each NestedState. At the initialization of Automata, it will recursively set a prefix for each NestedState to identify the namespace. Manipulation of the variables inside a NestedState will not affect the variables with the same names in another NestedState by default. When using recursive NestedState, it is common to pass some variables from the parent ctx to the children ctx and obtain the processed results of the variables from the children ctx, which can be achieved by specifying the inputs and outputs of the children NestedState, please see here for details.

Other tricks

Setting the Next State Dynamically

Sometimes we need to dynamically determine the next state name. This can be achieved by modifying the value of NEXT_STATE_NAME

from neuralautomata.core.state import NEXT_STATE_NAME

If NEXT_STATE_NAME is set properly, the get_next_state method of NestedStatewill return that instead of self.next_state

Last updated