TP done
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
import strawberry
|
||||
|
||||
# Note : l'énoncé demande d'importer la V1 à l'étape 3, puis la V2 à l'étape 6
|
||||
# L'import ci-dessous correspond à l'étape 6.
|
||||
from app.graphql.resolvers.analyze_movie_v2 import analyze_movie_by_id
|
||||
from app.graphql.types.movie_analysis import MovieAnalysis
|
||||
|
||||
@@ -11,13 +9,7 @@ class Query:
|
||||
"""
|
||||
Point d'entrée pour toutes les requêtes GraphQL de type 'query'.
|
||||
"""
|
||||
|
||||
# TODO : (Étape 3) L'énoncé vous demande d'implémenter ce champ
|
||||
# en important et en utilisant 'analyze_movie_v1'
|
||||
|
||||
# (Étape 6) L'énoncé vous demande ensuite de basculer vers 'analyze_movie_v2'
|
||||
# La configuration ci-dessous correspond à l'étape 6.
|
||||
analyzeMovie: MovieAnalysis = strawberry.field(
|
||||
resolver=analyze_movie_by_id, # 'analyze_movie_by_id' est importé depuis v2
|
||||
description="Analyse un film en utilisant l'IA."
|
||||
analyze_movie: MovieAnalysis = strawberry.field(
|
||||
resolver=analyze_movie_by_id,
|
||||
description="Lance une analyse par IA d'un film donné par son ID."
|
||||
)
|
||||
|
||||
@@ -10,9 +10,6 @@ async def analyze_movie_by_id(
|
||||
# movie_input: MovieInput, classe nécessaire seulement si on avait eu beaucoup de champs en entrée
|
||||
) -> MovieAnalysis:
|
||||
|
||||
# TODO : (Étape 3) Remplacer la ligne suivante par un appel au service V1
|
||||
# analysis_data = await analyze_movie(movie_id=movie_id)
|
||||
raise NotImplementedError("Le resolver analyze_movie_v1.analyze_movie_by_id() n'est pas implémenté.")
|
||||
analysis_data = await analyze_movie(movie_id=movie_id)
|
||||
|
||||
# La ligne ci-dessous doit être décommentée une fois le TODO complété
|
||||
# return MovieAnalysis(**analysis_data)
|
||||
return MovieAnalysis(**analysis_data)
|
||||
|
||||
@@ -5,18 +5,13 @@ from app.graphql.resolvers.helper import is_field_requested
|
||||
from app.graphql.types.movie_analysis import MovieAnalysis
|
||||
from app.services.movie_analyzer_v2 import analyze_movie
|
||||
|
||||
|
||||
async def analyze_movie_by_id(
|
||||
movie_id: strawberry.ID,
|
||||
info: Info,
|
||||
movie_id: strawberry.ID,
|
||||
info: Info,
|
||||
) -> MovieAnalysis:
|
||||
# TODO : (Étape 6) Remplacer la ligne suivante par la récupération
|
||||
# du LLM depuis 'info.context' (ex: llm = info.context["llm"])
|
||||
raise NotImplementedError("Le resolver V2 n'a pas encore récupéré le LLM du contexte.")
|
||||
|
||||
# llm = ... # <== Code à écrire
|
||||
llm = info.context["llm"]
|
||||
|
||||
# Le 'llm=llm' ci-dessous fonctionnera une fois le TODO complété
|
||||
analysis_data = await analyze_movie(
|
||||
movie_id=movie_id,
|
||||
ai_summary=is_field_requested(info, "aiSummary"),
|
||||
|
||||
@@ -7,10 +7,6 @@ from app.repositories._base_client import api_client
|
||||
class GenreRepository:
|
||||
async def list(self) -> List[Genre]:
|
||||
response = await api_client._request("GET", "/genres/")
|
||||
# TODO : (Étape 2) Remplacer la ligne suivante par un appel à
|
||||
# response = await api_client._request("GET", "/genres/")
|
||||
# La ligne ci-dessous doit être décommentée une fois le TODO complété
|
||||
return [Genre.model_validate(g) for g in response.json()]
|
||||
|
||||
|
||||
genre_repository = GenreRepository()
|
||||
@@ -1,28 +1,24 @@
|
||||
from typing import List, Optional
|
||||
import httpx
|
||||
|
||||
from app.core.exceptions import DALException
|
||||
from app.models.movie import Movie
|
||||
from app.repositories._base_client import api_client
|
||||
|
||||
|
||||
class MovieRepository:
|
||||
|
||||
async def list(self, skip: int = 0, limit: int = 100) -> List[Movie]:
|
||||
# TODO : (Étape 2) Remplacer la ligne suivante par un appel à
|
||||
# response = await api_client._request("GET", "/movies/", params={"skip": skip, "limit": limit})
|
||||
# La ligne ci-dessous doit être décommentée une fois le TODO complété
|
||||
response = await api_client._request("GET", "/movies/", params={"skip": skip, "limit": limit})
|
||||
response = await api_client._request(
|
||||
"GET", "/movies/", params={"skip": skip, "limit": limit}
|
||||
)
|
||||
return [Movie.model_validate(m) for m in response.json()]
|
||||
|
||||
async def find_by_id(self, movie_id: int) -> Optional[Movie]:
|
||||
try:
|
||||
response = await api_client._request("GET", f"/movies/{movie_id}")
|
||||
# TODO : (Étape 2) Remplacer la ligne suivante par un appel à
|
||||
# response = await api_client._request("GET", f"/movies/{movie_id}")
|
||||
# La ligne ci-dessous doit être décommentée une fois le TODO complété
|
||||
return Movie.model_validate(response.json())
|
||||
except DALException as e:
|
||||
if e.status_code == 404:
|
||||
return None
|
||||
raise
|
||||
|
||||
movie_repository = MovieRepository()
|
||||
@@ -1,17 +1,9 @@
|
||||
|
||||
async def analyze_movie(movie_id: str) -> dict:
|
||||
# TODO : retourner un dictionnaire python statique (chaînes de caractères en dur) avec comme attributs:
|
||||
# id (correspondant à movie_id)
|
||||
# aiSummary (chaîne de caractères arbitraire)
|
||||
# aiOpinionSummary (chaîne de caractères arbitraire)
|
||||
# aiBestGenre (chaîne de caractères arbitraire)
|
||||
# aiTags (TABLEAU de chaînes de caractères arbitraire)
|
||||
raise NotImplementedError("Le service movie_analyzer_v1.analyze_movie() n'est pas implémenté.")
|
||||
return {
|
||||
"id": movie_id,
|
||||
|
||||
|
||||
|
||||
|
||||
"aiSummary" : "C'est l'histoire de...",
|
||||
"aiOpinionSummary": "Le film est une aventure épique...",
|
||||
"aiBestGenre": "Fantastique",
|
||||
"aiTags": ["Épique", "Quête", "Magie"]
|
||||
}
|
||||
|
||||
|
||||
@@ -41,23 +41,13 @@ async def get_ai_best_genre(llm, synopsis, all_genres):
|
||||
genres_list = ", ".join([genre.label for genre in all_genres])
|
||||
|
||||
# Prompt pour choisir le genre le plus pertinent
|
||||
# TODO : compléter les instructions du prompt
|
||||
prompt = f"""
|
||||
# TODO : Écrire les instructions pour le LLM.
|
||||
# Objectif : Choisir le *seul* genre le plus pertinent pour le film.
|
||||
# Contraintes :
|
||||
# 1. Le LLM DOIT répondre en français.
|
||||
# 2. Le LLM DOIT choisir son genre EXCLUSIVEMENT parmi la liste fournie.
|
||||
# 3. Le LLM NE DOIT retourner QUE le nom du genre (ex: "Drame"), sans aucune autre phrase.
|
||||
|
||||
Voici le synopsis :
|
||||
{synopsis}
|
||||
|
||||
Voici la liste des genres autorisés :
|
||||
{genres_list}
|
||||
|
||||
Genre le plus pertinent :
|
||||
"""
|
||||
Français uniquement.
|
||||
Parmi la liste suivante de genres cinématographiques, choisis le genre le plus pertinent pour le synopsis donné.
|
||||
Liste des genres : {genres_list}
|
||||
Ne retourne que le nom du genre, sans explication, sans phrase d'introduction.
|
||||
Synopsis : {synopsis}
|
||||
"""
|
||||
|
||||
# Appel asynchrone au modèle de langage
|
||||
response = await llm.ainvoke(prompt)
|
||||
@@ -68,21 +58,15 @@ async def get_ai_tags(llm, title, synopsis):
|
||||
if not title or not synopsis:
|
||||
return None
|
||||
|
||||
# TODO : définir le prompt approprié
|
||||
prompt = f"""
|
||||
# TODO : Écrire les instructions pour le LLM.
|
||||
# Objectif : Générer 5 tags (mots-clés) pertinents pour le film.
|
||||
# Contraintes :
|
||||
# 1. Le LLM DOIT répondre en français.
|
||||
# 2. Le LLM DOIT retourner une liste de tags séparés par des virgules.
|
||||
# 3. Le LLM NE DOIT PAS inclure de phrase d'introduction (ex: "Voici les tags :").
|
||||
|
||||
Titre du film : {title}
|
||||
Synopsis : {synopsis}
|
||||
|
||||
Génère 5 tags pertinents, séparés par des virgules :
|
||||
"""
|
||||
Français uniquement.
|
||||
Liste 5 à 8 mots-clés (tags) pertinents pour le film suivant.
|
||||
Réponds uniquement avec les mots-clés séparés par des virgules, sans numérotation, sans explication, sans phrase d'introduction.
|
||||
Par exemple : "intelligence artificielle, prophétie, pirate informatique, réalité virtuelle".
|
||||
|
||||
Titre : {title}
|
||||
Synopsis : {synopsis}
|
||||
"""
|
||||
response = await llm.ainvoke(prompt)
|
||||
tags = [tag.strip() for tag in response.content.split(',') if tag.strip()]
|
||||
return tags
|
||||
@@ -113,12 +97,14 @@ async def analyze_movie(
|
||||
if ai_summary:
|
||||
tasks["aiSummary"] = get_ai_summary(llm, movie_data.synopsis)
|
||||
|
||||
# TODO : (Étape 5) compléter la logique d'ajout des tâches avec :
|
||||
# appeler 'get_ai_opinion_summary', 'get_ai_best_genre', 'get_ai_tags'
|
||||
# mettre le résultat respectivement dans la clé "aiOpinionSummary", "aiBestGenre", "aiTags" (ATTENTION : il faut respecter la casse pour ces clés!) du tableau associatif (dictionnaire) "tasks"
|
||||
# respectivement en fonction des booléens 'ai_opinion_summary', 'ai_best_genre', 'ai_tags'
|
||||
if ai_opinion_summary or ai_best_genre or ai_tags:
|
||||
raise NotImplementedError("La logique d'ajout de tâches (opinion, genre, tags) n'est pas implémentée.")
|
||||
if ai_opinion_summary:
|
||||
tasks["aiOpinionSummary"] = get_ai_opinion_summary(llm, movie_data.title, movie_data.opinions)
|
||||
|
||||
if ai_best_genre:
|
||||
tasks["aiBestGenre"] = get_ai_best_genre(llm, movie_data.synopsis, all_genres)
|
||||
|
||||
if ai_tags:
|
||||
tasks["aiTags"] = get_ai_tags(llm, movie_data.title, movie_data.synopsis)
|
||||
|
||||
if tasks:
|
||||
# On récupère les coroutines (les fonctions async prêtes à être lancées)
|
||||
|
||||
Reference in New Issue
Block a user