Flutter で AzureのOpenAI系のAPIが使いたかったので使えるようにしたというお話
Flutterはじめました
普段の仕事としてPoCみたいなことをやりたい場合は開発スピードが命なので、使える資産の多いPythonが使えるFletやStreamlitを利用する事が多いのですが、機能としての有用性が分かって、多くの人の手元で使えるようにしたい!という場合に、企業内だとサーバたてるにしろデスクトップアプリを配るにしろ色々と面倒だったりするわけでして、ケースによってはモバイルアプリとかの方が手軽でいいよね、みたいなこともあるわけです。そんな時にさっと作って出すならなんだろうな?まぁFlutterにしとけば急に基盤として使えなくなるなんてことにはならんだろう、みたいな軽い気持ちで勉強を始めた次第。
とりあえずなんか作ってみよう
あまり複雑なものを作ろうとするとデザイン考えたりとか色々と大変なので、最初はシンプルに何か質問したらGPT先生が返信してくれるような単純なチャットアプリくらいがいい規模感かな、ということで挑戦しようということに
Pythonのopenaiパッケージみたいなのが欲しいんですよ
上記「つくりながら学ぶ!AIアプリ開発入門 - LangChain & Streamlit による ChatGPT API 徹底活用」にも以下のようなサンプルが置かれていますが
llm = ChatOpenAI() # ChatGPT APIを呼んでくれる機能
message = "Hi, ChatGPT!" # あなたの質問をここに書く
messages = [
SystemMessage(content="You are a helpful assistant."),
HumanMessage(content=message)
]
response = llm(messages)
print(response)
まぁ、端的に言ってめちゃくちゃ簡単に使えるしとてもいい設計だなー、と思うわけです。しかし、入門したてで情報収集能力が低いせいかも知れませんが、Flutterでこんな感じにライトに使えてAzureOpenAIにも対応してて、ストリーム取得にも対応している、みたいな既存のライブラリを見つけることができませんでした。
というわけでなんかPython版のopenaiパッケージみたく手軽に使えるものを作ってみた
慣れないDartという言語だったこともあり、まぁそこそこ(二晩くらい?)試行錯誤ありましたが、こんな感じで使えるようになりました。
呼び出し方
onSubmitted: (text) {
cs.ChatRequest newRequest = cs.ChatRequest(
model: "gpt-4",
messages: [cs.ChatMessage(messageType: cs.MessageType.user, content: text)],
maxTokens: 500,
stream: true,
);
fetchChatResponses(newRequest);
messageController.clear(); // テキストフィールドをクリア
},
この呼び出す際の stream って値を宣言しない、もしくは false にしておけば、通常のストリームなしのまとまった文字列としてレスポンス返してくれるヤツになります。そうです。その辺もopenaiパッケージの振る舞いを真似っこしました!
レスポンスとかの処理
利用する側での自由度高い方がいいよね、Python版もなんかそうだもんね、と思い、返ってきたテキストデータだけを抽出して戻すんじゃなくて、JSONのデータそのまま渡すようにしてるんで、必要なものを自分で選択的に使ってもらうイメージで
void fetchChatResponses(cs.ChatRequest request) async {
currentResponse = ""; // 新しい応答の取得を開始する前にリセット
chatService.chat(request).listen((response) {
if (response['choices'][0]['delta']['content'] != null && response['choices'][0]['delta']['content'].isNotEmpty) {
setState(() {
// 既存のメッセージアイテムを更新するか、新しいメッセージアイテムを追加
if (currentResponse.isEmpty) {
messages.add(response['choices'][0]['delta']['content']); // 初めてのチャンクの場合、新しいメッセージとして追加
} else {
messages[messages.length - 1] = currentResponse + response['choices'][0]['delta']['content']; // 既存のメッセージを更新
}
currentResponse += response['choices'][0]['delta']['content']; // 現在の応答にチャンクを追加
});
}
}, onError: (error) {
print("Error: $error");
// エラー処理...
});
}
まぁ、なんか動いた
というわけで満足。
呼び出し方等のサンプルも含めて、アプリとしてビルドできる形でコードはここに置いておきますので(とはいえアクセストークン等は当然ですがご自身でご準備ください)、ご興味ある方はご自由にお使いください。ライセンスはApache V2です。
呼び出して使うやつのコードはココです
参考文献
こちらの記事がとても参考になりました。Thank you! and ありがとうございます!!
Discussion