Guide de l'atelier pratique sur Duet AI pour les développeurs

1. Objectifs

L'objectif de cet atelier est de proposer des formations pratiques sur Duet AI aux utilisateurs et aux professionnels.

Dans cet atelier de programmation, vous allez apprendre à:

  1. Activez Duet AI dans votre projet GCP et configurez-le pour l'utiliser dans un IDE et dans la console Cloud.
  2. Utilisez Duet AI pour générer, compléter et expliquer du code.
  3. Utiliser Duet AI pour expliquer et résoudre un problème lié à une application
  4. Fonctionnalités de Duet AI telles que le chat IDE et le chat multitours, la génération de code par chat ou intégrée, des actions intelligentes comme l'explication du code et la confirmation de la récitation, et plus encore.

Narrative

Pour montrer comment Duet AI pour les développeurs est utilisé de manière authentique dans le développement quotidien, les activités de cet atelier se déroulent dans un contexte narratif.

Un nouveau développeur rejoint une entreprise d'e-commerce. Il est chargé d'ajouter un nouveau service à l'application d'e-commerce existante (composée de plusieurs services). Le nouveau service fournit des informations supplémentaires (dimensions, poids, etc.) sur les produits du catalogue de produits. Ce service permettra de réduire les frais de port en fonction des dimensions et du poids des produits.

Comme le développeur débute dans l'entreprise, il utilisera Duet AI pour générer du code, les explications et la documentation.

Une fois le service codé, un administrateur de la plate-forme utilise Duet AI (chat) pour créer l'artefact (conteneur Docker) et les ressources nécessaires pour le déployer sur GCP (par exemple, Artifact Registry, autorisations IAM, dépôt de code, infrastructure de calcul GKE ou Cloud Run, etc.).

Une fois l'application déployée sur GCP, un opérateur d'application/un ingénieur SRE utilise Duet AI (et Cloud Ops) pour l'aider à corriger une erreur dans le nouveau service.

Persona

L'atelier aborde le persona suivant:

  1. Développeur d'applications : certaines connaissances en programmation et en développement logiciel sont requises.

Cette variante de l'atelier Duet AI est réservée aux développeurs. Aucune connaissance des ressources cloud GCP n'est requise. Les scripts permettant de créer les ressources GCP nécessaires à l'exécution de cette application sont disponibles ici. Vous pouvez suivre les instructions de ce guide pour déployer les ressources GCP requises.

2. Préparer l'environnement

Activer Duet AI

Vous pouvez activer Duet AI dans un projet GCP via l'API (outils gcloud ou IaC tels que Terraform) ou via l'interface utilisateur de la console Cloud.

Pour activer Duet AI dans un projet Google Cloud, vous devez activer l'API Cloud AI Companion, et attribuer les rôles Identity and Access Management (IAM) "Utilisateur de Cloud AI Companion" et "Lecteur Service Usage" aux utilisateurs.

Via gcloud

Activez Cloud Shell :

Configurez vos PROJECT_ID et USER, puis activez l'API Cloud AI Companion.

export PROJECT_ID=<YOUR PROJECT ID>
export USER=<YOUR USERNAME> # Use your full LDAP, e.g. name@example.com
gcloud config set project ${PROJECT_ID}
gcloud services enable cloudaicompanion.googleapis.com --project ${PROJECT_ID}

La sortie ressemble à ceci:

Updated property [core/project].
Operation "operations/acat.p2-60565640195-f37dc7fe-b093-4451-9b12-934649e2a435" finished successfully.

Attribuez les rôles Identity and Access Management (IAM) "Utilisateur Cloud AI Companion" et "Lecteur Service Usage" au compte UTILISATEUR. L'API Cloud Companion se trouve derrière les fonctionnalités de l'IDE et de la console que nous allons utiliser. L'autorisation "Lecteur de l'utilisation du service" sert de vérification rapide avant d'activer l'UI dans la console (afin que l'UI Duet ne s'affiche que dans les projets dans lesquels l'API est activée).

gcloud projects add-iam-policy-binding  ${PROJECT_ID} \
--member=user:${USER} --role=roles/cloudaicompanion.user

gcloud projects add-iam-policy-binding  ${PROJECT_ID} \
--member=user:${USER} --role=roles/serviceusage.serviceUsageViewer

La sortie ressemble à ceci:

...
- members:
  - user:<YOUR USER ACCOUNT>
  role: roles/cloudaicompanion.user

...
- members:
  - user:<YOUR USER ACCOUNT>
  role: roles/serviceusage.serviceUsageViewer

Via la console Cloud

Pour activer l'API, accédez à la page de l'API Cloud AI Companion dans la console Google Cloud.

Dans le sélecteur de projet, choisissez un projet.

Cliquez sur Activer.

La page est mise à jour et affiche l'état Activé. Duet AI est désormais disponible dans le projet Google Cloud sélectionné pour tous les utilisateurs disposant des rôles IAM requis.

Pour attribuer les rôles IAM requis pour utiliser Duet AI, accédez à la page IAM.

Dans la colonne Compte principal, recherchez le compte UTILISATEUR pour lequel vous souhaitez activer l'accès à Duet AI, puis cliquez sur l'icône en forme de crayon ✏️ Modifier le compte principal sur cette ligne.

