CoolFace
Apppublic

jaothan/DockerGenAI_Streamlit

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
chains.py252 linesDownload Raw Back to root
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