{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "qYvD2zzKobTC"
},
"source": [
"\n",
"\n",
"# Collaborative Movie Recommendation System using Redis, CrewAI, and LangGraph\n",
"\n",
"
\n",
"\n",
"This notebook demonstrates the implementation of a collaborative movie recommendation system using Redis for data storage, CrewAI for agent-based task execution, and LangGraph for workflow management. The system analyzes user preferences, matches movies based on these preferences, and generates personalized recommendations.\n",
"\n",
"Key concepts:\n",
"- Redis: Used for storing movie data and chat history\n",
"- CrewAI: Facilitates the creation and management of AI agents for specific tasks\n",
"- LangGraph: Manages the overall workflow of the recommendation system"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "NTFxCojYECnx"
},
"source": [
"\n",
"\n",
"## Let's Begin!\n",
"
\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"id": "Zz62U5COgF21"
},
"outputs": [],
"source": [
"%pip install -U --quiet crewai==0.76.2\n",
"%pip install -U --quiet langchain langchain-openai \"langchain-redis>=0.2.0\" langgraph"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "VO0i-1c9m2Kb",
"outputId": "ec942dbf-226a-426d-8964-e03831e0dd99"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OPENAI_API_KEY:··········\n"
]
}
],
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_env(key: str):\n",
" if key not in os.environ:\n",
" os.environ[key] = getpass.getpass(f\"{key}:\")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Po4K08Uoa5HJ"
},
"source": [
"### Setup Redis"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "vlF2874ZoBWu",
"outputId": "e5e7ebc0-b70c-4682-d70c-b33c584e72d4"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb jammy main\n",
"Starting redis-stack-server, database path /var/lib/redis-stack\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"gpg: cannot open '/dev/tty': No such device or address\n",
"curl: (23) Failed writing body\n"
]
}
],
"source": [
"# NBVAL_SKIP\n",
"%%sh\n",
"curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg\n",
"echo \"deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main\" | sudo tee /etc/apt/sources.list.d/redis.list\n",
"sudo apt-get update > /dev/null 2>&1\n",
"sudo apt-get install redis-stack-server > /dev/null 2>&1\n",
"redis-stack-server --daemonize yes"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "My-zol_loQaw",
"outputId": "b58c2466-ee10-480c-ad4c-608cbf747e8b"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Connecting to Redis at: redis://localhost:6379\n"
]
}
],
"source": [
"# Use the environment variable if set, otherwise default to localhost\n",
"REDIS_URL = os.getenv(\"REDIS_URL\", \"redis://localhost:6379\")\n",
"print(f\"Connecting to Redis at: {REDIS_URL}\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "p8lqllwDoV_K"
},
"source": [
"\n",
"## Setup and Imports"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "HGIZ2qksoVKk",
"outputId": "3b7d8502-e5f0-4b2a-b147-087ab8b41a0b"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"19:21:01 httpx INFO HTTP Request: GET https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json \"HTTP/1.1 200 OK\"\n"
]
}
],
"source": [
"import re\n",
"import random\n",
"import pandas as pd\n",
"\n",
"from typing import List, Dict, Union\n",
"from langchain_redis import RedisVectorStore, RedisCache, RedisChatMessageHistory\n",
"from langchain_openai import OpenAIEmbeddings, ChatOpenAI\n",
"from langchain_core.messages import HumanMessage, AIMessage\n",
"from langchain_core.prompts import ChatPromptTemplate\n",
"from crewai import Agent, Task, Crew, Process\n",
"from langgraph.graph import StateGraph, END"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "MXUx4nNZouFZ"
},
"source": [
"## Load and Prepare MovieLens Dataset\n",
"\n",
"Download and process the MovieLens dataset, which contains movie information and ratings. This data will be used to populate our recommendation system."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "O5EUxmIGoqtH",
"outputId": "89d1d81e-e7b7-48e3-bc7d-9dde914ed35b"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"--2024-11-04 19:21:03-- https://files.grouplens.org/datasets/movielens/ml-latest-small.zip\n",
"Resolving files.grouplens.org (files.grouplens.org)... 128.101.65.152\n",
"Connecting to files.grouplens.org (files.grouplens.org)|128.101.65.152|:443... connected.\n",
"HTTP request sent, awaiting response... 200 OK\n",
"Length: 978202 (955K) [application/zip]\n",
"Saving to: ‘ml-latest-small.zip.2’\n",
"\n",
"ml-latest-small.zip 100%[===================>] 955.28K 4.31MB/s in 0.2s \n",
"\n",
"2024-11-04 19:21:04 (4.31 MB/s) - ‘ml-latest-small.zip.2’ saved [978202/978202]\n",
"\n",
"Archive: ml-latest-small.zip\n",
"replace ml-latest-small/links.csv? [y]es, [n]o, [A]ll, [N]one, [r]ename: A\n",
" inflating: ml-latest-small/links.csv \n",
" inflating: ml-latest-small/tags.csv \n",
" inflating: ml-latest-small/ratings.csv \n",
" inflating: ml-latest-small/README.txt \n",
" inflating: ml-latest-small/movies.csv \n"
]
}
],
"source": [
"# Download MovieLens dataset (small version for demonstration)\n",
"!wget https://files.grouplens.org/datasets/movielens/ml-latest-small.zip\n",
"!unzip ml-latest-small.zip\n",
"\n",
"movies_df = pd.read_csv('ml-latest-small/movies.csv')\n",
"ratings_df = pd.read_csv('ml-latest-small/ratings.csv')"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "jVs3f4Dho2tY"
},
"source": [
"## Initialize Redis Components\n",
"\n",
"Set up Redis components for storing movie data, caching, and managing chat history. This allows for efficient data retrieval and persistence across user sessions.\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "lNLVmXfYo39_",
"outputId": "54e39aa9-c8eb-444d-efb2-80a257638531"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"19:22:35 httpx INFO HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n",
"19:22:35 httpx INFO HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n",
"19:22:35 redisvl.index.index INFO Index already exists, not overwriting.\n",
"19:22:37 httpx INFO HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n",
"19:22:40 httpx INFO HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n",
"19:22:41 httpx INFO HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n",
"19:22:44 httpx INFO HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n",
"19:22:48 httpx INFO HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n",
"19:22:50 httpx INFO HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n",
"19:22:54 httpx INFO HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n",
"19:22:57 httpx INFO HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n",
"19:23:00 httpx INFO HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n",
"19:23:03 httpx INFO HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n"
]
}
],
"source": [
"embeddings = OpenAIEmbeddings()\n",
"vector_store = RedisVectorStore.from_texts(\n",
" texts=movies_df['title'].tolist(),\n",
" metadatas=movies_df.to_dict('records'),\n",
" embedding=embeddings,\n",
" redis_url=REDIS_URL,\n",
" index_name=\"movie_recommendations\"\n",
")\n",
"\n",
"cache = RedisCache(redis_url=REDIS_URL)\n",
"chat_history = RedisChatMessageHistory(\"movie_recommendations\", redis_url=REDIS_URL)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "sFIh9ITnpMnB"
},
"source": [
"## Define CrewAI Agents\n",
"\n",
"Create three specialized AI agents using CrewAI:\n",
"\n",
"1. Preference Analyst: Analyzes user preferences based on input and chat history\n",
"2. Movie Matcher: Finds movies that match the analyzed preferences\n",
"3. Recommendation Generator: Creates personalized movie recommendations\n",
"\n",
"Each agent is equipped with specific tools and functions to perform its task."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"id": "QCpoYbIxpOFV"
},
"outputs": [],
"source": [
"llm = ChatOpenAI(temperature=0.7)\n",
"\n",
"# Create a tool from the vector store retriever\n",
"from langchain.tools import Tool\n",
"\n",
"# Create a wrapper function for the retriever\n",
"def search_movies(query: str) -> str:\n",
" \"\"\"Search for movies in the database based on the query.\"\"\"\n",
" results = vector_store.similarity_search(query, k=5)\n",
" return \"\\n\".join(f\"{i+1}. {doc.page_content}\" for i, doc in enumerate(results))\n",
"\n",
"# Create a tool from the vector store retriever\n",
"retriever_tool = Tool(\n",
" name=\"Movie Database Lookup\",\n",
" func=search_movies,\n",
" description=\"Use this tool to search for movies in the database based on titles or descriptions.\"\n",
")\n",
"\n",
"def analyze_preferences(user_input: str, chat_history: List[Union[HumanMessage, AIMessage]]) -> str:\n",
" # Combine the current input with the last few messages from chat history\n",
" context = user_input + \" \" + \" \".join([m.content for m in chat_history[-3:] if isinstance(m, HumanMessage)])\n",
"\n",
" # Use OpenAI embeddings to find similar movies\n",
" embeddings = OpenAIEmbeddings()\n",
" query_vector = embeddings.embed_query(context)\n",
"\n",
" # Search for similar movies in the vector store\n",
" similar_movies = vector_store.similarity_search_by_vector(query_vector, k=5)\n",
"\n",
" # Extract genres and themes from the similar movies\n",
" genres = set()\n",
" themes = set()\n",
" for movie in similar_movies:\n",
" movie_metadata = movie.metadata\n",
" if 'genres' in movie_metadata:\n",
" genres.update(movie_metadata['genres'].split('|'))\n",
" if 'keywords' in movie_metadata:\n",
" themes.update(movie_metadata['keywords'].split('|')[:3]) # Limit to top 3 keywords\n",
"\n",
" # Construct the preference analysis\n",
" preferences = f\"Based on the user's input and chat history, they seem to prefer:\\n\"\n",
" preferences += f\"Genres: {', '.join(genres)}\\n\"\n",
" preferences += f\"Themes/Keywords: {', '.join(themes)}\\n\"\n",
"\n",
" # Add any specific preferences from the current input\n",
" if \"action\" in user_input.lower():\n",
" preferences += \"The user has explicitly mentioned interest in action movies.\\n\"\n",
" if \"comedy\" in user_input.lower():\n",
" preferences += \"The user has explicitly mentioned interest in comedy movies.\\n\"\n",
" if \"recent\" in user_input.lower() or \"new\" in user_input.lower():\n",
" preferences += \"The user seems interested in recent or new releases.\\n\"\n",
"\n",
" return preferences\n",
"\n",
"preference_analyst = Agent(\n",
" role='Preference Analyst',\n",
" goal='Analyze user preferences based on their input and chat history',\n",
" backstory='You are an expert in understanding user preferences for movies',\n",
" tools=[retriever_tool],\n",
" llm=llm,\n",
" verbose=True\n",
")\n",
"\n",
"def match_movies(preferences: str) -> List[Dict[str, str]]:\n",
" # Extract genres and themes from the preferences\n",
" genres = re.findall(r'Genres: (.*)', preferences)[0].split(', ')\n",
" themes = re.findall(r'Themes/Keywords: (.*)', preferences)[0].split(', ')\n",
"\n",
" # Combine genres and themes for the search query\n",
" search_query = ' '.join(genres + themes)\n",
"\n",
" # Use OpenAI embeddings to convert the search query to a vector\n",
" embeddings = OpenAIEmbeddings()\n",
" query_vector = embeddings.embed_query(search_query)\n",
"\n",
" # Search for similar movies in the vector store\n",
" matched_movies = vector_store.similarity_search_by_vector(query_vector, k=10)\n",
"\n",
" # Process and format the matched movies\n",
" formatted_movies = []\n",
" for movie in matched_movies:\n",
" movie_data = movie.metadata\n",
" formatted_movie = {\n",
" \"title\": movie_data.get('title', 'Unknown Title'),\n",
" \"year\": movie_data.get('year', 'Unknown Year'),\n",
" \"genres\": movie_data.get('genres', 'Unknown Genres'),\n",
" \"description\": movie_data.get('overview', 'No description available')[:200] + '...' # Truncate long descriptions\n",
" }\n",
" formatted_movies.append(formatted_movie)\n",
"\n",
" return formatted_movies\n",
"\n",
"# Update the movie_matcher agent\n",
"movie_matcher = Agent(\n",
" role='Movie Matcher',\n",
" goal='Find movies that match user preferences',\n",
" backstory='You are an expert in matching user preferences to movies in the database',\n",
" tools=[retriever_tool],\n",
" llm=llm,\n",
" verbose=True\n",
")\n",
"\n",
"def generate_recommendations(matched_movies: List[Dict[str, str]], user_preferences: str) -> str:\n",
" # Extract key preferences\n",
" genres_preferred = re.findall(r'Genres: (.*)', user_preferences)[0].split(', ')\n",
" themes_preferred = re.findall(r'Themes/Keywords: (.*)', user_preferences)[0].split(', ')\n",
"\n",
" # Sort matched movies based on relevance to preferences\n",
" def relevance_score(movie):\n",
" score = 0\n",
" for genre in genres_preferred:\n",
" if genre.lower() in movie['genres'].lower():\n",
" score += 2\n",
" for theme in themes_preferred:\n",
" if theme.lower() in movie['description'].lower():\n",
" score += 1\n",
" return score\n",
"\n",
" sorted_movies = sorted(matched_movies, key=relevance_score, reverse=True)\n",
"\n",
" # Generate personalized recommendations\n",
" recommendations = \"Based on your preferences, here are some personalized movie recommendations:\\n\\n\"\n",
"\n",
" for i, movie in enumerate(sorted_movies[:5], 1):\n",
" recommendations += f\"{i}. {movie['title']} ({movie['year']})\\n\"\n",
" recommendations += f\" Genres: {movie['genres']}\\n\"\n",
" recommendations += f\" Description: {movie['description']}\\n\"\n",
"\n",
" # Add personalized reason for recommendation\n",
" reason = \"This movie was recommended because \"\n",
" matching_genres = [genre for genre in genres_preferred if genre.lower() in movie['genres'].lower()]\n",
" matching_themes = [theme for theme in themes_preferred if theme.lower() in movie['description'].lower()]\n",
"\n",
" if matching_genres:\n",
" reason += f\"it's a {' and '.join(matching_genres)} film, which aligns with your genre preferences. \"\n",
" if matching_themes:\n",
" reason += f\"It explores themes like {' and '.join(matching_themes)}, which you seem interested in. \"\n",
" if not matching_genres and not matching_themes:\n",
" reason += \"it offers a mix of elements that we think you might enjoy based on your overall preferences. \"\n",
"\n",
" recommendations += f\" Why we recommend it: {reason}\\n\\n\"\n",
"\n",
" # Add a personalized closing message\n",
" closing_messages = [\n",
" \"We hope you find something you'll love in this selection!\",\n",
" \"Don't hesitate to ask for more recommendations if none of these catch your eye.\",\n",
" \"Remember, great movies often surprise us. Give something new a try!\",\n",
" \"Enjoy your movie night!\"\n",
" ]\n",
" recommendations += random.choice(closing_messages)\n",
"\n",
" return recommendations\n",
"\n",
"# Update the recommendation_generator agent\n",
"recommendation_generator = Agent(\n",
" role='Recommendation Generator',\n",
" goal='Generate personalized movie recommendations',\n",
" backstory='You are an expert in creating engaging and personalized movie recommendations',\n",
" tools=[retriever_tool],\n",
" llm=llm,\n",
" verbose=True\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "BBAQkn9yuxR3"
},
"source": [
"## Define Tasks\n",
"\n",
"Define the tasks that will be executed by the CrewAI agents. Each task is associated with a specific agent and has a clear description and expected output. These tasks form the backbone of our recommendation process:\n",
"\n",
"1. Analyze Preferences: Examines user input and chat history to determine movie preferences\n",
"2. Match Movies: Finds movies that align with the analyzed preferences\n",
"3. Generate Recommendations: Creates a personalized list of movie recommendations based on the matched movies\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"id": "ZlX1fuIkmiEg"
},
"outputs": [],
"source": [
"analyze_preferences_task = Task(\n",
" description='Analyze user preferences based on their input and chat history',\n",
" agent=preference_analyst,\n",
" expected_output=\"A detailed analysis of the user's movie preferences\"\n",
")\n",
"\n",
"match_movies_task = Task(\n",
" description='Find movies that match the analyzed user preferences',\n",
" agent=movie_matcher,\n",
" expected_output=\"A list of movies matching the user's preferences\"\n",
")\n",
"\n",
"generate_recommendations_task = Task(\n",
" description='Generate personalized movie recommendations based on matched movies',\n",
" agent=recommendation_generator,\n",
" expected_output=\"A personalized list of movie recommendations\"\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "LYDF0t34u3ju"
},
"source": [
"## Create Crew\n",
"\n",
"Assemble the agents and tasks into a CrewAI Crew. This crew will work collaboratively to generate movie recommendations.\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"id": "SBXk7AOhu2Jm"
},
"outputs": [],
"source": [
"movie_crew = Crew(\n",
" agents=[preference_analyst, movie_matcher, recommendation_generator],\n",
" tasks=[analyze_preferences_task, match_movies_task, generate_recommendations_task],\n",
" verbose=True\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "5XnO80gIu_mJ"
},
"source": [
"## LangGraph Workflow\n",
"\n",
"Sets up the LangGraph workflow, which manages the overall process of generating recommendations. It defines the input and output structures and creates a state graph that orchestrates the flow of data between different stages of the recommendation process."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "aV4zy0q8u9jy",
"outputId": "8ea9e69c-11ee-4d5c-8b56-bcbef4a4f0fd"
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
":19: LangGraphDeprecationWarning: Initializing StateGraph without state_schema is deprecated. Please pass in an explicit state_schema instead of just an input and output schema.\n",
" workflow = StateGraph(\n"
]
}
],
"source": [
"from langgraph.graph import StateGraph, END\n",
"from typing import TypedDict, Annotated\n",
"\n",
"# Define the input and output types\n",
"class UserInput(TypedDict):\n",
" user_input: str\n",
"\n",
"class MovieOutput(TypedDict):\n",
" result: str\n",
"\n",
"# Define the workflow\n",
"def run_crew(state):\n",
" user_input = state['user_input']\n",
" history = chat_history.messages\n",
" result = movie_crew.kickoff(inputs={'user_input': user_input, 'chat_history': history})\n",
" return {\"result\": result}\n",
"\n",
"# Create the workflow\n",
"workflow = StateGraph(\n",
" input=UserInput,\n",
" output=MovieOutput\n",
")\n",
"\n",
"# Add the node\n",
"workflow.add_node(\"run_crew\", run_crew)\n",
"\n",
"# Set the entrypoint\n",
"workflow.set_entry_point(\"run_crew\")\n",
"\n",
"# Add the edge to end the workflow\n",
"workflow.add_edge(\"run_crew\", END)\n",
"\n",
"# Compile the workflow\n",
"app = workflow.compile()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "1iuoXS2HvFHV"
},
"source": [
"## Interactive Recommendation Loop\n",
"\n",
"This is the main interaction point of the system. It runs a loop that:\n",
"1. Takes user input about movie preferences\n",
"2. Processes the input through the CrewAI and LangGraph workflow\n",
"3. Generates and displays personalized movie recommendations\n",
"4. Maintains a chat history for context in future recommendations"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "C6WD1KisvHtJ",
"outputId": "23de4bf9-10ef-461b-dda3-45e9e784f54a"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"What kind of movie are you in the mood for? (or 'quit' to exit): Romantic Comedy with strong female lead\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[92m19:23:26 - LiteLLM:INFO\u001b[0m: utils.py:2751 - \n",
"LiteLLM completion() model= gpt-3.5-turbo; provider = openai\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1m\u001b[95m# Agent:\u001b[00m \u001b[1m\u001b[92mPreference Analyst\u001b[00m\n",
"\u001b[95m## Task:\u001b[00m \u001b[92mAnalyze user preferences based on their input and chat history\u001b[00m\n",
"19:23:26 LiteLLM INFO \n",
"LiteLLM completion() model= gpt-3.5-turbo; provider = openai\n",
"19:23:27 httpx INFO HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[92m19:23:27 - LiteLLM:INFO\u001b[0m: utils.py:944 - Wrapper: Completed Call, calling success_handler\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"19:23:27 LiteLLM INFO Wrapper: Completed Call, calling success_handler\n",
"19:23:27 httpx INFO HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[92m19:23:27 - LiteLLM:INFO\u001b[0m: utils.py:2751 - \n",
"LiteLLM completion() model= gpt-3.5-turbo; provider = openai\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001b[1m\u001b[95m# Agent:\u001b[00m \u001b[1m\u001b[92mPreference Analyst\u001b[00m\n",
"\u001b[95m## Thought:\u001b[00m \u001b[92mI need to start by reviewing the user's input and chat history to gather information about their movie preferences. I will then use the Movie Database Lookup tool to gather more details about the movies mentioned or implied in the conversation. This will help me provide a detailed analysis of the user's movie preferences.\u001b[00m\n",
"\u001b[95m## Using tool:\u001b[00m \u001b[92mMovie Database Lookup\u001b[00m\n",
"\u001b[95m## Tool Input:\u001b[00m \u001b[92m\n",
"\"{\\\"name\\\": \\\"Inception\\\"}\"\u001b[00m\n",
"\u001b[95m## Tool Output:\u001b[00m \u001b[92m\n",
"1. Inception (2010)\n",
"2. Inception (2010)\n",
"3. Inception (2010)\n",
"4. Conception (2011)\n",
"5. Conception (2011)\u001b[00m\n",
"19:23:27 LiteLLM INFO \n",
"LiteLLM completion() model= gpt-3.5-turbo; provider = openai\n",
"19:23:28 httpx INFO HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[92m19:23:28 - LiteLLM:INFO\u001b[0m: utils.py:944 - Wrapper: Completed Call, calling success_handler\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"19:23:28 LiteLLM INFO Wrapper: Completed Call, calling success_handler\n",
"19:23:28 httpx INFO HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[92m19:23:28 - LiteLLM:INFO\u001b[0m: utils.py:2751 - \n",
"LiteLLM completion() model= gpt-3.5-turbo; provider = openai\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001b[1m\u001b[95m# Agent:\u001b[00m \u001b[1m\u001b[92mPreference Analyst\u001b[00m\n",
"\u001b[95m## Using tool:\u001b[00m \u001b[92mMovie Database Lookup\u001b[00m\n",
"\u001b[95m## Tool Input:\u001b[00m \u001b[92m\n",
"\"{\\\"name\\\": \\\"Conception\\\"}\"\u001b[00m\n",
"\u001b[95m## Tool Output:\u001b[00m \u001b[92m\n",
"1. Conception (2011)\n",
"2. Conception (2011)\n",
"3. Conception (2011)\n",
"4. Inception (2010)\n",
"5. Inception (2010)\u001b[00m\n",
"19:23:28 LiteLLM INFO \n",
"LiteLLM completion() model= gpt-3.5-turbo; provider = openai\n",
"19:23:30 httpx INFO HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[92m19:23:30 - LiteLLM:INFO\u001b[0m: utils.py:944 - Wrapper: Completed Call, calling success_handler\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"19:23:30 LiteLLM INFO Wrapper: Completed Call, calling success_handler\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[92m19:23:30 - LiteLLM:INFO\u001b[0m: utils.py:2751 - \n",
"LiteLLM completion() model= gpt-3.5-turbo; provider = openai\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001b[1m\u001b[95m# Agent:\u001b[00m \u001b[1m\u001b[92mPreference Analyst\u001b[00m\n",
"\u001b[95m## Final Answer:\u001b[00m \u001b[92m\n",
"Based on the user's interest in movies like \"Inception\" and \"Conception\", it seems they have a preference for complex, mind-bending films with intricate plots and themes. Both movies involve elements of mystery, science fiction, and psychological depth. The user may enjoy movies that challenge conventional storytelling and explore deep philosophical concepts. It is likely that they appreciate films that require active engagement and thought-provoking analysis.\u001b[00m\n",
"\n",
"\n",
"\u001b[1m\u001b[95m# Agent:\u001b[00m \u001b[1m\u001b[92mMovie Matcher\u001b[00m\n",
"\u001b[95m## Task:\u001b[00m \u001b[92mFind movies that match the analyzed user preferences\u001b[00m\n",
"19:23:30 LiteLLM INFO \n",
"LiteLLM completion() model= gpt-3.5-turbo; provider = openai\n",
"19:23:31 httpx INFO HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[92m19:23:31 - LiteLLM:INFO\u001b[0m: utils.py:944 - Wrapper: Completed Call, calling success_handler\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"19:23:31 LiteLLM INFO Wrapper: Completed Call, calling success_handler\n",
"19:23:32 httpx INFO HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[92m19:23:32 - LiteLLM:INFO\u001b[0m: utils.py:2751 - \n",
"LiteLLM completion() model= gpt-3.5-turbo; provider = openai\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001b[1m\u001b[95m# Agent:\u001b[00m \u001b[1m\u001b[92mMovie Matcher\u001b[00m\n",
"\u001b[95m## Thought:\u001b[00m \u001b[92mI should use the Movie Database Lookup tool to search for movies that match the user's preferences for complex, mind-bending films with intricate plots, mystery, science fiction, psychological depth, and deep philosophical concepts.\u001b[00m\n",
"\u001b[95m## Using tool:\u001b[00m \u001b[92mMovie Database Lookup\u001b[00m\n",
"\u001b[95m## Tool Input:\u001b[00m \u001b[92m\n",
"\"{\\\"description\\\": \\\"complex, mind-bending, mystery, science fiction, psychological depth, philosophical concepts\\\"}\"\u001b[00m\n",
"\u001b[95m## Tool Output:\u001b[00m \u001b[92m\n",
"1. Atomic Brain, The (1963)\n",
"2. Atomic Brain, The (1963)\n",
"3. Atomic Brain, The (1963)\n",
"4. Beautiful Mind, A (2001)\n",
"5. Beautiful Mind, A (2001)\u001b[00m\n",
"19:23:32 LiteLLM INFO \n",
"LiteLLM completion() model= gpt-3.5-turbo; provider = openai\n",
"19:23:32 httpx INFO HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[92m19:23:32 - LiteLLM:INFO\u001b[0m: utils.py:944 - Wrapper: Completed Call, calling success_handler\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"19:23:32 LiteLLM INFO Wrapper: Completed Call, calling success_handler\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[92m19:23:32 - LiteLLM:INFO\u001b[0m: utils.py:2751 - \n",
"LiteLLM completion() model= gpt-3.5-turbo; provider = openai\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001b[1m\u001b[95m# Agent:\u001b[00m \u001b[1m\u001b[92mMovie Matcher\u001b[00m\n",
"\u001b[95m## Final Answer:\u001b[00m \u001b[92m\n",
"1. Atomic Brain, The (1963)\n",
"2. Beautiful Mind, A (2001)\u001b[00m\n",
"\n",
"\n",
"\u001b[1m\u001b[95m# Agent:\u001b[00m \u001b[1m\u001b[92mRecommendation Generator\u001b[00m\n",
"\u001b[95m## Task:\u001b[00m \u001b[92mGenerate personalized movie recommendations based on matched movies\u001b[00m\n",
"19:23:32 LiteLLM INFO \n",
"LiteLLM completion() model= gpt-3.5-turbo; provider = openai\n",
"19:23:33 httpx INFO HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[92m19:23:33 - LiteLLM:INFO\u001b[0m: utils.py:944 - Wrapper: Completed Call, calling success_handler\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"19:23:33 LiteLLM INFO Wrapper: Completed Call, calling success_handler\n",
"19:23:33 httpx INFO HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[92m19:23:34 - LiteLLM:INFO\u001b[0m: utils.py:2751 - \n",
"LiteLLM completion() model= gpt-3.5-turbo; provider = openai\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001b[1m\u001b[95m# Agent:\u001b[00m \u001b[1m\u001b[92mRecommendation Generator\u001b[00m\n",
"\u001b[95m## Thought:\u001b[00m \u001b[92mI need to search for movie recommendations based on the movies \"Atomic Brain, The (1963)\" and \"Beautiful Mind, A (2001)\" to provide personalized recommendations.\u001b[00m\n",
"\u001b[95m## Using tool:\u001b[00m \u001b[92mMovie Database Lookup\u001b[00m\n",
"\u001b[95m## Tool Input:\u001b[00m \u001b[92m\n",
"\"{\\\"name\\\": \\\"Movie Recommendations based on Atomic Brain, The (1963)\\\"}\"\u001b[00m\n",
"\u001b[95m## Tool Output:\u001b[00m \u001b[92m\n",
"1. Atomic Brain, The (1963)\n",
"2. Atomic Brain, The (1963)\n",
"3. Atomic Brain, The (1963)\n",
"4. The Brain (1969)\n",
"5. The Brain (1969)\u001b[00m\n",
"19:23:34 LiteLLM INFO \n",
"LiteLLM completion() model= gpt-3.5-turbo; provider = openai\n",
"19:23:34 httpx INFO HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[92m19:23:34 - LiteLLM:INFO\u001b[0m: utils.py:944 - Wrapper: Completed Call, calling success_handler\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"19:23:34 LiteLLM INFO Wrapper: Completed Call, calling success_handler\n",
"19:23:34 httpx INFO HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[92m19:23:34 - LiteLLM:INFO\u001b[0m: utils.py:2751 - \n",
"LiteLLM completion() model= gpt-3.5-turbo; provider = openai\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001b[1m\u001b[95m# Agent:\u001b[00m \u001b[1m\u001b[92mRecommendation Generator\u001b[00m\n",
"\u001b[95m## Using tool:\u001b[00m \u001b[92mMovie Database Lookup\u001b[00m\n",
"\u001b[95m## Tool Input:\u001b[00m \u001b[92m\n",
"\"{\\\"name\\\": \\\"Movie Recommendations based on Beautiful Mind, A (2001)\\\"}\"\u001b[00m\n",
"\u001b[95m## Tool Output:\u001b[00m \u001b[92m\n",
"1. Beautiful Mind, A (2001)\n",
"2. Beautiful Mind, A (2001)\n",
"3. Beautiful Mind, A (2001)\n",
"4. Crazy/Beautiful (2001)\n",
"5. Crazy/Beautiful (2001)\u001b[00m\n",
"19:23:34 LiteLLM INFO \n",
"LiteLLM completion() model= gpt-3.5-turbo; provider = openai\n",
"19:23:35 httpx INFO HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[92m19:23:35 - LiteLLM:INFO\u001b[0m: utils.py:944 - Wrapper: Completed Call, calling success_handler\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"19:23:35 LiteLLM INFO Wrapper: Completed Call, calling success_handler\n",
"\n",
"\n",
"\u001b[1m\u001b[95m# Agent:\u001b[00m \u001b[1m\u001b[92mRecommendation Generator\u001b[00m\n",
"\u001b[95m## Final Answer:\u001b[00m \u001b[92m\n",
"Based on \"Atomic Brain, The (1963)\" and \"Beautiful Mind, A (2001)\", here are personalized movie recommendations:\n",
"1. The Brain (1969)\n",
"2. Crazy/Beautiful (2001)\u001b[00m\n",
"\n",
"\n",
"Recommendation: Based on \"Atomic Brain, The (1963)\" and \"Beautiful Mind, A (2001)\", here are personalized movie recommendations:\n",
"1. The Brain (1969)\n",
"2. Crazy/Beautiful (2001)\n",
"\n",
"Current Chat History:\n",
"HumanMessage: Romantic Comedy with strong female lead...\n",
"AIMessage: Based on \"Atomic Brain, The (1963)\" and \"Beautiful...\n",
"What kind of movie are you in the mood for? (or 'quit' to exit): quit\n"
]
}
],
"source": [
"def message_to_dict(message):\n",
" return {\n",
" \"type\": \"human\" if isinstance(message, HumanMessage) else \"ai\",\n",
" \"content\": message.content\n",
" }\n",
"\n",
"while True:\n",
" user_input = input(\"What kind of movie are you in the mood for? (or 'quit' to exit): \")\n",
" if user_input.lower() == 'quit' or user_input.lower() == 'exit':\n",
" break\n",
"\n",
" chat_history.add_user_message(user_input)\n",
"\n",
" # Convert chat history to a serializable format\n",
" serializable_history = [message_to_dict(msg) for msg in chat_history.messages]\n",
"\n",
" # Run the crew with the current user input and serializable chat history\n",
" result = movie_crew.kickoff(\n",
" inputs={\n",
" \"user_input\": user_input,\n",
" \"chat_history\": serializable_history\n",
" }\n",
" )\n",
"\n",
" # The result should now be a string containing the final recommendations\n",
" recommendation_text = str(result)\n",
"\n",
" print(f\"Recommendation: {recommendation_text}\")\n",
" chat_history.add_ai_message(recommendation_text)\n",
"\n",
" # Optionally, print the current chat history\n",
" print(\"\\nCurrent Chat History:\")\n",
" for message in chat_history.messages:\n",
" print(f\"{type(message).__name__}: {message.content[:50]}...\") # Print first 50 chars"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "l6TRlIVNvLFG"
},
"source": [
"## Cleanup\n",
"\n",
"Clears the Redis vector store and chat history"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "mVKTDoSevKfk",
"outputId": "0106a9e4-b3bd-4ee8-a11d-d73792a50eff"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Thank you for using our movie recommendation system!\n"
]
}
],
"source": [
"# Clear the vector store\n",
"vector_store.index.delete(drop=True)\n",
"\n",
"# Clear the chat history\n",
"chat_history.clear()\n",
"\n",
"print(\"Thank you for using our movie recommendation system!\")"
]
}
],
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"display_name": "redis-ai-res",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.11.9"
}
},
"nbformat": 4,
"nbformat_minor": 0
}