Dans le volet d'accès Modifier, cliquez sur Ajouter un autre rôle.

Dans "Sélectionner un rôle", sélectionnez Utilisateur Cloud AI Companion.

Cliquez sur Ajouter un autre rôle et sélectionnez Lecteur Service Usage.

Cliquez sur Enregistrer.

Configurer l'IDE

Les développeurs peuvent choisir parmi une variété d'IDE qui répondent le mieux à leurs besoins. L'aide au codage de Duet AI est disponible dans plusieurs IDE tels que Visual Studio Code, les IDE JetBrains (IntelliJ, PyCharm, GoLand, WebStorm, etc.), Cloud Workstations et l'éditeur Cloud Shell.

Dans cet atelier, vous pouvez utiliser Cloud Workstations ou l'éditeur Cloud Shell.

Cet atelier utilise l'éditeur Cloud Shell.

Notez que la configuration de Cloud Workstations peut prendre entre 20 et 30 minutes.

Pour l'utiliser immédiatement, utilisez l'éditeur Cloud Shell.

Ouvrez l'éditeur Cloud Shell en cliquant sur l'icône en forme de crayon ✏️ dans la barre de menu supérieure de Cloud Shell.

L'éditeur Cloud Shell possède une interface utilisateur et une expérience utilisateur très semblables à ceux de VSCode.

d6a6565f83576063.png

Cliquez sur les touches CTRL (sous Windows)/CMD (sous Mac) + , (virgule) pour accéder au volet "Paramètres".

Dans la barre de recherche, saisissez "duet ai".

Vérifiez ou activez Cloudcode › Duet AI: Activer et Cloudcode › Duet AI › Suggestions intégrées: Activer automatiquement

111b8d587330ec74.png

Dans la barre d'état inférieure, cliquez sur Cloud Code - Connexion et suivez le workflow de connexion.

Si vous êtes déjà connecté, la barre d'état indique Cloud Code – Aucun projet.

Cliquez sur "Cloud Code - No project" (Cloud Code – Aucun projet). Un volet déroulant "Action" s'affiche en haut de l'écran. Cliquez sur Sélectionner un projet Google Cloud.

3241a59811e3c84a.png

Commencez à saisir votre ID de projet. Votre projet devrait apparaître dans la liste.

c5358fc837588fe.png

Sélectionnez votre ID de projet dans la liste des projets.

La barre d'état inférieure est mise à jour pour afficher l'ID de votre projet. Si ce n'est pas le cas, vous devrez peut-être actualiser l'onglet de l'éditeur Cloud Shell.

Cliquez sur l'icône Duet AI d97fc4e7b594c3af.png dans la barre de menu de gauche. La fenêtre de chat Duet AI s'affiche. Si vous recevez le message "Select GCP Project" (Sélectionner un projet GCP). Cliquez sur le projet et sélectionnez-le à nouveau.

La fenêtre de chat Duet AI s'affiche désormais

781f888360229ca6.png

3. Configurer l'infrastructure

d3234d237f00fdbb.png

Pour exécuter le nouveau service de livraison dans GCP, vous avez besoin des ressources GCP suivantes:

  1. Une instance Cloud SQL, avec une base de données
  2. Un cluster GKE pour exécuter le service conteneurisé
  3. Un dépôt Artifact Registry pour stocker l'image Docker
  4. Un dépôt Cloud Source Repositories pour le code

Dans le terminal Cloud Shell, clonez le dépôt suivant et exécutez les commandes suivantes pour configurer l'infrastructure dans votre projet GCP.

# Set your project
export PROJECT_ID=<INSERT_YOUR_PROJECT_ID>
gcloud config set core/project ${PROJECT_ID}

# Enable Cloudbuild and grant Cloudbuild SA owner role 
export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format 'value(projectNumber)')
gcloud services enable cloudbuild.googleapis.com
gcloud projects add-iam-policy-binding ${PROJECT_ID} --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com --role roles/owner

# Clone the repo
git clone https://github.com/duetailabs/dev.git ~/duetaidev
cd ~/duetaidev

# Run Cloudbuild to create the necessary resources
gcloud builds submit --substitutions=_PROJECT_ID=${PROJECT_ID}

# To destroy all GCP resources, run the following
# gcloud builds submit --substitutions=_PROJECT_ID=${PROJECT_ID} --config=cloudbuild_destroy.yaml

4. Développer un service Python Flask

9745ba5c70782e76.png

Le service que nous allons créer se composera au final des fichiers suivants. Vous n'avez pas besoin de créer ces fichiers maintenant. Vous allez les créer un par un en suivant les instructions ci-dessous:

  1. package-service.yaml : spécification OpenAPI pour le service de packages qui contient des données telles que la hauteur, la largeur, le poids et des instructions de manipulation spéciales.
  2. data_model.py : modèle de données pour la spécification de l'API package-service. Crée également la table packages dans la base de données product_details.
  3. connect_connector.py : connexion Cloud SQL (définit un moteur, une session et un ORM de base)
  4. db_init.py : génère des exemples de données dans la table packages.
  5. main.py : service Python Flask avec un point de terminaison GET pour récupérer les détails du package à partir des données packages en fonction de product_id.
  6. test.py - Test unitaire
  7. requirement.txt : configuration requise pour Python
  8. Dockerfile - Pour conteneuriser l'application

