my first Flask app: 相づちくん(その2 SPA/チャットUI化の巻)
先週、Flaskを使った初めてのチャットボットWebアプリケーション「相づちくん」を作成しました。
ただ、このバージョンはフォームを使って入出力を行うものなので、入力するたびに画面全体が書き換わってしまって、チャットっぽさがまったくありません。
ここはやはり、入出力を連続して表示するSPA(Single Page Application)を行いチャット風画面作りを目指したいと思いました。そこで、webで発見したチャット用のUIをフロントエンドとして使って、チャットUI版の「相づちくん」を作成しました。
「相づちくん」のSPA化は以下のような流れで行いました。
フロントエンド(チャットUI)の選択
フロントエンドのチャット画面(チャットUI)をゼロから作るのはちょっと大変そうです。でも、サンプルとして参考にできるものがあるだろうと楽天的に考えてWebを漁ったところ、まさにちょうどよいコード(riversun氏のchatux)が公開されておりました。chatuxの日本語サンプルページには、自分の環境で使う場合のカスタマイズ方法も丁寧に説明されているので、これを参考にすればお手軽に相づちくんのSPA化ができそうだと考えました。
SPA化でやるべきこと
とは言え、取り組み始めた状態ではチャットUIが動く理屈がわかっておらず、chatuxの説明を読んでも、今ひとつピンときません。Client Side Rendering(SPA)・SSR・SSG を整理してみた - 7839などの記事を読んで調べたところ、大まかに言えば次の要素や機能をFlask側に追加すればよいことがわかりました。
- 下記の情報を含むHTMLファイル(UI起動用HTMLファイル)
- a) UI用JavaScriptのダウンロード先 URL
- b) UI用JavaScriptの呼び出し(起動)
- c) UIがデータを取得するアクセス先のURL
- このHTMLファイルを返すエントリー
- データ(今回はチャットの返信)をJSON形式で返すエントリー
今回の場合は1) のHTMLファイル内の情報(a-c)については、chatuxの指定のフォーマットで書くので、chatuxのサンプルを改変して使います。
2), 3)のエントリーの作成はFlaskのアプリケーションに機能追加することになります。
UI起動用HTMLファイル(index.html)の作成
このHTMLファイルについては、chatuxの日本語サンプルページの最初の方の項目「エコーチャット用のUIを作る」に掲載されたHTMLを一部変更して使用しました。ファイル名はindex.htmlです。
具体的には、下記の通りです。
- titleを’エコーボット’から ‘相づちくん’に変更
- データ取得用URLを下記のように自分のFlaskのURLに変更。
//echo chat server endpoint: 'http://localhost:5000/eyezoochi_spa',
HTMLを返すエントリーの作成
エントリーを処理するコードは、前回作ったeyezoochi.py
に追加しました。
HTMLを返すエントリーはルート’/’として、シンプルに下記を追加しました
bp.route('/') def index(): return render_template('index.html')
データ(チャットの返信)をJSON形式で返すエントリー
チャットの返信を返すエントリーとしては、下記をeyezoochi.pyに追加しました。
@bp.route('/eyezoochi_spa', methods=('GET', 'POST')) def eyezoochi_spa(): # GETデータからパラメータ取得 input_text = request.args.get('text') callback = request.args.get('callback') # 人工無脳Unmoで回答文生成 proto = Unmo('proto') response_text = proto.dialogue(input_text) # 回答メッセージの生成 response = callback + '(' + json.dumps({ "output":[ { "type":"text", "value":response_text } ] }) + ')' return response
簡単なコードですが、次のようなところで引っかかって少々悩みました。
- POSTとGETでデータのと扱いが違う。
- 前回書いた関数はPOSTでフォームで取得したデータを受け取っていたのですが、chatuxはGETでデータを送ってきます。
- 最初、POSTもデータも受信データの取り出し方は同じだと勘違いしてデータが受け取れませんでした。ちょっと困った後、GET時の書き方を調べてなんとかなりました。
- chatboxのサンプルの説明をよく読んだらPOSTで送るようHTML内で設定することもできるようですが、今回はお勉強ということで。
- callbackの設定
- chatuxのサンプルの説明では、サーバ側はNode.jsを前提としています。そのコードの説明には「以下のようなJSONを返すようにする」と応答用JSONデータの形式が記載されているので、バカ正直に記載された形式のJSON形式で返信するようにしたのですが、chatux側でエラーとなってしまいます。
- Node.js用のコードを見ていたら、クライアントからGETで送ってくるcallbackの値をセットしなければならないことの気づき、その値を受信データから取り出して返信データに追加したらうまくいくようになりました。
- クライアントが送るcallback値を返すのは最低限のセキュリティということだと思いますが、そういうお作法を知らなかったので勉強になりました。
- JSON形式データの生成
- 最初、Flaskが持つjsonifyというコンポーネントを使えばいいんだろうと思っていたのですが、うまくいきません。type()で型をを調べてみるとjsonifyはテキストではなく、Flaskが定義するクラスです。この型だと、callback値(str型)との結合もうまくいきません。
- Python から JSON へ変換 - Python で JSON の読み書き - Pythonのきほん - やさしい Python 入門などを参考に、jsonifyの代わりにとして標準のJSONコンポーネントのjson.dumps()(型はstr)を使うことでうまくいきました。
- Webのプロトコルとjsonify(), json.dumps()の関係は、flask jsonify vs json.dumps 違いを簡単に解説 | 経営管理deプログラミングに解説されていました。
こうした追加をeyezoochi.pyに加えることで、http://localhost:5000/
にアクセスすると、chatuxのコードがダウンロードされ、チャットUIのウィンドウが表示されるようになりました。