|
1 | | -# Pyttman |
| 1 | + |
2 | 2 |  |
3 | 3 |
|
4 | 4 |
|
5 | | -## The Python chatbot framework with batteries included |
| 5 | +# The Python chatbot framework with batteries included |
6 | 6 | [](https://badge.fury.io/py/Pyttman) [](https://github.com/dotchetter/Pyttman/actions/workflows/codeql-analysis.yml) [](https://github.com/dotchetter/Pyttman/actions/workflows/python-package.yml) |
7 | 7 |
|
8 | | -### About |
| 8 | +### If you've ever developed a chatbot or otherwise virtual assistant |
| 9 | +-how many times have you had to iterate over the message contents to route the |
| 10 | +message to the correct part of your code? Or even worse; How many hours have you spent on |
| 11 | +trying to decipher a message and its contents to find words of interest? Maybe the name of a song, or a phone number? Or a username? |
| 12 | + |
| 13 | +### I had enough of that - so I wrote this framework. |
| 14 | + |
9 | 15 | The Pyttman Framework aims to provide a similar experience as Django and Flask does for web, but for chatbots and digital assistants. |
| 16 | +It's large and full of features yet easy to use. |
| 17 | + |
| 18 | +# Abiliy, Intent, Entity |
| 19 | +Pyttman apps are structured with inspiration from the MVC (Model View Controller) |
| 20 | +pattern, but things are named a bit differently. Here's a short rundown on what they're about. |
| 21 | + |
| 22 | + |
| 23 | +## Ability |
| 24 | +Chatbots can have many areas of capabilities; Anything from answering questions, searching the web, |
| 25 | +playing songs to playing games. I call these **Abilities**, which encapsulate responses to |
| 26 | +queries in the same domain. The **Ability** class **has** `Intent` classes, and acts like a hub for the Intent classes which |
| 27 | +correspond to the same domain of the application. *The ability to store expenses* may contain |
| 28 | +many components - saving new expenses, visualizing existing ones, searching for records, etcetera. |
| 29 | + |
| 30 | +**Ability** classes also facilitate a session storage object accessible in the Ability as `self.storage` and in intent classes |
| 31 | +as `self.ability.storage`. This allows efficient and safe memory storage for intents to manipulate data in their respective |
| 32 | +domain, without global variables and risking exposing data to intents of another domain. |
| 33 | + |
| 34 | +```python |
| 35 | +class MusicPlayer(Ability): |
| 36 | + """ |
| 37 | + This Ability class holds Intent classes which serve users |
| 38 | + ways to play music through the chatbot. |
| 39 | + """ |
| 40 | + |
| 41 | + # Intent classes are added to this tuple for them to be available |
| 42 | + # to users when the application starts. |
| 43 | + intents = (PlaySong, |
| 44 | + RepeatSong, |
| 45 | + FindSongByLyrics) |
| 46 | +``` |
| 47 | + |
| 48 | + |
| 49 | +## Intent |
| 50 | +The **Intent** class is the heart of the Pyttman development experience. They are classes, 1:1 to a message and a response. |
| 51 | +If you're a web developer, you see the similarity to a controller. |
| 52 | +Here, we tell which messages it should match and also how to respond with |
| 53 | +implementing the `respond` method. |
| 54 | + |
| 55 | +**Consider a message like** `Play Last Train Home by John Mayer on Spotify`. |
| 56 | + |
| 57 | +```python |
| 58 | +class PlaySong(Intent): |
| 59 | + """ |
| 60 | + Lets people play songs on either YouTube, Spotify or SoundCloud. |
| 61 | + """ |
| 62 | + lead = ("play",) |
| 63 | + exclude = ("on",) # These words will be excluded in all entities. |
| 64 | + |
| 65 | + song = TextEntityField(span=5) |
| 66 | + artist = TextEntityField(prefixes=("by", "with"), span=10) |
| 67 | + shuffle_songs = BoolEntityField(message_contains=("shuffle",)) |
| 68 | + platform = TextEntityField(as_list=True, valid_strings=( |
| 69 | + "Spotify", "SoundCloud", "YouTube") |
| 70 | + ) |
| 71 | + |
| 72 | + def respond(self, message: Message) -> Reply | ReplyStream: |
| 73 | + song = message.entities["song"] |
| 74 | + artist = message.entities["artist"] |
| 75 | + shuffle_songs = message.entities["shuffle_songs"] |
| 76 | + platform = message.entities["platform"] |
| 77 | + # Play the song... |
| 78 | + return Reply(f"Playing {song} by {artist} on {platform}!") |
| 79 | +``` |
| 80 | + |
| 81 | +## Entity |
| 82 | +*What are those `EntityField` classes in the example above?* |
| 83 | + |
| 84 | +In Pyttman, the **Entity** is a word, or several words sought in a message. When writing chatbots |
| 85 | +it's very common to have to parse the message from humans to try and find words of |
| 86 | +interest. Maybe your chatbot registers purchases in a database. You'll need to parse |
| 87 | +the name of the purchased item as well as the price. But how do you know where to stop |
| 88 | +the name of the item? It could be one word, or several. The price could be mentioned |
| 89 | +first - or last - or in the middle of the message. Positioning won't help. Regex? |
| 90 | +This repetitive and complex process also won't scale. When you've invented the parser |
| 91 | +for one answer, you'll need to do it again for the next thing your chatbot can do. |
| 92 | + |
| 93 | +### Let's take a closer look at the example code above. |
| 94 | + |
| 95 | +This EntityField declared in the Intent class above tells Pyttman that you're looking for |
| 96 | +a text value in the message. The `span` argument defaults to 1, but in this case is set to 5. |
| 97 | +That means the value parsed here can be 5 words long, at the longest - after which Pyttman will |
| 98 | +stop adding words to the entity, should it be any longer than that. |
| 99 | + |
| 100 | +```python |
| 101 | +song = TextEntityField(span=5) |
| 102 | +``` |
| 103 | + |
| 104 | +This EntityField is convenient for flags; it will set itself to `True` if a matching word was found in the message, otherwise `False`. |
| 105 | +```python |
| 106 | +shuffle_songs = BoolEntityField(message_contains=("shuffle",)) |
| 107 | +``` |
| 108 | + |
| 109 | +For situations where a set of known valid choices are available, the `valid_strings` argument can be provided, rendering all other words ignored. |
| 110 | +In the example below, `message.entities["platform"]` can only ever be `Spotify, SoundCloud, YouTube` or its `default` which defaults to `None`. |
| 111 | +```python |
| 112 | +platform = TextEntityField(as_list=True, valid_strings=( |
| 113 | + "Spotify", "SoundCloud", "YouTube")) |
| 114 | +``` |
| 115 | + |
| 116 | +When the message has entered the `respond` method of the matching Intent, the entities are |
| 117 | +available on the `entities` property on the `Message` object. |
| 118 | + |
| 119 | +Given the Intent above and the message `Play Last Train Home by John Mayer on Spotify`, the dictionary `message.entities` look like this: |
| 120 | + |
| 121 | +```python |
| 122 | +{ |
| 123 | + 'song': 'Last Train Home', |
| 124 | + 'artist': 'John Mayer', |
| 125 | + 'shuffle_songs': False, |
| 126 | + 'platform': 'Spotify' |
| 127 | +} |
| 128 | +``` |
| 129 | + |
| 130 | + |
| 131 | +# Easy to get started with |
| 132 | +Pyttman ships with a CLI tool which is available after pip install. It helps creating |
| 133 | +and running your apps with simple commands. |
| 134 | + |
| 135 | +``` |
| 136 | +# Install it |
| 137 | +pip install pyttman |
| 138 | +
|
| 139 | +# Create an app from a template |
| 140 | +pyttman new app my_first_app |
| 141 | +
|
| 142 | +# Create an Ability module with files from a template |
| 143 | +pyttman new ability |
| 144 | +
|
| 145 | +# Run it in dev mode |
| 146 | +pyttman dev my_first_app |
| 147 | +
|
| 148 | +# Run it in shell mode, and interact with your objects through a console with Pyttman |
| 149 | +modules and objects loaded |
| 150 | +pyttman shell my_first_app |
| 151 | +
|
| 152 | +# Run it in production mode, for your platform of choice as set in settings.py |
| 153 | +pyttman runclient my_first_app |
| 154 | +``` |
| 155 | + |
| 156 | +## Find content in messages |
10 | 157 |
|
11 | | -With class based intents, abilities and entities - elements which are key in any chatbot environment; they are |
12 | | -offered in a very easy way to work with if one is unfamiliar with them; or a very extensive and flexible framework |
13 | | -with the ability to subclass and customize behavior of key parts of the framework. |
14 | 158 |
|
15 | 159 | **Pyttman aims to offer developers a platform-independent experience.** |
16 | 160 | We're constantly developing support for more platforms. As of today, Pyttman offers built-in support for Discord through the [discord.py](https://github.com/Rapptz/discord.py) library, and we have more on the way. |
|
0 commit comments