Si vous rencontrez des problèmes persistants au cours des exercices, sachez que vous trouverez tous les fichiers finaux dans l'ANNEXE de cet atelier de programmation, à titre de référence.

À l'étape précédente, vous avez créé un dépôt Cloud Source Repositories. Clonez le dépôt. Vous allez créer les fichiers de l'application dans le dossier du dépôt cloné.

Dans le terminal Cloud Shell, exécutez la commande suivante pour cloner le dépôt.

cd ~
gcloud source repos clone shipping shipping
cd ~/shipping 

Ouvrez la barre latérale de chat Duet AI depuis le menu de gauche de l'éditeur Cloud Shell. L'icône ressemble à 8b135a000b259175.png. Vous pouvez désormais utiliser Duet AI pour l'assistance au codage.

package-service.yaml

Lorsque aucun fichier n'est ouvert, demandez à Duet de générer une spécification OpenAPI pour le service de livraison.

Invite 1: générez une spécification OpenAPI yaml pour un service fournissant des informations sur la livraison et le colis en fonction d'un ID produit numérique. Le service doit inclure des informations sur la hauteur, la largeur, la profondeur et le poids des colis, ainsi que toute instruction de manipulation particulière.

ba12626f491a1204.png

Trois options sont listées en haut à droite de la fenêtre du code généré.

Vous pouvez COPY 71194556d8061dae.pngle code et le coller dans un fichier.

Vous pouvez ADD df645de8c65607a.png le code dans le fichier actuellement ouvert dans l'éditeur.

Vous pouvez également OPEN a4c7ed6d845df343.png le code dans un nouveau fichier.

Cliquez sur OPEN a4c7ed6d845df343.png le code dans un nouveau fichier.

Cliquez sur CTRL/CMD + s pour enregistrer le fichier et enregistrez-le dans le dossier de l'application sous le nom package-service.yaml. Cliquez sur OK.

f6ebd5b836949366.png

Le fichier final se trouve dans la section ANNEXE de cet atelier de programmation. Si ce n'est pas le cas, apportez manuellement les modifications appropriées.

Vous pouvez également essayer différentes requêtes pour voir les réponses de Duet AI.

Pour réinitialiser l'historique des discussions de Duet AI, cliquez sur l'icône Corbeille f574ca2c1e114856.png en haut de la barre latérale de Duet AI.

data_model.py

Créez ensuite le fichier Python du modèle de données du service basé sur la spécification OpenAPI.

Une fois le fichier package-service.yaml ouvert, saisissez la requête suivante.

Invite 1: À l'aide de l'ORM Python sqlalchemy, générez un modèle de données pour ce service d'API. Incluez également une fonction distincte et un point d'entrée principal qui crée les tables de base de données.

b873a6a28bd28ca1.png

Examinons chaque partie générée. Duet AI reste un assistant. Bien qu'il puisse vous aider à rédiger rapidement du code, vous devez continuer à examiner le contenu généré et à le comprendre au fur et à mesure.

Tout d'abord, il existe une classe appelée Package de genre Base qui définit le modèle de données de la base de données packages comme suit:

class Package(Base):
    __tablename__ = 'packages'

    id = Column(Integer, primary_key=True)
    product_id = Column(String(255))
    height = Column(Float)
    width = Column(Float)
    depth = Column(Float)
    weight = Column(Float)
    special_handling_instructions = Column(String(255))

Ensuite, vous avez besoin d'une fonction qui crée la table dans la base de données, comme ceci:

def create_tables(engine):
    Base.metadata.create_all(engine)

Enfin, vous avez besoin d'une fonction principale qui exécute la fonction create_tables pour créer la table dans la base de données CloudSQL, comme suit:

if __name__ == '__main__':
    from sqlalchemy import create_engine

    engine = create_engine('sqlite:///shipping.db')
    create_tables(engine)

    print('Tables created successfully.')

Notez que la fonction main crée un moteur à l'aide d'une base de données sqlite locale. Pour utiliser Cloud SQL, vous devez le modifier. Vous le ferez un peu plus tard.

Utiliser OPEN a4c7ed6d845df343.png le code dans un nouveau workflow de fichier, comme précédemment. Enregistrez le code dans un fichier nommé data_model.py (notez le trait de soulignement dans le nom et non un tiret).

Pour réinitialiser l'historique des discussions de Duet AI, cliquez sur l'icône Corbeille f574ca2c1e114856.png en haut de la barre latérale de Duet AI.

connect-connector.py

Créez le connecteur Cloud SQL.

Une fois le fichier data_model.py ouvert, saisissez les invites suivantes.

Invite 1: À l'aide de la bibliothèque cloud-sql-python-connector, générez une fonction qui initialise un pool de connexions pour une instance Cloud SQL de Postgres.

ed05cb6ff85d34c5.png

Notez que la réponse n'utilise pas la bibliothèque cloud-sql-python-connector. Vous pouvez affiner les requêtes et donner un coup de pouce à Duet en ajoutant des détails au même fil de discussion.

Utilisons une autre requête.

