Skip to content
Open
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
86 changes: 84 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

A framework for building bots, made for humans.


# How to run?

1. Install the `poetry` environment:
```shell
poetry install
Expand All @@ -26,4 +26,86 @@ A framework for building bots, made for humans.
```

5. Check the API server @ http://localhost:8000/docs.
6. Use one of the clients to connect the platforms: [Discord](https://github.com/CyberCRI/ikigai-discord-client).
6. Use one of the clients to connect the platforms: [Discord](https://github.com/CyberCRI/ikigai-discord-client).

---

# How does the `electro` framework work?

The core idea behind the framework is to simplify the flow of users' conversations by allowing you to group dialogs into
Python classes.
These classes are then called Flows, and class members are called Flow Steps.
When a Flow represents a full dialog, the Flow Steps are the individual messages.

## Flows

The Flow is created when you inherit from the `electro.Flow` class.
It automatically gathers all the members of the child class that are Flow Steps and keeps an ordered list of all those
Flow Steps internally.

In practice, it looks like this:

```python
from electro import Flow, MessageFlowStep
from electro.triggers import CommandTrigger


class TestFlow(Flow):
"""Test Flow."""

_triggers = [
CommandTrigger("test"),
]

send_test_message_1 = MessageFlowStep("test_flow_message")
send_test_message_2 = MessageFlowStep("test_flow_message_2")
```

### Triggering a Flow

In the example above, we see this line:

```python
_triggers = [
CommandTrigger("test"),
]
```

This is, in essence, how you _trigger_ a Flow.
Why trigger?
Imagine you have multiple Flows, and only one can be run at a time.
A trigger is something that allows you to "enter"
that specific Flow, and proceed from there. Usually a Trigger is a command. However, it can be almost anything that
inherits from the `electro.triggers.BaseFlowTrigger` class and implements the `._check(...)` method.

### Registering a Flow

The Flow keeps track of all its Flow Steps. But who keeps track of all the Flows? That's the job of the FlowManager.
**There's only one FlowManager in the Project** (and it hooks to the FastAPI to send/receive messages).

To register your Flow, you have to:

```python
from electro.flow_manager import global_flow_manager # import the Global Flow Manager

global_flow_manager.add_flow(YourFlow)
```

## Flow Steps

Each Flow Step is responsible for two things:

- sending a text/photo/buttons (or do nothing).
- receiving and processing the response (or don't).

It works like that to give you more control over the flow of the conversation (because usually you _ask a question_ and
_listen to the response_ within the same entity), unlike the usual "handlers" approach.

## Under the hood

The Flow class knows all of its steps, indexes them, and keeps track of which Step every User is currently at.
When a message is sent, the Flow Step index is still N.
When a message is received from the user, Flow gets the Flow Step with index N, calls the
`FlowStep().process_response()` method. If that method raises `FlowStepDone`, the N becomes N+1 and we move to the next
Flow Step. The cycle continues until there are no more Steps in the Flow.