DEV Community

Cover image for Integrating Google Firebase Firestore with ChatGPT API. Saving money
Raphael Araújo
Raphael Araújo

Posted on

1

Integrating Google Firebase Firestore with ChatGPT API. Saving money

As commented in my previous post, I developed a services architecture to save a little when consuming the OpenAI API and the gpt-3.5-turbo model.

The final version of the architecture from my previous post.

Now I’m going to show you some of the code that I inserted in Firebase functions that is called every time a new question is inserted in Firestore.

const functions = require("firebase-functions");
require("dotenv").config();
exports.answerQuestion = functions.firestore
.document("/questions/{questionId}")
.onCreate((snap, context) => {
const data = snap.data();
functions.logger.log(
"Answering question",
context.params.questionId,
data.title,
);
fetch(process.env.RENDER_API_URL, {
method: "post",
body: JSON.stringify({
data,
collection: "questions",
document: context.params.questionId,
}),
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${process.env.SECURITY_TOKEN}`,
},
});
});
view raw index.js hosted with ❤ by GitHub

This function will send the question data registered by the user to a service on Render.com where the API of the ChatGPT model will be consumed.

Structure of question on Firestore

It’s worth remembering why I didn’t do everything on the Firebase Cloud Function side:

Cloud Function charges based on how long your function runs, as well as the number of invocations and provisioned resources. As the ChatGPT API can be slow to respond depending on the complexity of your query, you could end up paying a lot for the time your function is waiting for the API response.

At the end of the process, the answer to the question will be updated in Firestore based on the data received from the ChatGPT API.

import os
import time
import threading
import firebase_admin
from dotenv import load_dotenv
from firebase_admin import firestore, credentials
from langchain.schema import AIMessage
from flask import Flask, request
from flask_httpauth import HTTPTokenAuth
from ask import execute as ask_execute
from query import upsert_question, execute as query_execute
load_dotenv()
cred = credentials.Certificate("./serviceAccountKey.json")
firebase_admin.initialize_app(cred)
app = Flask(__name__)
auth = HTTPTokenAuth(scheme='Bearer')
tokens = {
os.environ.get('FIREBASE_TOKEN'): "firebase"
}
@auth.verify_token
def verify_token(token):
if token in tokens:
return tokens[token]
@app.route('/answer_question', methods=['POST'])
@auth.login_required
def answer_question():
data = request.get_json()
def long_running_task(**kwargs):
params = kwargs.get(
'post_data', {"data": {}, "collection": "", "document": ""}
)
data = params["data"]
collection_path = params["collection"]
document_path = params["document"]
client = firestore.client()
affected_doc = client.collection(
collection_path).document(document_path)
answer = None
question = data["title"]
similarity_search = query_execute(question, k=1, namespace="questions")
if similarity_search:
document, score = similarity_search[0]
if score > 0.95:
answer = AIMessage(content=document.metadata['answer'])
if not answer:
answer = ask_execute(question)
upsert_question(question, answer.content)
affected_doc.update({
u'answer': answer.content
})
thread = threading.Thread(
target=long_running_task,
kwargs={'post_data': data}
)
thread.start()
return {"message": "Accepted"}, 202
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
view raw app.py hosted with ❤ by GitHub

We can highlight some important snippets of the previous code:

  • Lines 13 and 14: These are custom methods that communicate with Pinecone and the OpenAI API. I suggest looking for more information at https://python.langchain.com/en/latest/use_cases/question_answering.html

  • Line 60: In the previous lines, the code is responsible for searching the database of questions already asked by users and finding the most similar question. Based on the most similar question ever asked before, line 60 is responsible for checking whether the similarity is so close (95%) that the answer from the previous question can be used to answer the new question. As I commented in my previous post, this comparison would not do very well to different questions such as: How much does 1 kg of your product cost?’ and ‘How much does 1g of your product cost?’.

  • Line 71: This part of the code solved my problem with the OpenAI API delay. Some may wonder why I haven’t used something related to background processing queues. But as I mentioned in the previous post, my goal, for now, is to look for cheaper alternatives. Hiring a Redis bank and a full-time worker is not my current plan. But changing that is definitely one of my future plans.

Documents that can help you in the development:

References:

Retry later

Top comments (0)

Retry later
Retry later