Invite 2: Vous devez utiliser la bibliothèque cloud-sql-python-connector.

d09095b44dde35bf.png

Assurez-vous qu'il utilise la bibliothèque cloud-sql-python-connector.

Utiliser OPEN a4c7ed6d845df343.png le code dans un nouveau workflow de fichier, comme précédemment. Enregistrez le code dans un fichier nommé connect_conector.py. Vous devrez peut-être importer manuellement la bibliothèque pg8000. Veuillez consulter le fichier ci-dessous.

Effacez l'historique des discussions de Duet AI et, une fois le fichier connect_connector.py ouvert, générez l'ORM DB engine, sessionmaker et base à utiliser dans l'application.

Invite 1: Créer un moteur, une classe sessionmaker et un ORM de base à l'aide de la méthode connect_with_connector

6e4214b72ab13a63.png

La réponse peut ajouter engine, Session et Base au fichier connect_connector.py.

Le fichier final se trouve dans la section ANNEXE de cet atelier de programmation. Si ce n'est pas le cas, apportez manuellement les modifications appropriées.

Vous pouvez également essayer différentes requêtes pour voir les variations potentielles des réponses de Duet AI.

Pour réinitialiser l'historique des discussions de Duet AI, cliquez sur l'icône Corbeille f574ca2c1e114856.png en haut de la barre latérale de Duet AI.

Mise à jour de data_model.py...

Vous devez utiliser le moteur créé à l'étape précédente (dans le fichier connect_connector.py) pour créer une table dans la base de données CloudSQL.

Effacer l'historique des discussions de Duet AI. Ouvrez le fichier data_model.py. Essayez l'invite suivante.

Invite 1: Dans la fonction principale, importez et utilisez le moteur à partir du fichier connect_connector.py

2e768c9b6c523b9a.png

Vous devriez obtenir une réponse indiquant l'importation de engine à partir de connect_connector (pour Cloud SQL). create_table utilise ce moteur (au lieu de la base de données locale sqlite par défaut).

Mettez à jour le fichier data_model.py.

Le fichier final se trouve dans la section ANNEXE de cet atelier de programmation. Si ce n'est pas le cas, apportez manuellement les modifications appropriées.

Vous pouvez également essayer différentes requêtes pour voir les différentes réponses de Duet AI.

Pour réinitialiser l'historique des discussions de Duet AI, cliquez sur l'icône Corbeille f574ca2c1e114856.png en haut de la barre latérale de Duet AI.

requirements.txt

Créez un fichier requirements.txt pour l'application.

Ouvrez connect_connector.py et le fichier data_model.py, puis saisissez l'invite suivante.

Invite 1: Générez un fichier d'exigences pip pour ce modèle de données et ce service

Invite 2: Générez un fichier d'exigences pip pour ce modèle de données et ce service à l'aide des dernières versions

69fae373bc5c6a18.png

Vérifiez que les noms et les versions sont corrects. Par exemple, dans la réponse ci-dessus, le nom et la version de google-cloud-sql-connecter sont incorrects. Corrigez manuellement les versions, puis créez un fichier requirements.txt semblable à celui-ci:

cloud-sql-python-connector==1.2.4
sqlalchemy==1.4.36
pg8000==1.22.0

Dans le terminal de commande, exécutez la commande suivante:

pip3 install -r requirements.txt

Pour réinitialiser l'historique des discussions de Duet AI, cliquez sur l'icône Corbeille f574ca2c1e114856.png en haut de la barre latérale de Duet AI.

Créer une table des packages dans Cloud SQL

Définissez les variables d'environnement du connecteur de base de données Cloud SQL.

export INSTANCE_NAME=$(gcloud sql instances list --format='value(name)')
export INSTANCE_CONNECTION_NAME=$(gcloud sql instances describe ${INSTANCE_NAME} --format="value(connectionName)")
export DB_USER=evolution
export DB_PASS=evolution
export DB_NAME=product_details

Exécutez maintenant data_model.py.

python data_model.py

Le résultat ressemble à ce qui suit (vérifiez le code pour voir ce qui est réellement attendu):

Tables created successfully.

Connectez-vous à l'instance Cloud SQL et vérifiez que la base de données a été créée.

gcloud sql connect ${INSTANCE_NAME} --user=evolution --database=product_details

Après avoir saisi le mot de passe (également appelé évolution), récupérez les tables.

product_details=> \dt

Le résultat ressemble à ce qui suit :

           List of relations
 Schema |   Name   | Type  |   Owner   
--------+----------+-------+-----------
 public | packages | table | evolution
(1 row)

Vous pouvez également vérifier le modèle de données et les détails de la table.

product_details=> \d+ packages

Le résultat ressemble à ce qui suit :

                                                                        Table "public.packages"
            Column             |       Type        | Collation | Nullable |               Default                | Storage  | Compression | Stats target | Description 
-------------------------------+-------------------+-----------+----------+--------------------------------------+----------+-------------+--------------+-------------
 id                            | integer           |           | not null | nextval('packages_id_seq'::regclass) | plain    |             |              | 
 product_id                    | integer           |           | not null |                                      | plain    |             |              | 
 height                        | double precision  |           | not null |                                      | plain    |             |              | 
 width                         | double precision  |           | not null |                                      | plain    |             |              | 
 depth                         | double precision  |           | not null |                                      | plain    |             |              | 
 weight                        | double precision  |           | not null |                                      | plain    |             |              | 
 special_handling_instructions | character varying |           |          |                                      | extended |             |              | 
