Skip to content

Commit 8ca8023

Browse files
authored
Merge pull request #287 from deeppavlov/dev
Release v0.6.3 # Release notes - Update README to make it more concise and include reasons to use DFF (#281) - Add [DFF optimization guide](https://deeppavlov.github.io/dialog_flow_framework/user_guides/optimization_guide.html) (#236) - Add documentation section description for the [index page](https://deeppavlov.github.io/dialog_flow_framework/index.html) (#281) - Add async method [dff.messengers.common.interface.CallbackMessengerInterface.on_request_async](https://deeppavlov.github.io/dialog_flow_framework/apiref/dff.messengers.common.interface.html#dff.messengers.common.interface.CallbackMessengerInterface.on_request_async) (#206) - Move information about messenger interfaces from `pipeline.6_custom_messenger_interface` (removed) to [messengers.web_api_interface.1_fastapi](https://deeppavlov.github.io/dialog_flow_framework/tutorials/tutorials.messengers.web_api_interface.1_fastapi.html) (#206) - Fix cross-references in [dff.messengers.common.interface](https://deeppavlov.github.io/dialog_flow_framework/apiref/dff.messengers.common.interface.html) (#206) - Update tutorials to fit into documentation without side-scrolling (#241)
2 parents 8b397d0 + 873792d commit 8ca8023

File tree

74 files changed

+1016
-518
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+1016
-518
lines changed

README.md

Lines changed: 46 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
1-
21
# Dialog Flow Framework
32

