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

1. Objectifs

L'objectif de cet atelier est de fournir une formation pratique sur Duet AI aux utilisateurs et aux professionnels.

Dans cet atelier de programmation, vous allez apprendre les points suivants :

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

Présentation

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. Ils doivent ajouter un service à l'application d'e-commerce existante (qui est composée de plusieurs services). Le nouveau service fournit des informations supplémentaires (dimensions, poids, etc.) sur les produits du catalogue. Ce service vous permettra de bénéficier de frais de port plus avantageux en fonction des dimensions et du poids des produits.

Comme le développeur est nouveau dans l'entreprise, il utilisera Duet AI pour générer, expliquer et documenter le code.

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

Une fois l'application déployée sur GCP, un opérateur d'application/SRE utilisera Duet AI (et Cloud Ops) pour résoudre un problème lié au nouveau service.

Persona

L'atelier couvre la persona suivante :

  1. Développeur d'applications : des connaissances en programmation et en développement de logiciels 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 requises pour exécuter cette application sont disponibles sur cette page. 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 (gcloud ou outils d'IaC comme Terraform) ou 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 IAM (Identity and Access Management) "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}

Le résultat ressemble à ce qui suit :

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 de Cloud AI Companion" et "Lecteur Service Usage" au compte UTILISATEUR. L'API Cloud Companion est à la base des fonctionnalités que nous allons utiliser dans l'IDE et la console. L'autorisation "Lecteur de l'utilisation du service" est utilisée pour une vérification rapide avant d'activer l'UI dans la console (afin que l'UI Duet n'apparaisse 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

Le résultat ressemble à ce qui suit :

...
- 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 projets, choisissez un projet.

Cliquez sur Activer.

La page s'actualise 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 l'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 Modifier l'accès, 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, puis sélectionnez Lecteur Service Usage.

Cliquez sur Enregistrer.

Configurer l'IDE

Les développeurs peuvent choisir parmi différents IDE celui qui répond le mieux à leurs besoins. L'assistance au code Duet AI est disponible dans plusieurs IDE, tels que Visual Studio Code, JetBrains IDEs (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'interface utilisateur et l'expérience utilisateur de l'éditeur Cloud Shell sont très similaires à celles de VSCode.

d6a6565f83576063.png

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

Dans la barre de recherche, saisissez "Duet AI".

Assurez-vous que les options Cloudcode › Duet AI : Activer et Cloudcode › Duet AI › Suggestions intégrées : Activer automatiquement sont activées.

111b8d587330ec74.png

Dans la barre d'état en bas de l'écran, cliquez sur Cloud Code - Se connecter et suivez la procédure de connexion.

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

Cliquez sur "Cloud Code - Aucun projet". Un volet déroulant d'actions s'affiche en haut de l'écran. Cliquez sur Sélectionner un projet Google Cloud.

3241a59811e3c84a.png

Commencez à saisir l'ID de votre projet. Il devrait apparaître dans la liste.

c5358fc837588fe.png

Sélectionnez votre PROJECT_ID dans la liste des projets.

La barre d'état en bas de l'écran affiche 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 pour afficher la fenêtre de chat Duet AI. Si le message "Sélectionner un projet GCP" s'affiche, Cliquez sur le projet, puis sélectionnez-le à nouveau.