Indexes:
    "packages_pkey" PRIMARY KEY, btree (id)
Access method: heap

Saisissez \q pour quitter Cloud SQL.

db_init.py

Ajoutons maintenant des exemples de données à la table packages.

Effacer l'historique des discussions de Duet AI. Une fois le fichier data_model.py ouvert, essayez les invites suivantes.

Invite 1: Générez une fonction qui crée 10 exemples de lignes de packages et les valide dans la table des packages.

Invite 2: À l'aide de la session de connect_connector, générez une fonction qui crée 10 exemples de lignes de packages et les valide dans la table des packages

34a9afc5f04ba5.png

Utiliser OPEN a4c7ed6d845df343.png le code dans un nouveau workflow de fichier, comme précédemment. Enregistrez le code dans un fichier nommé db_init.py.

Le fichier final se trouve dans la section ANNEXE de cet atelier de programmation. Si ce n'est pas le cas, apportez manuellement les modifications appropriées.

Vous pouvez également essayer différentes requêtes pour voir les différentes réponses de Duet AI.

Pour réinitialiser l'historique des discussions de Duet AI, cliquez sur l'icône Corbeille f574ca2c1e114856.png en haut de la barre latérale de Duet AI.

Créer des exemples de données de packages

Exécutez db_init.py à partir de la ligne de commande.

python db_init.py

Le résultat ressemble à ce qui suit :

Packages created successfully.

Connectez-vous à nouveau à l'instance Cloud SQL et vérifiez que les exemples de données ont bien été ajoutés à la table des packages.

Connectez-vous à l'instance Cloud SQL et vérifiez que la base de données a été créée.

gcloud sql connect ${INSTANCE_NAME} --user=evolution --database=product_details

Après avoir saisi le mot de passe (également appelé évolution), récupérez toutes les données de la table des packages.

product_details=> SELECT * FROM packages;

Le résultat ressemble à ce qui suit :

 id | product_id | height | width | depth | weight |   special_handling_instructions   
----+------------+--------+-------+-------+--------+-----------------------------------
  1 |          0 |     10 |    10 |    10 |     10 | No special handling instructions.
  2 |          1 |     10 |    10 |    10 |     10 | No special handling instructions.
  3 |          2 |     10 |    10 |    10 |     10 | No special handling instructions.
  4 |          3 |     10 |    10 |    10 |     10 | No special handling instructions.
  5 |          4 |     10 |    10 |    10 |     10 | No special handling instructions.
  6 |          5 |     10 |    10 |    10 |     10 | No special handling instructions.
  7 |          6 |     10 |    10 |    10 |     10 | No special handling instructions.
  8 |          7 |     10 |    10 |    10 |     10 | No special handling instructions.
  9 |          8 |     10 |    10 |    10 |     10 | No special handling instructions.
 10 |          9 |     10 |    10 |    10 |     10 | No special handling instructions.
(10 rows)

Saisissez \q pour quitter Cloud SQL.

main.py

Une fois les fichiers data_model.py, package-service.yaml et connect_connector.py ouverts, créez un main.py pour l'application.

Invite 1: À l'aide de la bibliothèque Python flask – Créez une implémentation qui utilise des points de terminaison HTTP REST pour ce service

Invite 2: À l'aide de la bibliothèque Python flask, créez une implémentation qui utilise des points de terminaison HTTP REST pour ce service. importer et utiliser SessionMaker de connect_conector.py vers pour les données de packages.

Invite 3: À l'aide de la bibliothèque Python flask, créez une implémentation qui utilise des points de terminaison HTTP REST pour ce service. importer et utiliser le package à partir de data_model.py et le SessionMaker de connect_conector.py vers pour les données de packages.

Invite 4: À l'aide de la bibliothèque Python flask – Créez une implémentation qui utilise des points de terminaison HTTP REST pour ce service. importer et utiliser le package à partir de data_model.py et le SessionMaker de connect_conector.py vers pour les données de packages. Utiliser l'adresse IP de l'hôte 0.0.0.0 pour app.run

6d794fc52a90e6ae.png

Mettez à jour les exigences pour main.py.

Invite: Créez un fichier d'exigences pour main.py

1cc0b318d2d4ca2f.png

Ajouter au fichier requirements.txt. Veillez à utiliser la version 3.0.0 de Flask.

Utiliser OPEN a4c7ed6d845df343.png le code dans un nouveau workflow de fichier, comme précédemment. Enregistrez le code dans un fichier nommé main.py.

Le fichier final se trouve dans la section ANNEXE de cet atelier de programmation. Si ce n'est pas le cas, apportez manuellement les modifications appropriées.

Pour réinitialiser l'historique des discussions de Duet AI, cliquez sur l'icône Corbeille f574ca2c1e114856.png en haut de la barre latérale de Duet AI.

5. Tester et exécuter l'application

Installez la configuration requise.

pip3 install -r requirements.txt

Exécutez main.py.

python main.py

