Package: async_btree
Async Behaviour Tree for PythonVersions followingSemantic VersioningSeedocumentation.OverviewWhat's a behavior tree ?Unlike a Finite State Machine, a Behaviour Tree is a tree of hierarchical nodes that controls the flow of decision and the execution of "tasks" or, as we will call them further, "Actions".
--behaviortreeIf your new (or not) about behavior tree, you could spend some time on this few links:Behavior trees for AI: How they workby Chris SimpsonIntroduction to BTsFew implementation libraries:task_behavior_engineA behavior tree based task engine written in Pythonpi_treesa Python/ROS library for implementing Behavior Treespr_behavior_treeA simple python behavior tree library based on coroutinesbtskBehavior Tree Starter KitbehaveA behavior tree implementation in PythonWhy another library so ?SIMPLICITYWhen you study behavior tree implementation, reactive node, dynamic change, runtime execution, etc ...
At a moment you're build more or less something that mimic an evaluator 'eval/apply' or a compilator, with a complex hierachical set of class.All complexity came with internal state management, using tree of blackboard to avoid global variable, multithreading issue, maybe few callback etc ...This break the simplicity and beauty of your initial design.What I find usefull with behavior tree:clarity of expressionnode tree representationpossibility to reuse behavioradd external measure to dynamicaly change a behavior, a first step on observable pattern...As I've used OOP for years (very long time), I will try to avoid class tree and prefer using the power of functionnal programming to obtain what I want: add metadata on a sematic construction, deal with closure, use function in parameters or in return value...And a last reason, more personal, it that i would explore python expressivity.SO HOW ?In this module, I purpose you to use the concept of coroutines, and their mecanisms to manage the execution flow.
By this way:we reuse simple language idiom to manage state, parameter, etcno design constraint on action implementationmost of language build block could be reusedYou could build expression like this:asyncdefa_func():"""A great function"""return"a"asyncdefb_decorator(child_value,other=""):"""A great decorator..."""returnf"b{child_value}{other}"assertrun(decorate(a_func,b_decorator))=="ba"This expression applyb_decoratoron functiona_func.
Note thatdecorate(a_func, b_decorator)is not an async function, only action, or condition are async function.Few guidelines of this implementation:In order to mimic all NodeStatus (success, failure, running), I replace this by truthy/falsy meaning of evaluation value.
A special dedicated exception decorate standard exception in order to give them a Falsy meaning (ControlFlowException).
By default, exception are raised like happen usually until you catch them.Blackboard pattern, act as a manager of context variable for behavior tree.
With python 3, please... simply usecontextvars!In order to be able to build a sematic tree, I've introduce a metadata tuple added on function implementation.The rest is just implementation details..A little note:You should not use this until you're ready to think about what you're doing :)Note about 'async' frameworkAs we use async function as underlaying mechanism to manage the execution flow, the standard library asyncio is pretty fine.
But, (always a but somewhere isn't it...), you should read thisamazing blog postby Nathaniel J. Smith.
And next studycurioframework in deep.As curio say:Don't Use Curio if You're Allergic to CurioPersonaly, after few time of testing and reading curio code, I'm pretty addict.Ifcuriois not present, we default toasyncio.InstallationInstall this library directly into an activated virtual environment with pip orPoetry:python -m pip install async-btreeor 'poetry add async-btreewith with curio extention:python -m pip install async-btree[curio]or 'poetry add async-btree[curio]UsageAfter installation, the package can imported:$ python
>>> import async_btree
>>> async_btree.__version__SeeAPI Reference documentation.With this framework, you didn't find any configuration file, no Xml, no json, no yaml.The main reason (oriented and personal point of view) is that you did not need to introduce an extra level of abtraction
to declare a composition of functions. I think it's true for most of main use case (except using an editor to wrote behaviour tree for example).So "If you wrote your function with python, wrote composition in python"...(remember that you did not need XML to do SQL, just write good sql...)So, the goal is to:define your business function wich implements actions or conditions, with all test case that you wish/needcompose them using those provided by this framework likesequence,selector, ...use them as it is or create a well define python module to reuse themWanna style have an abtract tree of our behaviour tree ?Functions from async-btree build an abstract tree for you.
If you lookup in code, you should see an annotation "node_metadata" on internal implementation.
This decorator add basic information like function name, parameters, and children relation ship.This abstract tree can be retreived and stringified withanalyzeandstringify_analyze.
Here the profile:defanalyze(target:CallableFunction)->Node:# here we have our "abtract tree code"...For example:# your behaviour tree, or a sub tree:my_func=alias(child=repeat_until(child=action(hello),condition=success_until_zero),name="btree_1")# retrieve meta information and build a Node treeabstract_tree_tree_1=analyze(my_func)# output the tree:print(stringify_analyze(abstract_tree_tree_1))This should print:--> btree_1:
     --(child)--> repeat_until:
         --(condition)--> success_until_zero:
         --(child)--> action:
                      target: helloNote about action and condition method:you could use sync or async functionyou could specify a return value with SUCCESS or FAILUREfunction with no return value will be evaluated as FAILURE until you decorate them with aalways_successoralways_failureSee thisexample/tutorial_1.pyfor more information.