4-
The Dialog Flow Framework (DFF) allows you to write conversational services.
5-
The service is written by defining a special dialog graph that describes the behavior of the dialog service.
6-
The dialog graph contains the dialog script. DFF offers a specialized language (DSL) for quickly writing dialog graphs.
7-
You can use it in services such as writing skills for Amazon Alexa, etc., chatbots for social networks, website call centers, etc.
8-
93
[![Documentation Status](https://github.com/deeppavlov/dialog_flow_framework/workflows/build_and_publish_docs/badge.svg)](https://deeppavlov.github.io/dialog_flow_framework)
104
[![Codestyle](https://github.com/deeppavlov/dialog_flow_framework/workflows/codestyle/badge.svg)](https://github.com/deeppavlov/dialog_flow_framework/actions/workflows/codestyle.yml)
115
[![Tests](https://github.com/deeppavlov/dialog_flow_framework/workflows/test_coverage/badge.svg)](https://github.com/deeppavlov/dialog_flow_framework/actions/workflows/test_coverage.yml)
@@ -14,6 +8,17 @@ You can use it in services such as writing skills for Amazon Alexa, etc., chatbo
148
[![PyPI](https://img.shields.io/pypi/v/dff)](https://pypi.org/project/dff/)
159
[![Downloads](https://pepy.tech/badge/dff)](https://pepy.tech/project/dff)
1610

11+
The Dialog Flow Framework (DFF) allows you to develop conversational services.
12+
DFF offers a specialized domain-specific language (DSL) for quickly writing dialogs in pure Python. The service is created by defining a special dialog graph that determines the behavior of the dialog agent. The latter is then leveraged in the DFF pipeline.
13+
You can use the framework in various services such as social networks, call centers, websites, personal assistants, etc.
14+
15+
## Why choose DFF
16+
17+
* Written in pure Python, the framework is easily accessible for both beginners and experienced developers.
18+
* For the same reason, all the abstractions used in DFF can be easily customized and extended using regular language synthax.
19+
* DFF offers easy and straightforward tools for state management which is as easy as setting values of a Python dictionary.
20+
* The framework is being actively maintained and thoroughly tested. The team is open to suggestions and quickly reacts to bug reports.
21+
1722
# Quick Start
1823
## Installation
1924

@@ -26,7 +31,6 @@ pip install dff
2631
The above command will set the minimum dependencies to start working with DFF.
2732
The installation process allows the user to choose from different packages based on their dependencies, which are:
2833
```bash
29-
pip install dff[core] # minimal dependencies (by default)
3034
pip install dff[json] # dependencies for using JSON
3135
pip install dff[pickle] # dependencies for using Pickle
3236
pip install dff[redis] # dependencies for using Redis
@@ -37,13 +41,6 @@ pip install dff[sqlite] # dependencies for using SQLite
3741
pip install dff[ydb] # dependencies for using Yandex Database
3842
pip install dff[telegram] # dependencies for using Telegram
3943
pip install dff[benchmark] # dependencies for benchmarking
40-
pip install dff[full] # full dependencies including all options above
41-
pip install dff[tests] # dependencies for running tests
42-
pip install dff[test_full] # full dependencies for running all tests (all options above)
43-
pip install dff[tutorials] # dependencies for running tutorials (all options above)
44-
pip install dff[devel] # dependencies for development
45-
pip install dff[doc] # dependencies for documentation
46-
pip install dff[devel_full] # full dependencies for development (all options above)
4744
```
4845

4946
For example, if you are going to use one of the database backends,
@@ -54,11 +51,15 @@ pip install dff[postgresql, mysql]
5451

5552
## Basic example
5653

54+
The following code snippet builds a simplistic chat bot that replies with messages
55+
``Hi!`` and ``OK`` depending on user input, which only takes a few lines of code.
56+
All the abstractions used in this example are thoroughly explained in the dedicated
57+
[user guide](https://deeppavlov.github.io/dialog_flow_framework/user_guides/basic_conceptions.html).
58+
5759
```python
58-
from dff.script import GLOBAL, TRANSITIONS, RESPONSE, Context, Message
60+
from dff.script import GLOBAL, TRANSITIONS, RESPONSE, Message
5961
from dff.pipeline import Pipeline
6062
import dff.script.conditions.std_conditions as cnd
61-
from typing import Tuple
6263

6364
# create a dialog script
6465
script = {
@@ -69,89 +70,56 @@ script = {
6970
}
7071
},
7172
"flow": {
72-
"node_hi": {RESPONSE: Message(text="Hi!!!")},
73-
"node_ok": {RESPONSE: Message(text="Okey")},
73+
"node_hi": {RESPONSE: Message(text="Hi!")},
74+
"node_ok": {RESPONSE: Message(text="OK")},
7475
},
7576
}
7677

7778
# init pipeline
7879
pipeline = Pipeline.from_script(script, start_label=("flow", "node_hi"))
7980

8081

81-
# handler requests
82-
def turn_handler(in_request: Message, pipeline: Pipeline) -> Tuple[Message, Context]:
83-
# Pass the next request of user into pipeline and it returns updated context with actor response
82+
def turn_handler(in_request: Message, pipeline: Pipeline) -> Message:
83+
# Pass user request into pipeline and get dialog context (message history)
84+
# The pipeline will automatically choose the correct response using script
8485
ctx = pipeline(in_request, 0)
85-
# Get last actor response from the context
86+
# Get last response from the context
8687
out_response = ctx.last_response
87-
# The next condition branching needs for testing
88-
return out_response, ctx
88+
return out_response
8989

9090

9191
while True:
92-
in_request = input("type your answer: ")
93-
out_response, ctx = turn_handler(Message(text=in_request), pipeline)
94-
print(out_response.text)
92+
in_request = input("Your message: ")
93+
out_response = turn_handler(Message(text=in_request), pipeline)
94+
print("Response: ", out_response.text)
9595
```
9696

9797
When you run this code, you get similar output:
9898
```
99-
type your answer: hi
100-
Okey
101-
type your answer: Hi
102-
Hi!!!
103-
type your answer: ok
104-
Okey
105-
type your answer: ok
106-
Okey
99+
Your message: hi
100+
Response: OK
101+
Your message: Hi
102+
Response: Hi!
103+
Your message: ok
104+
Response: OK
105+
Your message: ok
106+
Response: OK
107107
```
108108

109-
To get more advanced examples, take a look at
110-
[tutorials](https://github.com/deeppavlov/dialog_flow_framework/tree/master/tutorials) on GitHub.
111-
112-
# Context Storages
113-
## Description
114-
115-
Context Storages allow you to save and retrieve user dialogue states
116-
(in the form of a `Context` object) using various database backends.
117-
118-
The following backends are currently supported:
119-
* [JSON](https://www.json.org/json-en.html)
120-
* [pickle](https://docs.python.org/3/library/pickle.html)
121-
* [shelve](https://docs.python.org/3/library/shelve.html)
122-
* [SQLite](https://www.sqlite.org/index.html)
123-
* [PostgreSQL](https://www.postgresql.org/)
124-
* [MySQL](https://www.mysql.com/)
125-
* [MongoDB](https://www.mongodb.com/)
126-
* [Redis](https://redis.io/)
127-
* [Yandex DataBase](https://ydb.tech/)
128-
129-
Aside from this, we offer some interfaces for saving data to your local file system.
130-
These are not meant to be used in production, but can be helpful for prototyping your application.
131-
132-
## Basic example
133-
134-
```python
135-
from dff.script import Context
136-
from dff.pipeline import Pipeline
137-
from dff.context_storages import SQLContextStorage
138-
from .script import some_df_script
139-
140-
db = SQLContextStorage("postgresql+asyncpg://user:password@host:port/dbname")
141-
142-
pipeline = Pipeline.from_script(some_df_script, start_label=("root", "start"), fallback_label=("root", "fallback"))
109+
More advanced examples are available as a part of documentation:
110+
[tutorials](https://deeppavlov.github.io/dialog_flow_framework/tutorials.html).
143111

112+
## Further steps
144113

145-
def handle_request(request):
146-
user_id = request.args["user_id"]
147-
new_context = pipeline(request, user_id)
148-
return new_context.last_response
149-
150-
```
151-
152-
To get more advanced examples, take a look at
153-
[tutorials](https://github.com/deeppavlov/dialog_flow_framework/tree/master/tutorials/context_storages) on GitHub.
114+
To further explore the API of the framework, you can make use of the [detailed documentation](https://deeppavlov.github.io/dialog_flow_framework/index.html).
115+
Broken down into several sections to highlight all the aspects of development with DFF,
116+
the documentation for the library is constantly available online.
154117

155118
# Contributing to the Dialog Flow Framework
156119

120+
We are open to accepting pull requests and bug reports.
157121
Please refer to [CONTRIBUTING.md](https://github.com/deeppavlov/dialog_flow_framework/blob/master/CONTRIBUTING.md).
122+
123+
# License
124+
125+
DFF is distributed under the terms of the [Apache License 2.0](https://github.com/deeppavlov/dialog_flow_framework/blob/master/LICENSE).

dff/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# -*- coding: utf-8 -*-
2-
# flake8: noqa: F401
32

43

54
__author__ = "Denis Kuznetsov"

dff/context_storages/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# -*- coding: utf-8 -*-
2-
# flake8: noqa: F401
32

43
from .database import DBContextStorage, threadsafe_method, context_storage_factory
54
from .json import JSONContextStorage, json_available

dff/messengers/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
# -*- coding: utf-8 -*-
2-
# flake8: noqa: F401

dff/messengers/common/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# -*- coding: utf-8 -*-
2-
# flake8: noqa: F401
32

43
from .interface import MessengerInterface, PollingMessengerInterface, CallbackMessengerInterface, CLIMessengerInterface
54
from .types import PipelineRunnerFunction, PollingInterfaceLoopFunction

dff/messengers/common/interface.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ async def connect(self, pipeline_runner: PipelineRunnerFunction):
3030
May be used for sending an introduction message or displaying general bot information.
3131
3232
:param pipeline_runner: A function that should return pipeline response to user request;
33-
usually it's a :py:meth:`~Pipeline._run_pipeline(request, ctx_id)` function.
33+
usually it's a :py:meth:`~dff.pipeline.pipeline.pipeline.Pipeline._run_pipeline` function.
3434
:type pipeline_runner: PipelineRunnerFunction
3535
"""
3636
raise NotImplementedError
@@ -97,7 +97,7 @@ async def connect(
9797
for most cases the loop itself shouldn't be overridden.
9898
9999
:param pipeline_runner: A function that should return pipeline response to user request;
100-
usually it's a :py:meth:`~Pipeline._run_pipeline(request, ctx_id)` function.
100+
usually it's a :py:meth:`~dff.pipeline.pipeline.pipeline.Pipeline._run_pipeline` function.
101101
:type pipeline_runner: PipelineRunnerFunction
102102
:param loop: a function that determines whether polling should be continued;
103103
called in each cycle, should return `True` to continue polling or `False` to stop.
@@ -124,18 +124,33 @@ def __init__(self):
124124
async def connect(self, pipeline_runner: PipelineRunnerFunction):
125125
self._pipeline_runner = pipeline_runner
126126

127+
async def on_request_async(self, request: Any, ctx_id: Hashable) -> Context:
128+
"""
129+
Method invoked on user input. This method works just like
130+
:py:meth:`~dff.pipeline.pipeline.pipeline.Pipeline._run_pipeline`,
131+
however callback message interface may contain additional functionality (e.g. for external API accessing).
132+
Return context that represents dialog with the user;
133+
`last_response`, `id` and some dialog info can be extracted from there.
134+
135+
:param request: User input.
136+
:param ctx_id: Any unique id that will be associated with dialog between this user and pipeline.
137+
:return: Context that represents dialog with the user.
138+
"""
139+
return await self._pipeline_runner(request, ctx_id)
140+
127141
def on_request(self, request: Any, ctx_id: Hashable) -> Context:
128142
"""
129-
Method invoked on user input. This method works just like :py:meth:`.__call__(request, ctx_id)`,
143+
Method invoked on user input. This method works just like
144+
:py:meth:`~dff.pipeline.pipeline.pipeline.Pipeline._run_pipeline`,
130145
however callback message interface may contain additional functionality (e.g. for external API accessing).
131-
Returns context that represents dialog with the user;
146+
Return context that represents dialog with the user;
132147
`last_response`, `id` and some dialog info can be extracted from there.
133148
134149
:param request: User input.
135150
:param ctx_id: Any unique id that will be associated with dialog between this user and pipeline.
136151
:return: Context that represents dialog with the user.
137152
"""
138-
return asyncio.run(self._pipeline_runner(request, ctx_id))
153+
return asyncio.run(self.on_request_async(request, ctx_id))
139154

140155

141156
class CLIMessengerInterface(PollingMessengerInterface):
@@ -169,7 +184,7 @@ async def connect(self, pipeline_runner: PipelineRunnerFunction, **kwargs):
169184
The CLIProvider generates new dialog id used to user identification on each `connect` call.
170185
171186
:param pipeline_runner: A function that should return pipeline response to user request;
172-
usually it's a :py:meth:`~Pipeline._run_pipeline(request, ctx_id)` function.
187+
usually it's a :py:meth:`~dff.pipeline.pipeline.pipeline.Pipeline._run_pipeline` function.
173188
:type pipeline_runner: PipelineRunnerFunction
174189
:param \\**kwargs: argument, added for compatibility with super class, it shouldn't be used normally.
175190
"""

dff/messengers/telegram/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# -*- coding: utf-8 -*-
2-
# flake8: noqa: F401
32

43
try:
54
import telebot

dff/messengers/telegram/interface.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ async def endpoint():
235235

236236
json_string = request.get_data().decode("utf-8")
237237
update = types.Update.de_json(json_string)
238-
resp = self.on_request(*extract_telegram_request_and_id(update, self.messenger))
238+
resp = await self.on_request_async(*extract_telegram_request_and_id(update, self.messenger))
239239
self.messenger.send_response(resp.id, resp.last_response)
240240
return ""
241241

dff/pipeline/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# -*- coding: utf-8 -*-
2-
# flake8: noqa: F401
32

43

54
from .conditions import (

dff/pipeline/pipeline/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
# -*- coding: utf-8 -*-
2-
# flake8: noqa: F401

0 commit comments

Comments
 (0)