FlutterでOpenAI APIを使ってみた
こんにちは、ゲームソリューション部のsoraです。
今回は、FlutterでOpenAI APIを使ってみたことについて書いていきます。
実装した画面
リクエストメッセージを入力して、OpenAIのAPIを実行して文章を生成するシンプルなアプリです。
前提
OpenAIにてAPIキーを作成して、利用可能なクレジットを準備しておいてください。
クレジットを準備しなくてもAPIキーは作成できますが、クレジットが0の状態で使用してもエラーになります。
(私はこれに気づくのに結構時間がかかりました。)
また、外部パッケージとして以下のchat_gpt_sdk
もありますが、今回は使用せずにシンプルにhttpパッケージを使用して、APIを実行します。
http | Dart package
参考:chat_gpt_sdk | Dart package
状態管理にはRiverpod(flutter_riverpod)を使用しています。
flutter_riverpod | Flutter package
コードの解説
コードは以下です。
今回はテストのため、ファイル分けせずに全てmain.dart
に書いています。
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http/http.dart' as http; // 表示する文字列を管理するProvider class Response extends Notifier<String> { @override String build(){ return ''; } void clear() { state = ''; } void modify(String message) { state = message; } } final responseProvider = NotifierProvider<Response, String>(() { return Response(); }); // OpenAI API実行 Future<void> apiRequest(String message, WidgetRef ref) async { String responseMessage; final providerNotifier = ref.watch(responseProvider.notifier); // 取得したAPIキーを入れる const apiKey = '{OpenAIのAPIキー}'; const domain = 'api.openai.com'; const path = 'v1/chat/completions'; // モデルの指定 const model = 'gpt-3.5-turbo-0125'; // APIリクエスト http.Response response = await http.post( Uri.https(domain, path), headers: <String, String>{ 'Content-Type': 'application/json; charset=utf-8', 'Authorization': 'Bearer $apiKey', }, body: jsonEncode(<String, dynamic>{ "model": model, "messages": [ { 'role': 'system', 'content': 'あなたはAIではありません。人間として自然な会話をしてください。', }, { 'role': 'user', 'content': message, }, ], }), ); if (response.statusCode == 200) { String responseData = utf8.decode(response.bodyBytes).toString(); final responseJsonData = jsonDecode(responseData); responseMessage = (responseJsonData['choices'] as List).first['message']['content'] as String; providerNotifier.modify(responseMessage); } else { throw Exception('Failed to load sentence'); } } void main() { runApp( const ProviderScope( child: MyApp(), ), ); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData(), home: MyHomePage(), ); } } class MyHomePage extends ConsumerWidget { MyHomePage({super.key}); final _messageController = TextEditingController(); @override Widget build(BuildContext context, WidgetRef ref) { final providerValue = ref.watch(responseProvider); final providerNotifier = ref.watch(responseProvider.notifier); return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: const Text('ChatGPT test'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Container( padding: const EdgeInsets.only( left: 25, right: 25, ), child: TextField( controller: _messageController, maxLines: 1, decoration: const InputDecoration( hintText: 'メッセージを入力', hintStyle: TextStyle(color: Colors.black54), ), ), ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( child: const Text('AIチャット実行'), onPressed: (){ var msg = _messageController.text.trim(); if(msg.isEmpty){ _messageController.clear(); return; } providerNotifier.clear(); apiRequest(msg, ref); }, ), ] ), const SizedBox(height: 30), Container( padding: const EdgeInsets.only( left: 25, right: 25, ), child: Text( '$providerValue', style: const TextStyle( fontSize: 18, ), ), ), ], ), ), ); } }
OpenAI API実行
OpenAIのAPIキーやモデルを指定して、POSTメソッドでリクエストを投げます。
リクエストボディのメッセージの部分は、'role': 'assistant'
で'content'
にレスポンスメッセージを入れてリクエストすることで、過去のやり取りを理解してレスポンスを得ることができます。
ステータスコードが200だった場合は、受け取ったレスポンスの中から必要な部分を抜き出して、状態を更新するようにしています。
ちなみに、APIキーについて、テストのためそのまま記述するコードになっていますが、本来であれば暗号化したりサーバ側で扱うようにしてください。
よくenvied
やdotenv
を使用している例がありますが、それらは難読化であって暗号化ではなく安全性は高くないため、実際に公開するアプリでは注意が必要です。
// OpenAI API実行 Future<void> apiRequest(String message, WidgetRef ref) async { String responseMessage; final providerNotifier = ref.watch(responseProvider.notifier); // 取得したAPIキーを入れる const apiKey = '{OpenAIのAPIキー}'; const domain = 'api.openai.com'; const path = 'v1/chat/completions'; // モデルの指定 const model = 'gpt-3.5-turbo-0125'; // APIリクエスト http.Response response = await http.post( Uri.https(domain, path), headers: <String, String>{ 'Content-Type': 'application/json; charset=utf-8', 'Authorization': 'Bearer $apiKey', }, body: jsonEncode(<String, dynamic>{ "model": model, "messages": [ { 'role': 'system', 'content': 'あなたはAIではありません。人間として自然な会話をしてください。', }, { 'role': 'user', 'content': message, }, ], }), ); if (response.statusCode == 200) { String responseData = utf8.decode(response.bodyBytes).toString(); final responseJsonData = jsonDecode(responseData); responseMessage = (responseJsonData['choices'] as List).first['message']['content'] as String; providerNotifier.modify(responseMessage); } else { throw Exception('Failed to load sentence'); } }
最後に
今回は、FlutterでOpenAI APIを使ってみたことを記事にしました。
どなたかの参考になると幸いです。