Skip to content

Commit

Permalink
pywit v3.0 - bot engine
Browse files Browse the repository at this point in the history
  • Loading branch information
patapizza committed Apr 13, 2016
1 parent 4ce31d3 commit 6727898
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 40 deletions.
8 changes: 8 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## v3.0

Bot Engine integration

### breaking

- the `message` API is wrapped around a `Wit` class, and doesn't take the token as first parameter

## v2.0

Rewrite in pure Python
Expand Down
33 changes: 12 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,29 @@
# pywit

`pywit` is a Python client to easily use the [Wit.ai](http://wit.ai) HTTP API.
`pywit` is the Python SDK for [Wit.ai](http://wit.ai).

## Install

Using `pip`:
```bash
pip install wit
```

## Usage

```python
import wit
print(wit.message('MY_ACCESS_TOKEN', 'turn on the lights in the kitchen'))
```

See below for more examples.

## Install from source

From source:
```bash
git clone https://github.com/wit-ai/pywit
python setup.py install
```

## API
## Usage

```python
import wit
See the `examples` folder for examples.

if __name__ == '__main__':
access_token = 'MY_ACCESS_TOKEN'
## API

# GET /message to extract intent and entities from user request
response = wit.message('turn on the lights in the kitchen', access_token)
print('/message -> {}'.format(response))
```
`pywit` provides a Wit class with the following methods:
* `message` - the Wit message API
* `converse` - the low-level Wit converse API
* `run_actions` - a higher-level method to the Wit converse API

See the [docs](https://wit.ai/docs) for more information.
67 changes: 67 additions & 0 deletions examples/joke.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from random import shuffle
from wit import Wit

# Joke example
# See https://wit.ai/patapizza/example-joke

access_token = 'YOUR_ACCESS_TOKEN'

def first_entity_value(entities, entity):
if entity not in entities:
return None
val = entities[entity][0]['value']
if not val:
return None
return val['value'] if isinstance(val, dict) else val

all_jokes = {
'chuck': [
'Chuck Norris counted to infinity - twice.',
'Death once had a near-Chuck Norris experience.',
],
'tech': [
'Did you hear about the two antennas that got married? The ceremony was long and boring, but the reception was great!',
'Why do geeks mistake Halloween and Christmas? Because Oct 31 === Dec 25.',
],
'default': [
'Why was the Math book sad? Because it had so many problems.'
],
}

def say(session_id, msg):
print(msg)

def merge(context, entities):
new_context = dict(context)
if 'joke' in new_context:
del new_context['joke']
category = first_entity_value(entities, 'category')
if category:
new_context['cat'] = category
sentiment = first_entity_value(entities, 'sentiment')
if sentiment:
new_context['ack'] = 'Glad you liked it.' if sentiment == 'positive' else 'Hmm.'
elif 'ack' in new_context:
del new_context['ack']
return new_context

def error(session_id, msg):
print('Oops, I don\'t know what to do.')

def select_joke(context):
new_context = dict(context)
jokes = all_jokes[new_context['cat'] or 'default']
shuffle(jokes)
new_context['joke'] = jokes[0]
return new_context

actions = {
'say': say,
'merge': merge,
'error': error,
'select-joke': select_joke,
}
client = Wit(access_token, actions)

session_id = 'my-user-id-42'
client.run_actions(session_id, 'tell me a joke about tech', {})
30 changes: 30 additions & 0 deletions examples/weather.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from wit import Wit

# Weather example
# See https://wit.ai/patapizza/example-weather

access_token = 'YOUR_ACCESS_TOKEN'

def say(session_id, msg):
print(msg)

def merge(context, entities):
return context

def error(session_id, msg):
print('Oops, I don\'t know what to do.')

def fetch_forecast(context):
context['forecast'] = 'cloudy'
return context

actions = {
'say': say,
'merge': merge,
'error': error,
'fetch-forecast': fetch_forecast,
}
client = Wit(access_token, actions)

session_id = 'my-user-id-42'
client.run_actions(session_id, 'weather in London', {})
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

setup(
name='wit',
version='2.0',
version='3.0',
description='Wit SDK for Python',
author='The Wit Team',
author_email='[email protected]',
Expand Down
4 changes: 1 addition & 3 deletions wit/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
from wit import (
message
)
from wit import Wit
101 changes: 86 additions & 15 deletions wit/wit.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,95 @@
import os

WIT_API_HOST = os.getenv('WIT_URL', 'https://api.wit.ai')
DEFAULT_MAX_STEPS = 5

class WitError(Exception):
pass

def req(access_token, meth, path, params):
rsp = requests.request(
meth,
WIT_API_HOST + path,
headers={
'authorization': 'Bearer ' + access_token,
'accept': 'application/vnd.wit.20160330+json'
pass

def req(access_token, meth, path, params, payload=None):
rsp = requests.request(
meth,
WIT_API_HOST + path,
headers={
'authorization': 'Bearer ' + access_token,
'accept': 'application/vnd.wit.20160330+json'
},
params=params,
)
return rsp.json()
params=params,
json=payload,
)
return rsp.json()

def validate_actions(actions):
learn_more = 'Learn more at https://wit.ai/docs/quickstart';
if not isinstance(actions, dict):
raise WitError('The second parameter should be a dictionary.')
for action in ['say', 'merge', 'error']:
if action not in actions:
raise WitError('The \'' + action + '\' action is missing. ' + learn_more)
for action in actions.keys():
if not hasattr(actions[action], '__call__'):
raise TypeError('The \'' + action + '\' action should be a function.')
return actions

class Wit:
access_token = None
actions = {}

def message(access_token, msg):
def __init__(self, access_token, actions):
self.access_token = access_token
self.actions = validate_actions(actions)

def message(self, msg):
params = {}
if msg:
params['q'] = msg
return req(access_token, 'GET', '/message', params)
params['q'] = msg
return req(self.access_token, 'GET', '/message', params)

def converse(self, session_id, message, context={}):
params = {'session_id': session_id}
if message:
params['q'] = message
return req(self.access_token, 'POST', '/converse', params, context)

def run_actions(
self,
session_id,
message,
context={},
max_steps = DEFAULT_MAX_STEPS):
if max_steps <= 0:
raise WitError('max iterations reached')
rst = self.converse(session_id, message, context)
if not rst['type']:
raise WitError('couldn\'t find type in Wit response')
if rst['type'] == 'stop':
return context
if rst['type'] == 'msg':
if 'say' not in self.actions:
raise WitError('unknown action: say')
print('Executing say with: {}'.format(rst['msg']))
self.actions['say'](session_id, rst['msg'])
elif rst['type'] == 'merge':
if 'merge' not in self.actions:
raise WitError('unknown action: merge')
print('Executing merge')
context = self.actions['merge'](context, rst['entities'])
if context is None:
print('WARN missing context - did you forget to return it?')
context = {}
elif rst['type'] == 'action':
if rst['action'] not in self.actions:
raise WitError('unknown action: ' + rst['action'])
print('Executing action {}'.format(rst['action']))
context = self.actions[rst['action']](context)
if context is None:
print('WARN missing context - did you forget to return it?')
context = {}
elif rst['type'] == 'error':
if 'error' not in self.actions:
raise WitError('unknown action: error')
print('Executing error')
self.actions['error'](session_id, message)
else:
raise WitError('unknown type: ' + rst['type'])
return self.run_actions(session_id, None, context, max_steps - 1)

0 comments on commit 6727898

Please sign in to comment.