DEV Community

Cover image for Integrando a API do ChatGPT com o Google Firebase Firestore
Raphael Araújo
Raphael Araújo

Posted on

Integrando a API do ChatGPT com o Google Firebase Firestore

Como comentado no meu post anterior, eu desenvolvi uma arquitetura de serviços para tentar economizar um pouco na hora de consumir a API da OpenAI e o modelo gpt-3.5-turbo.

Arquitetura final descrita no [post anterior](https://dev.to/raphox/tentando-nao-ficar-pobre-antes-de-ficar-rico-criando-uma-startup-de-servicos-de-inteligencia-artificial-1mag).

Agora vou mostrar um pouco do código que inseri no Firebase functions que é chamado toda vez uma nova perguntar é inserida no 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

Essa função irá enviar os dados da pergunta cadastrada pelo usuário, para um serviço no Render.com onde a API do modelo do ChatGPT será consumida.

Estrutura de perguntas no Firestore.

Vale relembrar o motivo de eu não ter feito tudo do lado do Firebase Cloud Function:

O Cloud Function cobra de acordo com o tempo de execução da sua função, além do número de invocações e dos recursos provisionados. Como a API do ChatGPT pode demorar para responder, dependendo da complexidade da sua consulta, você pode acabar pagando muito pelo tempo que a sua função fica aguardando a resposta da API.

Ao fim do processo, a pergunta terá sua resposta atualizada no Firestore com base nos dados recibidos da API do ChatGPT.

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

Podemos destacar alguns trechos importantes do código anterior:
  • Linhas 13 e 14: São métodos customizados que fazem a comunicação com o Pinecone e a API da OpenAI. Sugiro buscar mais informações em https://python.langchain.com/en/latest/use_cases/question_answering.html

  • Linha 60: Nas linhas anteriores, o código é responsável por buscar na base de dados de perguntas já realizadas pelos usuários e encontrar a pergunta mais parecida. Com base na pergunta mais parecida já feita anteriormente, a linha 60 é responsável por verificar se a similaridade é tão próxima (95%) que a resposta da pergunta anterior pode ser utilizada para responder a nova pergunta. Como comentei no meu post anterior, esse comparativo não se daria muito bem para diferenciar perguntas como: Quanto custa 1kg do seu produto?’ e ‘Quanto custa 1g do seu produto?’.

  • Linha 71: Essa parte do código foi responsável por sanar meu problema com o delay da API da OpenAI. Alguns podem estar se perguntando o porquê de eu não ter utilizado algo relacionado a filas de processamento em background. Mas como comentei no post anterior, meu objetivo por agora é buscar alternativas mais baratas. Contratar um banco Redis e um worker em periodo integral, não está nos meu planos por agora. Mas, mudar isso, com certeza está em um dos meu planos futuros.

Documentações que podem lhe ajudar no desenvolvimento:

Referências:

  1. https://flask-httpauth.readthedocs.io/en/latest/

  2. https://zoltan-varadi.medium.com/flask-api-how-to-return-response-but-continue-execution-828da40881e7

Image of Datadog

Create and maintain end-to-end frontend tests

Learn best practices on creating frontend tests, testing on-premise apps, integrating tests into your CI/CD pipeline, and using Datadog’s testing tunnel.

Download The Guide

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs