This example script is to demonstrate how to launch a simple task using a frontend webapp built in React. The "static" nature of this task means that all of the content required for a worker to complete the task must be set before the task is launched, and must be able to be sent to the app upon initialization. The actual app you write can vary in complexity however you like so long as this constraint is met.
This specific example can be run with:
python run_task__local__inhouse.py
and can additionally be launched with an onboarding step by specifying an onboarding qualification:
python run_task__local__inhouse.py mephisto.blueprint.onboarding_qualification=test-react-static-qualification
or by specifying the config file that already has this set:
python run_task__local__inhouse.py conf=example_with_onboarding__local__inhouse
This task is configured using Hydra - details about using hydra to configure tasks can be read here and in other examples. For more about being able to customize the configuration files, please refer to the Hydra documentation. Under our current setup, using Hydra means that you must be in the same directory as the python script for hydra to correctly pick up the config files.
In this script, we set certain configuration variables by default in every run:
defaults:
- /mephisto/blueprint: static_react_task
- /mephisto/architect: local
- /mephisto/provider: inhouse
These defaults are handed to Hydra in order to ensure that by default, we run a task locally with a mock provider (so we can demo).
We also set conf
to example
, which means this script by default will also load in all of the configuration variables set in conf/example__local__inhouse.yaml
.
If your task has other variables that you think will almost always be set to particular values (say you always expect mephisto.blueprint.units_per_assignment
to be 1
)
that differ from Mephisto's default for those values (if such a default exists),
you can include them by default by adding a config file with your defaults to conf
and adding the string path to it here in the list, like conf/base
.
See the ParlAI Chat demo for an example of this.
You can create override configuration files, such as example__local__inhouse.yaml
vs example_with_onboarding__local__inhouse.yaml
in the conf
directory.
Having these files makes it really easy to set multiple values at once. You can only select one such configuration file per run, using the conf=example
argument.
You can also override configuration variables on the command line. Say you want to launch your server on port 4000 rather than 3000, you can use:
python run_task__local__inhouse.py mephisto.architect.port=4000
To be able to launch with a HerokuArchitect
rather than the default LocalArchitect
, you can use:
python run_task__local__inhouse.py mephisto/architect=heroku
In this case, the provided app demonstrates being able to send task data forward to the frontend. See the run_task__local__inhouse.py
script. Here we have:
shared_state = SharedStaticTaskState(
static_task_data=[
{"text": "This text is good text!"},
{"text": "This text is bad text!"},
],
validate_onboarding=onboarding_always_valid,
)
This block of code is preparing two tasks, each with a "text"
field set. When the task run is launched with operator.launch_task_run(cfg.mephisto, shared_state)
,
this creates two tasks for workers to work on, one for each entry in the static_task_data
array.
This data is later pulled via the useMephistoTask
hook, and when a worker accepts a task, they will be given the contents of one of the entries as their initialTaskData
.
See the webapp/src/app.jsx
file. We render
<BaseFrontend
taskData={initialTaskData}
onSubmit={handleSubmit}
isOnboarding={isOnboarding}
/>
which if you look at the webapp/src/components/core_components.jsx
file you can see pulling text
directly out of the taskData
as it was set in static_task_data
:
<p className="title is-3 is-spaced">{taskData.text}</p>
This is the flow for providing data to a static react task.
You can also pass an additional dict under the key task_config
as a part of SharedStaticTaskState
to populate the frontend's taskConfig
with those values for every task in a run.
From within the frontend, any call to the handleSubmit
method will store the data in any object passed as an argument to the local filestorage:
<button
className="button is-success is-large"
onClick={() => onSubmit({ rating: "good" })}
>
Mark as Good
</button>
This data can later be viewed using MephistoDataBrowser
or other scripts.
An onboarding step can be added to tasks, which will be shown the first time a worker encounters a task with the same onboarding_qualification
set.
For Static React Tasks, calling handleSubmit
when isOnboarding
is true will submit the onboarding.
The object passed will be sent to the Mephisto backend, wherein the contents will be passed to the validate_onboarding
method of the SharedTaskState
.
If that method returns False
, the worker will be prevented from working on the real task, or any future tasks with the same onboarding_qualification
set.
This task has one such button in webapp/src/components/core_components.jsx
that is only shown if isOnboarding
is true, and always submits a successful onboarding.
See the README on Blueprints for more info on the SharedTaskState
.
<button
className="button is-link"
onClick={() => onSubmit({ success: true })}
>
Move to main task.
</button>
In run_task__local__inhouse.py
we have a step for building the frontend before running anything: build_task()
.
This method in theory only needs to be called on the first run, or when changes are made to the webapp
directory.
For local development, you may want to have changes made to your React code reflect locally without having to restart the server each time.
To enable this, update the Hydra config's mephisto.blueprint.link_task_source
value to true
(default is false
).
After running python run_task__local__inhouse.py
, you can then run npm run dev:watch
in the webapp folder to auto-regenerate the task_source file.
Since the task source file is now symlinked, simply refreshing the browser will reflect your changes.
You won't need to kill and restart the server each time anymore.
In order to get started on your own task, it is a good idea to copy this static_react_task
directory into your workspace and use it as a starting point.
Generally you'll do the following:
-
Copy the
static_react_task
directory to your project directory -
Update
static_task_data
in therun_task__local__inhouse.py
script to provide the required data for your task -
Update any task-related variables in your
conf/my_new_config.yaml
file to make sense for your task. Some examples of blueprint variables are:extra_source_dir
takes in an optional path to sources that the frontend may refer to (such as images/video/css/scripts)data_json
takes in a path to a json file containing task data- To see other configurable blueprint variables type
mephisto wut blueprint=static_task
-
Update
webapp/src/components/core_components.jsx
to have the frontend you'd like. Include an onboarding step if possible. -
Run
run_task__local__inhouse.py
to pilot your task over localhost. -
Repeat 4 & 5 until you're happy with your task.
-
Launch a small batch on a crowd provider to see how real workers handle your task.
-
Iterate more.
-
Collect some good data.
To run tests locally you should first launch the task as follows:
python run_task__local__inhouse.py mephisto.task.post_install_script=link_mephisto_task.sh mephisto.task.force_rebuild=true
This will run the task and make sure to link the mephisto-core
package with the local one.
Then you can run cypress by going into the webapp directory and running npm run test
. This should open the cypress app.
Click the Chrome browser and select a spec to run some tests.