La fenêtre de chat Duet AI s'affiche.

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 des fichiers suivants. Vous n'avez pas besoin de créer ces fichiers maintenant. Vous les créerez un par un en suivant les instructions ci-dessous :

  1. package-service.yaml : spécification OpenAPI pour le service de colis, qui contient des données telles que la hauteur, la largeur, le poids et les instructions de manutention 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 CloudSQL (définit le moteur, la session et l'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 permettant de 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 : exigences Python
  8. Dockerfile : pour conteneuriser cette application

Si vous rencontrez des problèmes lors des exercices, 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 d'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 dans le menu de gauche de l'éditeur Cloud Shell. L'icône ressemble à 8b135a000b259175.png. Vous pouvez désormais utiliser Duet AI pour obtenir de l'aide au codage.

package-service.yaml

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

Requête 1 : génère une spécification OpenAPI YAML pour un service qui fournit des informations sur la livraison et les colis à partir d'un ID de produit numérique. Le service doit inclure des informations sur la hauteur, la largeur, la profondeur et le poids des colis, ainsi que des instructions de manutention spécifiques.

ba12626f491a1204.png

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

Vous pouvez COPY 71194556d8061dae.png le 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, puis stockez-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 nécessaires.

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

Réinitialisez l'historique du chat Duet AI en cliquant sur l'icône Corbeille f574ca2c1e114856.png en haut de la barre latérale Duet AI.

data_model.py

Ensuite, vous allez créer le fichier Python du modèle de données pour le service en fonction de la spécification OpenAPI.

Ouvrez le fichier package-service.yaml et saisissez le prompt suivant.

Requête 1 : à l'aide de l'ORM SQLAlchemy Python, génère 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 du code rapidement, vous devez toujours examiner le contenu généré et le comprendre au fur et à mesure.

Tout d'abord, il existe une classe appelée Package de typeBase qui définit le modèle de données pour 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 suit :

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 Cloud SQL, 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 devrez le modifier. Vous le ferez un peu plus tard.

Utilisez le workflow OPEN a4c7ed6d845df343.png le code dans un nouveau 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 le tiret).

Réinitialisez l'historique du chat Duet AI en cliquant sur l'icône Corbeille f574ca2c1e114856.png en haut de la barre latérale Duet AI.

connect-connector.py

Créez le connecteur Cloud SQL.

Ouvrez le fichier data_model.py, puis saisissez les prompts suivants.

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

ed05cb6ff85d34c5.png

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

Utilisons un autre prompt.

Requête 2 : Doit utiliser la bibliothèque cloud-sql-python-connector.

d09095b44dde35bf.png

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

Utilisez le workflow OPEN a4c7ed6d845df343.png le code dans un nouveau 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 du chat Duet AI, puis, avec le fichier connect_connector.py ouvert, générez les ORM DB engine, sessionmaker et base à utiliser dans l'application.

Requête 1 : Créez une classe engine, sessionmaker et un ORM de base à l'aide de la méthode connect_with_connector

6e4214b72ab13a63.png

La réponse peut ajouter les 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 nécessaires.

Vous pouvez également essayer différentes requêtes pour voir la variation potentielle des réponses de Duet AI.

Réinitialisez l'historique du chat Duet AI en cliquant sur l'icône Corbeille f574ca2c1e114856.png en haut de la barre latérale Duet AI.

Mettre à jour data_model.py

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

Effacez l'historique des discussions Duet AI. Ouvrez le fichier data_model.py. Essayez le prompt suivant.

Requête 1 : Dans la fonction principale, importez et utilisez le moteur depuis connect_connector.py.

2e768c9b6c523b9a.png

La réponse devrait indiquer que engine a été importé depuis 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 nécessaires.

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

Réinitialisez l'historique du chat Duet AI en cliquant sur l'icône Corbeille f574ca2c1e114856.png en haut de la barre latérale Duet AI.

requirements.txt

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

Ouvrez les fichiers connect_connector.py et data_model.py, puis saisissez la requête suivante.

Requête 1 : Génère un fichier de requirements pip pour ce modèle de données et ce service

Requête 2 : Génère un fichier de requirements pip pour ce modèle de données et ce service en utilisant les 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 et créez un fichier requirements.txt qui ressemble à ceci :

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

Réinitialisez l'historique du chat Duet AI en cliquant sur l'icône Corbeille f574ca2c1e114856.png en haut de la barre latérale Duet AI.

Créer une table de packages dans Cloud SQL

Définissez les variables d'environnement pour le connecteur de base de données CloudSQL.

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 (evolution), 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 consulter le modèle de données et les détails des tableaux.

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 ensuite des exemples de données à la table packages.

Effacez l'historique des discussions Duet AI. Une fois le fichier data_model.py ouvert, essayez les requêtes suivantes.

Requête 1 : Génère une fonction qui crée 10 lignes d'échantillons de packages et les enregistre dans la table des packages

Requête 2 : À l'aide de la session de connect_connector, générez une fonction qui crée 10 lignes d'échantillons de packages et les valide dans la table des packages.

34a9afc5f04ba5.png

Utilisez le workflow OPEN a4c7ed6d845df343.png le code dans un nouveau 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 nécessaires.

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