Le résultat ressemble à ce qui suit :

 * Serving Flask app 'main'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000
 * Running on http://10.88.0.3:5000
Press CTRL+C to quit

À partir d'un deuxième terminal, testez le point de terminaison /packages/<product_id>.

curl localhost:5000/packages/1

Le résultat ressemble à ce qui suit :

{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}

Vous pouvez également tester n'importe quel autre ID produit dans vos exemples de données.

Saisissez CTRL_C pour quitter le conteneur Docker en cours d'exécution dans le terminal.

Générer des tests unitaires

Ouvrez le fichier main.py et générez des tests unitaires.

Invite 1: Générez des tests unitaires.

e861e5b63e1b2657.png

Utiliser OPEN a4c7ed6d845df343.png le code dans un nouveau workflow de fichier, comme précédemment. Enregistrez le code dans un fichier nommé test.py.

Dans la fonction test_get_package, un product_id doit être défini. Vous pouvez l'ajouter manuellement.

Le fichier final se trouve dans la section ANNEXE de cet atelier de programmation. Si ce n'est pas le cas, apportez manuellement les modifications appropriées.

Pour réinitialiser l'historique des discussions de Duet AI, cliquez sur l'icône Corbeille f574ca2c1e114856.png en haut de la barre latérale de Duet AI.

L'exécution des tests unitaires

Exécutez le test unitaire.

python test.py

Le résultat ressemble à ce qui suit :

.
----------------------------------------------------------------------
Ran 1 test in 1.061s

OK

Fermez tous les fichiers dans l'éditeur Cloud Shell et effacez l'historique des discussions en cliquant sur l'icône Corbeille 1ecccfe10d6c540.png dans la barre d'état en haut.

Dockerfile

Créez un Dockerfile pour cette application.

Ouvrez main.py et essayez les invites suivantes.

Invite 1: Générez un Dockerfile pour cette application.

Invite 2: Générez un Dockerfile pour cette application. Copiez tous les fichiers dans le conteneur.

9c473caea437a5c3.png

Vous devez également définir ENVARS pour INSTANCE_CONNECTION_NAME, DB_USER, DB_PASS et DB_NAME. Vous pouvez le faire manuellement. Votre Dockerfile doit se présenter comme suit:

FROM python:3.10-slim

WORKDIR /app

COPY . ./

RUN pip install -r requirements.txt

# Add these manually for your project
ENV INSTANCE_CONNECTION_NAME=YOUR_INSTANCE_CONNECTION_NAME
ENV DB_USER=evolution
ENV DB_PASS=evolution
ENV DB_NAME=product_details

CMD ["python", "main.py"]

Utiliser OPEN a4c7ed6d845df343.png le code dans un nouveau workflow de fichier, comme précédemment. Enregistrez le code dans un fichier nommé "Dockerfile".

Le fichier final se trouve dans la section ANNEXE de cet atelier de programmation. Si ce n'est pas le cas, apportez manuellement les modifications appropriées.

Exécuter l'application localement

Lorsque Dockerfile est ouvert, essayez l'invite suivante.

Invite 1: Comment exécuter localement un conteneur à l'aide de ce Dockerfile ?

570fd5c296ca8c83.png

Suivez les instructions.

# Build
docker build -t shipping .
# And run
docker run -p 5000:5000 -it shipping

Le résultat ressemble à ce qui suit :

 * Serving Flask app 'main'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000
 * Running on http://172.17.0.2:5000
Press CTRL+C to quit

À partir d'une deuxième fenêtre de terminal, accédez au conteneur.

curl localhost:5000/packages/1

Le résultat ressemble à ce qui suit :

{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}

L'application conteneurisée fonctionne.

Saisissez CTRL_C pour quitter le conteneur Docker en cours d'exécution dans le terminal.

Créer une image de conteneur dans Artifact Registry

Créez l'image de conteneur et transférez-la vers Artifact Registry.

cd ~/shipping
gcloud auth configure-docker us-central1-docker.pkg.dev
docker build -t us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping .
docker push us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping

Le conteneur d'application se trouve maintenant dans us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping, qui peut être déployé sur GKE.

6. Déployer l'application sur le cluster GKE

Un cluster GKE Autopilot a été créé lorsque vous avez créé les ressources GCP pour cet atelier. Se connecter au cluster GKE

gcloud container clusters get-credentials gke1 \
    --region=us-central1

Annotez le compte de service Kubernetes par défaut avec le compte de service Google.

kubectl annotate serviceaccount default iam.gke.io/gcp-service-account=cloudsqlsa@${PROJECT_ID}.iam.gserviceaccount.com

Le résultat ressemble à ce qui suit :

serviceaccount/default annotated

Préparez et appliquez le fichier k8s.yaml.

cp ~/duetaidev/k8s.yaml_tmpl ~/shipping/.
export INSTANCE_NAME=$(gcloud sql instances list --format='value(name)')
export INSTANCE_CONNECTION_NAME=$(gcloud sql instances describe ${INSTANCE_NAME} --format="value(connectionName)")
export IMAGE_REPO=us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping
envsubst < ~/shipping/k8s.yaml_tmpl > k8s.yaml
kubectl apply -f k8s.yaml

