infrastructure/movie-search-app/pinecone_model.py (62 lines of code) (raw):
# Copyright 2024 Google LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import google.generativeai as genai
from typing import Iterable
from pinecone.grpc import PineconeGRPC as Pinecone
import logging
import os
from data_model import ChatMessage, State
import mesop as me
generation_config = {
"temperature": 1,
"top_p": 0.95,
"top_k": 64,
"max_output_tokens": 8192,
}
def configure_gemini():
state = me.state(State)
genai.configure(api_key=state.gemini_api_key)
def classify_intent(input: str) -> str:
configure_gemini()
model = genai.GenerativeModel(
model_name="gemini-1.5-flash-latest",
generation_config=generation_config,
system_instruction=[intent_prompt],
)
json_resp = model.generate_content(input)
logging.info(f"INTENT: {json_resp}")
return json_resp.text.replace("```", "").replace("json", "").strip()
def generate_embedding(input: str) -> list[float]:
result = genai.embed_content(
model="models/text-embedding-004",
content=input,
task_type="retrieval_document",
title="Embedding of single string")
return result
def get_movies(embedding: list[float]) -> dict:
state = me.state(State)
PINECONE_INDEX_NAME = os.environ.get("PINECONE_INDEX_NAME")
if PINECONE_INDEX_NAME is None:
PINECONE_INDEX_NAME = "netflix-index-01"
logging.warning("PINECONE_INDEX_NAME not set, using default: %s", PINECONE_INDEX_NAME)
pc = Pinecone(api_key=state.pinecone_api_key)
index = pc.Index(name=PINECONE_INDEX_NAME)
query_resp = index.query(vector=embedding, namespace="sandpaper", top_k=5, include_metadata=True)
movies_list = []
for match in query_resp.matches:
meta = match["metadata"]
movies_list.append({"title":meta["title"],"summary":meta["summary"],"director":meta["director"],"genre": meta["genre"],"actors": meta["actors"]})
return movies_list
def send_prompt_flash(input: str, history: list[ChatMessage],sys_instruction: list[str]) -> Iterable[str]:
configure_gemini()
model = genai.GenerativeModel(
model_name="gemini-1.5-flash-latest",
generation_config=generation_config,
system_instruction=sys_instruction,
)
chat_session = model.start_chat(
history=[
{"role": message.role, "parts": [message.content]} for message in history
]
)
for chunk in chat_session.send_message(input, stream=True):
yield chunk.text
intent_prompt = """
Answer the following questions as a Json string based solely on provided chat history. Do not assume anything that the user did not explicitly say.
isOnTopic: true or false, indicating whether the most recent query is on topic.
shouldRecommendMovie: true of false, indicating whether the user has asked for a movie or show recommendation and has given enough information to make a recommendation. If it is a follow up question related to a product or to a previous recommendation then it is true.
shouldRecommendMovieReasoning: A string explaning what information to obtain to make a movie or show recommendation.
summary: If isOnTopic is true, output a summary of what the user is looking for.
Examples
History: [{'role': 'user', 'content': "Hi"}]
Answer: {
"isOnTopic": true,
"shouldRecommendMovie": false,
"shouldRecommendMovieReasoning": "User has not mention what they are looking for.",
"summary": ""
}
History: [{'role': 'user', 'content': "Hi, I am looking for a movie about a spy changing faces."}]
Answer: {
"isOnTopic": true,
"shouldRecommendMovie": true,
"shouldRecommendMovieReasoning": "User is looking for a movie recommendation.",
"summary": "A movie about a spy changing faces."
}
Do not use markdown for the output, respond with only JSON
"""