FlutterからCloud Functions for Firebase(Python)経由でOpen AIのAPIを使ってみた
こんにちは、ゲームソリューション部のsoraです。
今回は、FlutterからCloud Functions for Firebase(Python)経由でOpen AIのAPIを使ってみたことについて書いていきます。
実装した画面
見た目としては、リクエストメッセージを入力してOpenAIのAPIを実行して文章を生成するシンプルなアプリです。
内部的には、シークレットキーをバックエンド(Firebase)に持たせることで、Flutter側で管理せずに実行する作りにします。
環境準備
Firebase CLIのインストールなどの必要な環境準備していきます。
npmはインストールされている前提とします。
# firebase cliのインストール npm install -g firebase-tools # Firebaseのログイン firebase login # ⇒GUIで認証する # flutterfire_cliのインストール dart pub global activate flutterfire_cli
コードの解説
Cloud Functions for Firebase(Python)
コードは以下です。
解説はコード内コメントに記載しています。
基本的にはHTTP経由で実行する場合のコードと同様ですが、SDKを使用してのアクセスになるため、少し記述が変わる部分があります。
リージョンはデフォルトでus-central1
になっているため、asia-northeast1(東京)
にしています。
openaiのライブラリが必要なため、requirements.txtに記述して追加しておきます。
from firebase_functions import https_fn, options from firebase_functions.params import SecretParam from firebase_admin import initialize_app from openai import OpenAI import os # 新しいAppインスタンスを初期化して返却する initialize_app() # リージョンの指定 options.set_global_options(region=options.SupportedRegion.ASIA_NORTHEAST1) # SDKを使用してのアクセスのため、on_requestではなくon_call @https_fn.on_call(secrets=["OPENAI_KEY"]) def test_api(req: https_fn.CallableRequest) -> https_fn.Response: client = OpenAI( # secretsの値を取得 api_key = os.environ.get('OPENAI_KEY'), ) # on_requestの場合はreq.args、on_callの場合はreq.data req_message = req.data["message"] response = client.chat.completions.create( model="gpt-3.5-turbo", messages=[ {"role": "system", "content": "あなたはAIではありません。人間として自然な会話をしてください。"}, {"role": "user", "content": req_message}], ) res_text = response.choices[0].message.content return {"message": res_text}
コードが完成したためデプロイします。
# 仮想環境上でライブラリのインストール source "/home/{user}/test/functions/venv/bin/activate" && python3.12 -m pip install -r requirements.txt # 仮想環境の終了(実行しなくても問題はない) (venv):~$ deactivate # デプロイ firebase deploy --only functions:test_api
ブログ冒頭でも記載しましたが、シークレットキーは以前登録したものを使用するため、登録については今回は割愛します。
シークレットキーの登録については、以下をご参考ください。
Flutter
コードは以下です。
解説はコード内コメントに記載しています。
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; // Firebaseを使用するために必要なパッケージ import 'package:cloud_functions/cloud_functions.dart'; import 'package:firebase_core/firebase_core.dart'; // 接続先の情報を記述したファイルのインポート import 'firebase_options.dart'; // 表示する文字列を管理する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 { // Cloud Functions for Firebaseのリージョンがデフォルトでない場所にデプロイした場合は、明示的に指定する必要がある final functions = FirebaseFunctions.instanceFor( region: 'asia-northeast1' ); final providerNotifier = ref.watch(responseProvider.notifier); // Cloud Functions for Firebaseの関数名を指定 final HttpsCallable callable = functions.httpsCallable("test_api"); // 関数を呼び出し、引数を渡す final HttpsCallableResult result = await callable.call({ 'message': message }); final resMessage = result.data['message']; providerNotifier.modify(resMessage); } void main() async { WidgetsFlutterBinding.ensureInitialized(); // optionsで指定しているのはflutterfire configure(詳細は後述)にて生成されたfirebase_options.dartの設定値を使用 await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform ); 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('OpenAI API 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, ), ), ), ], ), ), ); } }
コード内に記述しましたが、Firebase上のプロジェクトへの接続情報が記述されたファイルを生成するために、以下コマンドを実行します。
実行することでfirebase_options.dart
が自動生成されます。
# firebaseの自身のプロジェクトとの連携のための設定ファイルの自動生成 # ⇒プロジェクトのルートディレクトリで実行すること flutterfire configure # もしくは以下でも可能 flutterfire configure --project={FirebaseのProjectID}
最後に
今回は、FlutterからCloud Functions for Firebase(Python)経由でOpen AIのAPIを使ってみたことを記事にしました。
どなたかの参考になると幸いです。