This project was created as part of the Google AI Hackathon 2024.
Gemini Movie Detectives is a project aimed at leveraging the power of the Gemini Pro model via VertexAI to create an engaging quiz game using the latest movie data from The Movie Database (TMDB).
Try it yourself: movie-detectives.com
The backend infrastructure is built with FastAPI and Python, employing the Retrieval-Augmented Generation (RAG) methodology to enrich queries with real-time metadata. Utilizing Jinja templating, the backend modularizes prompt generation into base, personality, and data enhancement templates, enabling the generation of accurate and engaging quiz questions.
The frontend is powered by Vue 3 and Vite, supported by daisyUI and Tailwind CSS for efficient frontend development. Together, these tools provide users with a sleek and modern interface for seamless interaction with the backend.
In Movie Detectives, quiz answers are interpreted by the Language Model (LLM) once again, allowing for dynamic scoring and personalized responses. This showcases the potential of integrating LLM with RAG in game design and development, paving the way for truly individualized gaming experiences. Furthermore, it demonstrates the potential for creating engaging quiz trivia or educational games by involving LLM. Adding and changing personalities or languages is as easy as adding more Jinja template modules. With very little effort, this can change the full game experience, reducing the effort for developers. Try it yourself and change the AI personality in the quiz configuration.
Frontend: gemini-movie-detectives-ui
- Tech stack and project overview
- Project setup
- Configuration
- Docker
- API example usage
- Rate limit
- Personalities
- Languages
- Python 3.12 + FastAPI API development
- httpx for TMDB integration
- Jinja templating for modular prompt generation
- Pydantic for data modeling and validation
- Poetry for dependency management
- Docker for deployment
- TMDB API for movie data
- VertexAI and Gemini for generating quiz questions and evaluating answers
- Ruff as linter and code formatter together with pre-commit hooks
- Github Actions to automatically run tests and linter on every push
Movie Detectives - System Overview
(Optional) Configure poetry to use in-project virtualenvs:
poetry config virtualenvs.in-project true
Install dependencies:
poetry install
Run:
source .venv/bin/activate
uvicorn gemini_movie_detectives_api.main:app --reload
curl -s localhost:8000/movies | jq .
Prerequisite
- TMDB API key (can be generated for free)
- GCP project with VertexAI API enabled
- JSON credentials file for GCP Service Account with VertexAI permissions
The API is configured via environment variables. If a .env
file is present in the project root, it will be loaded
automatically. The following variables must be set:
TMDB_API_KEY
: The API key for The Movie Database (TMDB).GCP_PROJECT_ID
: The ID of the Google Cloud Platform (GCP) project used for VertexAI and Gemini.GCP_LOCATION
: The location used for prediction processes.GCP_SERVICE_ACCOUNT_FILE
: The path to the service account file used for authentication with GCP.
There are more config variables with defaults, which can be used to adjust the default API behavior.
docker image rm gemini-movie-detectives-api
docker build -t gemini-movie-detectives-api .
docker run -d --rm --name gemini-movie-detectives-api -p 9091:9091 gemini-movie-detectives-api
curl -s localhost:9091/movies | jq .
docker stop gemini-movie-detectives-api
docker save gemini-movie-detectives-api:latest | gzip > gemini-movie-detectives-api_latest.tar.gz
curl -s localhost:8000/movies | jq .
curl -s "localhost:8000/movies?page=3&vote-avg-min=8&vote-count-min=1000" | jq ".[0]"
{
"adult": false,
"backdrop_path": "/eHMh7kChaNeD4VTdMCXLJbRTzcI.jpg",
"genre_ids": [
18,
36,
10752
],
"id": 753342,
"original_language": "en",
"original_title": "Napoleon",
"overview": "An epic that details the checkered rise and fall of French Emperor Napoleon Bonaparte and his relentless journey to power through the prism of his addictive, volatile relationship with his wife, Josephine.",
"popularity": 193.344,
"poster_path": "/vcZWJGvB5xydWuUO1vaTLI82tGi.jpg",
"release_date": "2023-11-22",
"title": "Napoleon",
"video": false,
"vote_average": 6.484,
"vote_count": 1953,
"poster_url": "https://image.tmdb.org/t/p/original/vcZWJGvB5xydWuUO1vaTLI82tGi.jpg"
}
curl -s localhost:8000/movies/random | jq .
{
"adult": false,
"backdrop_path": "/oe7mWkvYhK4PLRNAVSvonzyUXNy.jpg",
"belongs_to_collection": null,
"budget": 85000000,
"genres": [
{
"id": 28,
"name": "Action"
},
{
"id": 53,
"name": "Thriller"
}
],
"homepage": "https://www.amazon.com/gp/video/detail/B0CH5YQPZQ",
"id": 359410,
"imdb_id": "tt3359350",
"original_language": "en",
"original_title": "Road House",
"overview": "Ex-UFC fighter Dalton takes a job as a bouncer at a Florida Keys roadhouse, only to discover that this paradise is not all it seems.",
"popularity": 1880.547,
"poster_path": "/bXi6IQiQDHD00JFio5ZSZOeRSBh.jpg",
"production_companies": [
{
"id": 21,
"logo_path": "/usUnaYV6hQnlVAXP6r4HwrlLFPG.png",
"name": "Metro-Goldwyn-Mayer",
"origin_country": "US"
},
{
"id": 1885,
"logo_path": "/xlvoOZr4s1PygosrwZyolIFe5xs.png",
"name": "Silver Pictures",
"origin_country": "US"
}
],
"production_countries": [
{
"iso_3166_1": "US",
"name": "United States of America"
}
],
"release_date": "2024-03-08",
"revenue": 0,
"runtime": 121,
"spoken_languages": [
{
"english_name": "English",
"iso_639_1": "en",
"name": "English"
}
],
"status": "Released",
"tagline": "Take it outside.",
"title": "Road House",
"video": false,
"vote_average": 7.14,
"vote_count": 1182,
"poster_url": "https://image.tmdb.org/t/p/original/bXi6IQiQDHD00JFio5ZSZOeRSBh.jpg"
}
curl -s -X POST localhost:8000/quiz \
-H 'Content-Type: application/json' \
-d '{"vote_avg_min": 5.0, "vote_count_min": 1000.0, "popularity": 3}' | jq .
{
"quiz_id": "84c19425-c179-4198-9773-a8a1b71c9605",
"question": {
"question": "Imagine a family road trip, but not just any road trip, a life-or-death race against time! A giant space rock is hurtling towards Earth, and this family is trying to outrun the end of the world. Along the way, they witness cities crumbling like sandcastles and meet people who are both kind and cruel. Can they make it to safety in time?",
"hint1": "The movie is all about a family trying to survive a global catastrophe.",
"hint2": "Gr_e_l_nd"
},
"movie": {...}
}
curl -s -X POST localhost:8000/quiz | jq .
The API also validates the input data using Pydantic. If the input data is invalid, the API will return an error message:
curl -s -X POST localhost:8000/quiz \
-H 'Content-Type: application/json' \
-d '{"vote_avg_min": 11.0}' | jq .
{
"detail": [
{
"type": "less_than_equal",
"loc": [
"body",
"vote_avg_min"
],
"msg": "Input should be less than or equal to 9",
"input": 11.0,
"ctx": {
"le": 9.0
},
"url": "https://errors.pydantic.dev/2.6/v/less_than_equal"
}
]
}
curl -s -X POST localhost:8000/quiz/84c19425-c179-4198-9773-a8a1b71c9605/answer \
-H 'Content-Type: application/json' \
-d '{"answer": "Greenland"}' | jq .
{
"quiz_id": "84c19425-c179-4198-9773-a8a1b71c9605",
"question": {...},
"movie": {...},
"user_answer": "Greenland",
"result": {
"points": "3",
"answer": "Congratulations! You got it! Greenland is the movie we were looking for. You're like a human GPS, always finding the right way!"
}
}
In order to control costs and prevent abuse, the API offers a way to limit the number of quiz sessions per day.
There is a default value which can be overwritten by using an environment variable called QUIZ_RATE_LIMIT
. It is reset
every day at midnight. The API also has an endpoint to fetch details about the limit and current usage:
curl -s localhost:8000/limit | jq .
{
"daily_limit": 100,
"quiz_count": 0,
"last_reset_time": "2024-04-06T12:39:29.857703",
"last_reset_date": "2024-04-06",
"current_date": "2024-04-06"
}
Once a quiz is started and current_date
is greater than last_reset_date
, the quiz_count
is reset to 0.
Due to the modularity of the prompt generation, it is possible to easily switch personalities of the quiz master. The
personalities are defined in Jinja templates in the gemini_movie_detectives_api/templates/personality/
directory.
They are managed by a StrEnum
in gemini_movie_detectives_api/prompt.py
, which makes it easy to extend.
The following example shows how to switch to the Santa Claus / Christmas personality for a quiz:
curl -s -X POST localhost:8000/quiz \
-H 'Content-Type: application/json' \
-d '{"vote_avg_min": 5.0, "vote_count_min": 1000.0, "popularity": 3, "personality": "christmas"}' | jq .
{
"quiz_id": "e1d298c3-fcb0-4ebe-8836-a22a51f87dc6",
"question": {
"question": "Ho ho ho, this movie takes place in a world of dreams, just like the dreams children have on Christmas Eve after seeing Santa Claus! It's about a team who enters people's dreams to steal their secrets. Can you guess the movie? Merry Christmas!",
"hint1": "The main character is like a skilled elf, sneaking into people's minds instead of houses. ",
"hint2": "I_c_p_i_n "
},
"movie": {...}
}
Due to the modularity of the prompt generation, it is also possible to easily switch the language of the game.
Languages are defined in Jinja templates in the gemini_movie_detectives_api/templates/language/
directory.
They are managed by a StrEnum
in gemini_movie_detectives_api/prompt.py
, which makes it easy to extend.
This example shows how to change the language for a quiz:
curl -s -X POST localhost:8000/quiz \
-H 'Content-Type: application/json' \
-d '{"vote_avg_min": 5.0, "vote_count_min": 1000.0, "popularity": 3, "language": "german"}' | jq .
{
"quiz_id": "7f5f8cf5-4ded-42d3-a6f0-976e4f096c0e",
"question": {
"question": "Stellt euch vor, es gäbe riesige Monster, die auf der Erde herumtrampeln, als wäre es ein Spielplatz! Einer ist ein echtes Urviech, eine Art wandelnde Riesenechse mit einem Atem, der so heiß ist, dass er euer Toastbrot in Sekundenschnelle rösten könnte. Der andere ist ein gigantischer Affe, der so stark ist, dass er Bäume ausreißt wie Gänseblümchen. Und jetzt ratet mal, was passiert? Die beiden geraten aneinander, wie zwei Kinder, die sich um das letzte Stück Kuchen streiten! Wer wird wohl gewinnen, die Riesenechse oder der Superaffe? Das ist die Frage, die sich die ganze Welt stellt! ",
"hint1": "Der Film spielt in einer Zeit, in der Monster auf der Erde wandeln.",
"hint2": "G_dz_ll_ vs. K_ng "
},
"movie": {...}
}