Come eseguire il deployment di un'app di chatbot FastAPI in Cloud Run utilizzando Gemini

Come eseguire il deployment di un'app di chatbot FastAPI in Cloud Run utilizzando Gemini

Informazioni su questo codelab

subjectUltimo aggiornamento: apr 2, 2025
account_circleScritto da un Googler

1. Introduzione

In questo codelab imparerai a eseguire il deployment di un'app FastAPI in Cloud Run. L'app è un'app di chatbot che richiede un modello Gemini.

  • Come eseguire il deployment di FastAPI in Cloud Run
  • Chiedi a Gemini di Cloud Run in Python di utilizzare una libreria client Google

2. Configurazione e requisiti

Imposta le variabili di ambiente che verranno utilizzate durante questo codelab.

PROJECT_ID=<YOUR_PROJECT_ID>
REGION
=<YOUR_REGION>
GEMINI_MODEL
=gemini-2.0-flash-001

SERVICE_NAME
=fastapi-gemini
SERVICE_ACCOUNT
=fastapi-gemini-sa
SERVICE_ACCOUNT_ADDRESS
=$SERVICE_ACCOUNT@$PROJECT_ID.iam.gserviceaccount.com

Crea l'account di servizio eseguendo questo comando:

gcloud iam service-accounts create $SERVICE_ACCOUNT \
 
--display-name="Service Account for FastAPI Gemini CR service"

Concedi al tuo account di servizio l'accesso a Gemini con il ruolo Utente Vertex AI.

gcloud projects add-iam-policy-binding $PROJECT_ID \
 
--member="serviceAccount:$SERVICE_ACCOUNT_ADDRESS" \
 
--role="roles/aiplatform.user"

3. Creare l&#39;app

Crea una directory per il codice.

mkdir codelab-cr-fastapi-gemini
cd codelab
-cr-fastapi-gemini

Innanzitutto, crea i modelli HTML creando una directory dei modelli.

mkdir templates
cd templates

Crea un nuovo file denominato ai_message.html con i seguenti contenuti:

<div class="message-container ai-message-container">
    {{ ai_response_text }}
</div>

Crea un nuovo file denominato message.html con i seguenti contenuti:

<div class="message-container user-message">
    {{ message }}
</div>