Le résultat ressemble à ce qui suit :

deployment.apps/shipping created
service/shipping created

Attendez que les pods soient en cours d'exécution et qu'une adresse IP d'équilibreur de charge externe soit attribuée au service.

kubectl get pods
kubectl get service shipping

Le résultat ressemble à ce qui suit :

# kubectl get pods
NAME                      READY   STATUS    RESTARTS   AGE
shipping-f5d6f8d5-56cvk   1/1     Running   0          4m47s
shipping-f5d6f8d5-cj4vv   1/1     Running   0          4m48s
shipping-f5d6f8d5-rrdj2   1/1     Running   0          4m47s

# kubectl get service shipping
NAME       TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)        AGE
shipping   LoadBalancer   34.118.225.125   34.16.39.182   80:30076/TCP   5m41s

Pour les clusters GKE Autopilot, attendez quelques instants que les ressources soient prêtes.

Accédez au service via l'adresse EXTERNAL-IP.

export EXTERNAL_IP=$(kubectl get svc shipping --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl http://${EXTERNAL_IP}/packages/1

Le résultat ressemble à ce qui suit :

{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}

7. Autres informations: résoudre les problèmes liés à la demande

Supprimez le rôle IAM du client Cloud SQL du compte de service cloudsqlsa. Cela provoque une erreur de connexion à la base de données Cloud SQL.

gcloud projects remove-iam-policy-binding ${PROJECT_ID} \
    --member="serviceAccount:cloudsqlsa@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/cloudsql.client"

Redémarrez le pod de livraison.

kubectl rollout restart deployment shipping

Une fois le pod redémarré, essayez à nouveau d'accéder au service shipping.

export EXTERNAL_IP=$(kubectl get svc shipping --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl http://${EXTERNAL_IP}/packages/1 

Le résultat ressemble à ce qui suit :

...
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>

Inspectez les journaux en accédant à Kubernetes Engine > Charges de travail

d225b1916c829167.png

Cliquez sur le déploiement shipping, puis sur l'onglet Journaux.

1d0459141483d6a7.png

Cliquez sur l'icône Afficher dans l'explorateur de journaux df8b9d19a9fe4c73.png à droite de la barre d'état. Une nouvelle fenêtre Explorateur de journaux s'ouvre.

e86d1c265e176bc4.png

Cliquez sur l'une des entrées d'erreur Traceback, puis sur Expliquer cette entrée de journal.

d6af045cf03008bc.png

Vous pouvez lire l'explication de l'erreur.

Laissons à présent Duet AI pour vous aider à corriger l'erreur.

Essayez l'invite suivante.

Invite 1: Aidez-moi à résoudre cette erreur

9288dd6045369167.png

Saisissez le message d'erreur dans l'invite.

Invite 2: Interdit: le compte principal IAM authentifié ne semble pas autorisé à envoyer une requête API. Validez l'API Cloud SQL Admin est activé dans votre projet GCP et dans votre "client Cloud SQL" ; a été attribué au compte principal IAM

f1e64fbdc435d31c.png

Et ensuite.

Invite 3: Comment attribuer le rôle "Client Cloud SQL" à un compte de service Google à l'aide de gcloud ?

bb8926b995a8875c.png

Attribuez le rôle "Client Cloud SQL" à cloudsqlsa.

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member="serviceAccount:cloudsqlsa@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/cloudsql.client"

Patientez quelques instants, puis réessayez d'accéder à l'application.

export EXTERNAL_IP=$(kubectl get svc shipping --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl http://${EXTERNAL_IP}/packages/1

Le résultat ressemble à ce qui suit :

{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}

Vous avez utilisé Duet AI dans Cloud Logging, l'explorateur de journaux et la fonctionnalité Explication de journaux pour résoudre le problème.

8. Conclusion

Félicitations ! Vous avez terminé cet atelier de programmation.

Dans cet atelier de programmation, vous avez appris ce qui suit:

  1. Activez Duet AI dans votre projet GCP et configurez-le pour l'utiliser dans un IDE et dans la console Cloud.
  2. Utilisez Duet AI pour générer, compléter et expliquer du code.
  3. Utiliser Duet AI pour expliquer et résoudre un problème lié à une application
  4. Fonctionnalités de Duet AI telles que le chat IDE et le chat multitours, la génération de code par chat ou intégrée, des actions intelligentes comme l'explication du code et la confirmation de la récitation, et plus encore.

9. Annexe

package-service.yaml

swagger: "2.0"
info:
 title: Shipping and Package Information API
 description: This API provides information about shipping and packages.
 version: 1.0.0
host: shipping.googleapis.com
schemes:
 - https
produces:
 - application/json
paths:
 /packages/{product_id}:
   get:
     summary: Get information about a package
     description: This method returns information about a package, including its height, width, depth, weight, and any special handling instructions.
     parameters:
       - name: product_id
         in: path
         required: true
         type: integer
         format: int64
     responses:
       "200":
         description: A successful response
         schema:
           type: object
           properties:
             height:
               type: integer
               format: int64
             width:
               type: integer
               format: int64
             depth:
               type: integer
               format: int64
             weight:
               type: integer
               format: int64
             special_handling_instructions:
               type: string
       "404":
         description: The product_id was not found

data_model.py

from sqlalchemy import Column, Integer, String, Float
from sqlalchemy.ext.declarative import declarative_base

from connect_connector import engine

Base = declarative_base()

class Package(Base):
    __tablename__ = 'packages'

    id = Column(Integer, primary_key=True)
    product_id = Column(Integer, nullable=False)
    height = Column(Float, nullable=False)
    width = Column(Float, nullable=False)
    depth = Column(Float, nullable=False)
    weight = Column(Float, nullable=False)
    special_handling_instructions = Column(String, nullable=True)

def create_tables():
    Base.metadata.create_all(engine)

if __name__ == '__main__':
    create_tables()

    print('Tables created successfully.')

connect_connector.py

import os

from google.cloud.sql.connector import Connector, IPTypes
import sqlalchemy

# You may need to manually import pg8000 and Base as follows
import pg8000
from sqlalchemy.ext.declarative import declarative_base


def connect_with_connector() -> sqlalchemy.engine.base.Engine:
   """Initializes a connection pool for a Cloud SQL instance of Postgres."""
   # Note: Saving credentials in environment variables is convenient, but not
   # secure - consider a more secure solution such as
   # Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
   # keep secrets safe.
   instance_connection_name = os.environ[
       "INSTANCE_CONNECTION_NAME"
   ]  # e.g. 'project:region:instance'
   db_user = os.environ["DB_USER"]  # e.g. 'my-database-user'
   db_pass = os.environ["DB_PASS"]  # e.g. 'my-database-password'
   db_name = os.environ["DB_NAME"]  # e.g. 'my-database'

   ip_type = IPTypes.PRIVATE if os.environ.get("PRIVATE_IP") else IPTypes.PUBLIC

   connector = Connector()

   def getconn() -> sqlalchemy.engine.base.Engine:
       conn: sqlalchemy.engine.base.Engine = connector.connect(
           instance_connection_name,
           "pg8000",
           user=db_user,
           password=db_pass,
           db=db_name,
           ip_type=ip_type,
       )
       return conn

   pool = sqlalchemy.create_engine(
       "postgresql+pg8000://",
       creator=getconn,
       # ...
   )
   return pool

# Create a connection pool
engine = connect_with_connector()

# Create a sessionmaker class to create new sessions
SessionMaker = sqlalchemy.orm.sessionmaker(bind=engine)

# Create a Base class for ORM
# You may need to manually fix the following
Base = declarative_base()

db_init.py

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from connect_connector import engine

from data_model import Package

def create_packages():
    # Create a session
    session = sessionmaker(bind=engine)()

    # Create 10 sample packages
    for i in range(10):
        package = Package(
            product_id=i,
            height=10.0,
            width=10.0,
            depth=10.0,
            weight=10.0,
            special_handling_instructions="No special handling instructions."
        )

        # Add the package to the session
        session.add(package)

    # Commit the changes
    session.commit()

if __name__ == '__main__':
    create_packages()

    print('Packages created successfully.')

main.py

from flask import Flask, request, jsonify

from data_model import Package
from connect_connector import SessionMaker

app = Flask(__name__)

session_maker = SessionMaker()

@app.route("/packages/<int:product_id>", methods=["GET"])
def get_package(product_id):
  """Get information about a package."""

  session = session_maker

  package = session.query(Package).filter(Package.product_id == product_id).first()

  if package is None:
    return jsonify({"message": "Package not found."}), 404

  return jsonify(
      {
          "height": package.height,
          "width": package.width,
          "depth": package.depth,
          "weight": package.weight,
          "special_handling_instructions": package.special_handling_instructions,
      }
  ), 200

if __name__ == "__main__":
  app.run(host="0.0.0.0")

test.py

import unittest

from data_model import Package
from connect_connector import SessionMaker

from main import app

class TestPackage(unittest.TestCase):

    def setUp(self):
        self.session_maker = SessionMaker()

    def tearDown(self):
        self.session_maker.close()

    def test_get_package(self):
        """Test the `get_package()` function."""

        package = Package(
        product_id=11, # Ensure that the product_id different from the sample data
        height=10,
        width=10,
        depth=10,
        weight=10,
        special_handling_instructions="Fragile",
        )

        session = self.session_maker

        session.add(package)
        session.commit()

        response = app.test_client().get("/packages/11")

        self.assertEqual(response.status_code, 200)

        self.assertEqual(
            response.json,
            {
                "height": 10,
                "width": 10,
                "depth": 10,
                "weight": 10,
                "special_handling_instructions": "Fragile",
            },
        )

if __name__ == "__main__":
    unittest.main()

requirements.txt

cloud-sql-python-connector==1.2.4
sqlalchemy==1.4.36
pg8000==1.22.0
Flask==3.0.0
gunicorn==20.1.0
psycopg2-binary==2.9.3

Dockerfile

FROM python:3.10-slim

WORKDIR /app

COPY . ./

RUN pip install -r requirements.txt

# Add these manually for your project
ENV INSTANCE_CONNECTION_NAME=YOUR_INSTANCE_CONNECTION_NAME
ENV DB_USER=evolution
ENV DB_PASS=evolution
ENV DB_NAME=product_details

CMD ["python", "main.py"]