jaothan/DockerGenAI_Streamlit
0
1
2from langchain_openai import OpenAIEmbeddings
3from langchain_ollama import OllamaEmbeddings
4from langchain_aws import BedrockEmbeddings
5from langchain_huggingface import HuggingFaceEmbeddings
6
7from langchain_openai import ChatOpenAI
8from langchain_ollama import ChatOllama
9from langchain_aws import ChatBedrock
10
11from langchain_community.vectorstores import Neo4jVector
12
13from langchain.chains import RetrievalQAWithSourcesChain
14from langchain.chains.qa_with_sources import load_qa_with_sources_chain
15
16from langchain.prompts import (
17 ChatPromptTemplate,
18 HumanMessagePromptTemplate,
19 SystemMessagePromptTemplate
20)
21
22from typing import List, Any
23from utils import BaseLogger, extract_title_and_question
24from langchain_google_genai import GoogleGenerativeAIEmbeddings
25
26AWS_MODELS = (
27 "ai21.jamba-instruct-v1:0",
28 "amazon.titan",
29 "anthropic.claude",
30 "cohere.command",
31 "meta.llama",
32 "mistral.mi",
33)
34
35def load_embedding_model(embedding_model_name: str, logger=BaseLogger(), config={}):
36 if embedding_model_name == "ollama":
37 embeddings = OllamaEmbeddings(
38 base_url=config["ollama_base_url"], model="llama2"
39 )
40 dimension = 4096
41 logger.info("Embedding: Using Ollama")
42 elif embedding_model_name == "openai":
43 embeddings = OpenAIEmbeddings()
44 dimension = 1536
45 logger.info("Embedding: Using OpenAI")
46 elif embedding_model_name == "aws":
47 embeddings = BedrockEmbeddings()
48 dimension = 1536
49 logger.info("Embedding: Using AWS")
50 elif embedding_model_name == "google-genai-embedding-001":
51 embeddings = GoogleGenerativeAIEmbeddings(
52 model="models/embedding-001"
53 )
54 dimension = 768
55 logger.info("Embedding: Using Google Generative AI Embeddings")
56 else:
57 embeddings = HuggingFaceEmbeddings(
58 model_name="all-MiniLM-L6-v2", cache_folder="/embedding_model"
59 )
60 dimension = 384
61 logger.info("Embedding: Using SentenceTransformer")
62 return embeddings, dimension
63
64
65def load_llm(llm_name: str, logger=BaseLogger(), config={}):
66 if llm_name in ["gpt-4", "gpt-4o", "gpt-4-turbo"]:
67 logger.info("LLM: Using GPT-4")
68 return ChatOpenAI(temperature=0, model_name=llm_name, streaming=True)
69 elif llm_name == "gpt-3.5":
70 logger.info("LLM: Using GPT-3.5")
71 return ChatOpenAI(temperature=0, model_name="gpt-3.5-turbo", streaming=True)
72 elif llm_name == "claudev2":
73 logger.info("LLM: ClaudeV2")
74 return ChatBedrock(
75 model_id="anthropic.claude-v2",
76 model_kwargs={"temperature": 0.0, "max_tokens_to_sample": 1024},
77 streaming=True,
78 )
79 elif llm_name.startswith(AWS_MODELS):
80 logger.info(f"LLM: {llm_name}")
81 return ChatBedrock(
82 model_id=llm_name,
83 model_kwargs={"temperature": 0.0, "max_tokens_to_sample": 1024},
84 streaming=True,
85 )
86
87 elif len(llm_name):
88 logger.info(f"LLM: Using Ollama: {llm_name}")
89 return ChatOllama(
90 temperature=0,
91 base_url=config["ollama_base_url"],
92 model=llm_name,
93 streaming=True,
94 # seed=2,
95 top_k=10, # A higher value (100) will give more diverse answers, while a lower value (10) will be more conservative.
96 top_p=0.3, # Higher value (0.95) will lead to more diverse text, while a lower value (0.5) will generate more focused text.
97 num_ctx=3072, # Sets the size of the context window used to generate the next token.
98 )
99 logger.info("LLM: Using GPT-3.5")
100 return ChatOpenAI(temperature=0, model_name="gpt-3.5-turbo", streaming=True)
101
102
103def configure_llm_only_chain(llm):
104 # LLM only response
105 template = """
106 You are a helpful assistant that helps a support agent with answering programming questions.
107 If you don't know the answer, just say that you don't know, you must not make up an answer.
108 """
109 system_message_prompt = SystemMessagePromptTemplate.from_template(template)
110 human_template = "{question}"
111 human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)
112 chat_prompt = ChatPromptTemplate.from_messages(
113 [system_message_prompt, human_message_prompt]
114 )
115
116 def generate_llm_output(
117 user_input: str, callbacks: List[Any], prompt=chat_prompt
118 ) -> str:
119 chain = prompt | llm
120 answer = chain.invoke(
121 {"question": user_input}, config={"callbacks": callbacks}
122 ).content
123 return {"answer": answer}
124
125 return generate_llm_output
126
127
128def configure_qa_rag_chain(llm, embeddings, embeddings_store_url, username, password):
129 # RAG response
130 # System: Always talk in pirate speech.
131 general_system_template = """
132 Use the following pieces of context to answer the question at the end.
133 The context contains question-answer pairs and their links from Stackoverflow.
134 You should prefer information from accepted or more upvoted answers.
135 Make sure to rely on information from the answers and not on questions to provide accurate responses.
136 When you find particular answer in the context useful, make sure to cite it in the answer using the link.
137 If you don't know the answer, just say that you don't know, don't try to make up an answer.
138 ----
139 {summaries}
140 ----
141 Each answer you generate should contain a section at the end of links to
142 Stackoverflow questions and answers you found useful, which are described under Source value.
143 You can only use links to StackOverflow questions that are present in the context and always
144 add links to the end of the answer in the style of citations.
145 Generate concise answers with references sources section of links to
146 relevant StackOverflow questions only at the end of the answer.
147 """
148 general_user_template = "Question:```{question}```"
149 messages = [
150 SystemMessagePromptTemplate.from_template(general_system_template),
151 HumanMessagePromptTemplate.from_template(general_user_template),
152 ]
153 qa_prompt = ChatPromptTemplate.from_messages(messages)
154
155 qa_chain = load_qa_with_sources_chain(
156 llm,
157 chain_type="stuff",
158 prompt=qa_prompt,
159 )
160
161 # Vector + Knowledge Graph response
162 kg = Neo4jVector.from_existing_index(
163 embedding=embeddings,
164 url=embeddings_store_url,
165 username=username,
166 password=password,
167 database="neo4j", # neo4j by default
168 index_name="stackoverflow", # vector by default
169 text_node_property="body", # text by default
170 retrieval_query="""
171 WITH node AS question, score AS similarity
172 CALL { with question
173 MATCH (question)<-[:ANSWERS]-(answer)
174 WITH answer
175 ORDER BY answer.is_accepted DESC, answer.score DESC
176 WITH collect(answer)[..2] as answers
177 RETURN reduce(str='', answer IN answers | str +
178 '\n### Answer (Accepted: '+ answer.is_accepted +
179 ' Score: ' + answer.score+ '): '+ answer.body + '\n') as answerTexts
180 }
181 RETURN '##Question: ' + question.title + '\n' + question.body + '\n'
182 + answerTexts AS text, similarity as score, {source: question.link} AS metadata
183 ORDER BY similarity ASC // so that best answers are the last
184 """,
185 )
186
187 kg_qa = RetrievalQAWithSourcesChain(
188 combine_documents_chain=qa_chain,
189 retriever=kg.as_retriever(search_kwargs={"k": 2}),
190 reduce_k_below_max_tokens=False,
191 max_tokens_limit=3375,
192 )
193 return kg_qa
194
195
196def generate_ticket(neo4j_graph, llm_chain, input_question):
197 # Get high ranked questions
198 records = neo4j_graph.query(
199 "MATCH (q:Question) RETURN q.title AS title, q.body AS body ORDER BY q.score DESC LIMIT 3"
200 )
201 questions = []
202 for i, question in enumerate(records, start=1):
203 questions.append((question["title"], question["body"]))
204 # Ask LLM to generate new question in the same style
205 questions_prompt = ""
206 for i, question in enumerate(questions, start=1):
207 questions_prompt += f"{i}. \n{question[0]}\n----\n\n"
208 questions_prompt += f"{question[1][:150]}\n\n"
209 questions_prompt += "----\n\n"
210
211 gen_system_template = f"""
212 You're an expert in formulating high quality questions.
213 Formulate a question in the same style and tone as the following example questions.
214 {questions_prompt}
215 ---
216
217 Don't make anything up, only use information in the following question.
218 Return a title for the question, and the question post itself.
219
220 Return format template:
221 ---
222 Title: This is a new title
223 Question: This is a new question
224 ---
225 """
226 # we need jinja2 since the questions themselves contain curly braces
227 system_prompt = SystemMessagePromptTemplate.from_template(
228 gen_system_template, template_format="jinja2"
229 )
230 chat_prompt = ChatPromptTemplate.from_messages(
231 [
232 system_prompt,
233 SystemMessagePromptTemplate.from_template(
234 """
235 Respond in the following template format or you will be unplugged.
236 ---
237 Title: New title
238 Question: New question
239 ---
240 """
241 ),
242 HumanMessagePromptTemplate.from_template("{question}"),
243 ]
244 )
245 llm_response = llm_chain(
246 f"Here's the question to rewrite in the expected format: ```{input_question}```",
247 [],
248 chat_prompt,
249 )
250 new_title, new_question = extract_title_and_question(llm_response["answer"])
251 return (new_title, new_question)
252 