Crea un nuovo file denominato index.html con i seguenti contenuti:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>FastAPI HTMX Gemini Chat</title>
    <style>
        body { font-family: sans-serif; max-width: 700px; margin: auto; padding: 20px; background-color: #f4f4f4; }
        #chat-messages { border: 1px solid #ccc; background-color: #fff; padding: 15px; height: 400px; overflow-y: scroll; margin-bottom: 15px; border-radius: 5px; box-shadow: inset 0 1px 3px rgba(0,0,0,0.1); }
        .message-container { margin-bottom: 10px; padding: 8px 12px; border-radius: 15px; max-width: 80%; word-wrap: break-word; }
        .user-message { background-color: #dcf8c6; align-self: flex-end; margin-left: auto; text-align: right; border-bottom-right-radius: 0;}
        .ai-message-container { background-color: #eee; align-self: flex-start; margin-right: auto; border-bottom-left-radius: 0;}
        .ai-message-container p { margin: 0.2em 0; } /* Spacing for streamed paragraphs */
        .ai-message-container p:first-child { margin-top: 0; }
        .ai-message-container p:last-child { margin-bottom: 0; }
        form { display: flex; margin-top: 10px; }
        input[type="text"] { flex-grow: 1; padding: 10px; border: 1px solid #ccc; border-radius: 20px; margin-right: 10px; }
        button { padding: 10px 20px; background-color: #0b93f6; color: white; border: none; border-radius: 20px; cursor: pointer; font-weight: bold; }
        button:hover { background-color: #0a84dd; }
    </style>
    <script src="https://unpkg.com/htmx.org@2.0.4"
    integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
    <script src="https://unpkg.com/htmx-ext-sse@2.2.2" crossorigin="anonymous"></script>
</head>
<body>

    <h1>Chat with Gemini</h1>

    <div id="chat-messages">
        {% for msg in messages %}
             {# Render initial messages if needed #}
        {% endfor %}
    </div>

    <form
        hx-post="/ask"             {# Post to the /ask endpoint #}
        hx-target="#chat-messages" {# Target the main chat area #}
        hx-swap="beforeend"        {# Append the response (user msg + AI placeholder) #}
        hx-on::after-request="this.reset(); document.getElementById('chat-messages').scrollTop = document.getElementById('chat-messages').scrollHeight;" {# Clear form & scroll down #}
        >
        <input type="text" name="message" placeholder="Ask Gemini..." autofocus autocomplete="off">
        <button type="submit">Send</button>
    </form>

    <script>
        // Initial scroll to bottom on page load (if needed)
        window.onload = () => {
            const chatBox = document.getElementById('chat-messages');
            chatBox.scrollTop = chatBox.scrollHeight;
        }
    </script>

</body>
</html>

Ora crea il codice Python e gli altri file nella directory principale

cd ..

Crea un file .gcloudignore con i seguenti contenuti:

__pycache__

Crea un file denominato main.py con i seguenti contenuti:

from fastapi import FastAPI, Request, Form
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from typing import List, Annotated
from google import genai
import os

# in case the env var isn't set, use YOUR_<VARIABLE> as the default
# to help with debugging
project_id = os.getenv("PROJECT_ID", "YOUR_PROJECT_ID")
region = os.getenv("REGION", "YOUR_REGION")
gemini_model = os.getenv("GEMINI_MODEL", "gemini-2.0-flash-001")

app = FastAPI(title="FastAPI HTMX Chat")

templates = Jinja2Templates(directory="templates")

genai_client = genai.Client(
   
vertexai=True, project=project_id, location=region
)

system_prompt = f"""
You're a chatbot that helps pass the time with small talk, that is
polite conversation about unimportant or uncontroversial matters
that allows people to pass the time. Please keep your answers short.
"""

chat_messages: List[str] = []

# --- Routes ---
@app.get("/", response_class=HTMLResponse)
async def get_chat_ui(request: Request):
    """Serves the main chat page."""
   
print("Serving index.html")
   
return templates.TemplateResponse(
       
"index.html",
       
{"request": request, "messages": chat_messages} # Pass existing messages
   
)

@app.post("/ask", response_class=HTMLResponse)
async def ask_gemini_and_respond(
   
request: Request,
   
# Use Annotated for dependency injection with Form data
   
message: Annotated[str, Form()]
):
   
   
user_msg_html = templates.get_template('message.html').render({'message': message})
   
   
print("asking gemini...")
   
response = genai_client.models.generate_content(
       
model=gemini_model,
       
contents=[message],
       
config=genai.types.GenerateContentConfig(
           
system_instruction=system_prompt,
           
temperature=0.7,
       
),
   
)
   
   
print("Gemini responded with: " + response.text)
   
   
ai_response_html = templates.get_template('ai_message.html').render({'ai_response_text': response.text})

   
combined_html = user_msg_html + ai_response_html

   
return HTMLResponse(content=combined_html)

Crea un Dockerfile con i seguenti contenuti:

# Build stage
FROM python:3.12-slim AS builder

WORKDIR /app

# Install poetry
RUN pip install poetry
RUN poetry self add poetry-plugin-export

# Copy poetry files
COPY pyproject.toml poetry.lock* ./

# Copy application code
COPY . .

# Export dependencies to requirements.txt
RUN poetry export -f requirements.txt --output requirements.txt

# Final stage
FROM python:3.12-slim

RUN apt-get update && apt-get install -y libcairo2 python3-dev libffi-dev

WORKDIR /app

# Copy files from builder
COPY --from=builder /app/ .

# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Compile bytecode to improve startup latency
# -q: Quiet mode
# -b: Write legacy bytecode files (.pyc) alongside source
# -f: Force rebuild even if timestamps are up-to-date
RUN python -m compileall -q -b -f .

# Expose port
EXPOSE 8080

# Run the application
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

Crea un file pyproject.toml

[tool.poetry]
name = "codelab"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.12"
fastapi = "^0.115.12"
uvicorn = {extras = ["standard"], version = "^0.34.0"}
jinja2 = "^3.1.6"
python-multipart = "^0.0.20"
google-genai = "^1.8.0"


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

4. Esegui il deployment in Cloud Run

gcloud run deploy $SERVICE_NAME \
 --source . \
 --allow-unauthenticated \
 --service-account=$SERVICE_ACCOUNT_ADDRESS \
 --set-env-vars=PROJECT_ID=$PROJECT_ID \
 --set-env-vars=REGION=$REGION \
 --set-env-vars=GEMINI_MODEL=$GEMINI_MODEL

5. Testa il servizio

Apri l'URL del servizio nel browser web e fai una domanda a Gemini, ad esempio Perché il cielo è blu?

6. Complimenti!

Complimenti per aver completato il codelab.

Argomenti trattati

  • Come eseguire il deployment di FastAPI in Cloud Run
  • Chiedi a Gemini di Cloud Run in Python di utilizzare una libreria client Google

7. Esegui la pulizia

Per eliminare il servizio Cloud Run, vai alla console Cloud Run all'indirizzo https://console.cloud.google.com/run ed elimina il servizio.

Se scegli di eliminare l'intero progetto, puoi andare alla pagina https://console.cloud.google.com/cloud-resource-manager, selezionare il progetto creato nel passaggio 2 e scegliere Elimina. Se elimini il progetto, dovrai modificare i progetti nel tuo Cloud SDK. Puoi visualizzare l'elenco di tutti i progetti disponibili eseguendo gcloud projects list.