Réinitialisez l'historique du chat Duet AI en cliquant sur l'icône Corbeille f574ca2c1e114856.png en haut de la barre latérale 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.

Reconnectez-vous à l'instance Cloud SQL et vérifiez que les exemples de données ont été ajoutés à la table "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 (evolution), récupérez toutes les données de la table "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

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

Requête 1 : En utilisant la bibliothèque Python Flask, crée une implémentation qui utilise des points de terminaison REST HTTP pour ce service.

Requête 2 : À l'aide de la bibliothèque Python Flask, créez une implémentation qui utilise des points de terminaison REST HTTP pour ce service. Importez et utilisez SessionMaker à partir de connect_conector.py pour les données de packages.

Requête 3 : À l'aide de la bibliothèque Python Flask, créez une implémentation qui utilise des points de terminaison REST HTTP pour ce service. Importez et utilisez le package à partir de data_model.py et SessionMaker à partir de connect_conector.py pour les données des packages.

Requête 4 : À l'aide de la bibliothèque Python Flask, créez une implémentation qui utilise des points de terminaison REST HTTP pour ce service. Importez et utilisez le package à partir de data_model.py et SessionMaker à partir de connect_conector.py 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.

Requête : Crée un fichier d'exigences pour main.py

1cc0b318d2d4ca2f.png

Ajoutez ceci au fichier requirements.txt. Assurez-vous d'utiliser Flask version 3.0.0.

Utilisez OPEN a4c7ed6d845df343.png pour créer le code dans un nouveau 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 nécessaires.

Réinitialisez l'historique du chat Duet AI en cliquant sur l'icône Corbeille f574ca2c1e114856.png en haut de la barre latérale Duet AI.

5. Tester et exécuter l'application

Installez les éléments requis.

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 de 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.

Requête 1 : Génère des tests unitaires.

e861e5b63e1b2657.png

Utilisez le workflow OPEN a4c7ed6d845df343.png le code dans un nouveau 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 nécessaires.

Réinitialisez l'historique du chat Duet AI en cliquant sur l'icône Corbeille f574ca2c1e114856.png en haut de la barre latérale 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 du chat en cliquant sur l'icône Corbeille 1ecccfe10d6c540.png dans la barre d'état supérieure.

Dockerfile

Créez un Dockerfile pour cette application.

Ouvrez main.py et essayez les requêtes suivantes.

Requête 1 : Génère un Dockerfile pour cette application.

Requête 2 : Génère 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 fichier Dockerfile doit ressembler à ce qui 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"]

Utilisez le workflow OPEN a4c7ed6d845df343.png le code dans un nouveau 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 nécessaires.

Exécuter l'application en local

Une fois le Dockerfile ouvert, essayez le prompt suivant.

Requête 1 : Comment exécuter un conteneur localement à 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

Dans 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 transmettez-la à 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 à l'emplacement 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. Connectez-vous au cluster GKE.

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

annoter 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. Points en plus : Résoudre les problèmes liés à l'application

Supprimez le rôle IAM "Client Cloud SQL" du compte de service cloudsqlsa. Cela entraîne une erreur de connexion à la base de données CloudSQL.

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 sur le côté droit 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.

Ensuite, demandons à Duet AI de nous aider à résoudre l'erreur.

Essayez le prompt suivant.

Requête 1 : Aide-moi à résoudre cette erreur

9288dd6045369167.png

Saisissez le message d'erreur dans la requête.

Requête 2 : Interdit : le compte principal IAM authentifié ne semble pas autorisé à effectuer la requête API. Vérifiez que l'API Cloud SQL Admin est activée dans votre projet GCP et que le rôle "Client Cloud SQL" a été attribué au principal IAM.

f1e64fbdc435d31c.png

[Musique]

Requête 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 des 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 les points suivants :

  1. Activez Duet AI dans votre projet GCP et configurez-le pour l'utiliser dans un IDE et la console Cloud.
  2. Utiliser Duet AI pour générer, compléter et expliquer du code
  3. Utiliser Duet AI pour expliquer et résoudre un problème d'application
  4. Fonctionnalités de Duet AI, comme le chat IDE et le chat multitour, la génération de code par chat ou intégrée, les 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"]