File size: 5,373 Bytes
b676995
 
5040539
b676995
 
 
 
5040539
b676995
 
 
 
 
 
add7f0f
5040539
add7f0f
 
 
5040539
1ef3b0b
5040539
 
 
b676995
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5040539
b676995
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5040539
b676995
 
 
 
 
 
5040539
b676995
 
 
 
 
 
 
5040539
b676995
5040539
b676995
3ef1980
b676995
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5040539
b676995
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5040539
b676995
 
1234b00
b676995
 
5040539
1234b00
 
 
645b0e8
1234b00
 
645b0e8
 
 
 
 
 
 
 
b676995
1234b00
645b0e8
1234b00
b676995
1234b00
645b0e8
 
76b62e6
645b0e8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import os
import streamlit as st
import logging
from langchain_groq import ChatGroq
from langchain_community.tools import ArxivQueryRun, WikipediaQueryRun, DuckDuckGoSearchRun
from langchain_community.utilities import ArxivAPIWrapper, WikipediaAPIWrapper
from langchain import hub
from langchain.agents import create_openai_tools_agent, AgentExecutor
from langchain.prompts import PromptTemplate
from langchain_community.vectorstores import FAISS
from langchain_huggingface import HuggingFaceEmbeddings
from langchain.tools import Tool
from pydantic import BaseModel, Field
from typing import List, Dict

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logging.info("Streamlit app started")

# Load the API key
groq_api_key = "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" #os.getenv("GROQ_API_KEY")
if not groq_api_key:
    logging.error("GROQ API key is missing!")

# Define a query condensing template
condense_prompt_template = PromptTemplate(
    input_variables=["original_question", "conversation_history"],
    template="""
        Given the conversation history below, condense the user's query into a clear and specific question.
        Conversation History:
        {conversation_history}
        Original Question:
        {original_question}
        Condensed Question:"""
)

def condense_query(llm_model, original_question, conversation_history):
    prompt = condense_prompt_template.format(
        original_question=original_question,
        conversation_history=conversation_history
    )
    resp = llm_model.predict(prompt)
    return resp.strip()

# Initialize HuggingFace embeddings and FAISS vectorstore
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
vector_db = FAISS.load_local("faiss_index", embeddings, allow_dangerous_deserialization=True)
retriever_tool = vector_db.as_retriever()

# Define input/output models for OpenAI function-calling compliance
class RetrieveDocumentsInput(BaseModel):
    query: str = Field(..., description="Query to retrieve relevant documents.")

class RetrieveDocumentsOutput(BaseModel):
    documents: List[Dict[str, str]] = Field(..., description="List of retrieved documents, each containing 'content'.")

# Wrapper function for FAISS retriever
def retrieve_documents(query: str) -> List[Dict[str, str]]:
    results = retriever_tool.get_relevant_documents(query)
    return [{"content": doc.page_content} for doc in results]

# Define the FAISS tool
faiss_tool = Tool(
    name="retrieve_documents",
    description="Retrieve documents from FAISS vectorstore.",
    func=retrieve_documents,
)

# Initialize tools
arxiv_wrapper = ArxivAPIWrapper(top_k_results=1, doc_content_chars_max=250)
arxiv_tool = ArxivQueryRun(api_wrapper=arxiv_wrapper)

wiki_wrapper = WikipediaAPIWrapper(top_k_results=1, doc_content_chars_max=250)
wiki_tool = WikipediaQueryRun(api_wrapper=wiki_wrapper)

search_tool = DuckDuckGoSearchRun(name="Search")
tools = [arxiv_tool, wiki_tool, search_tool, faiss_tool]

# Set up the agent
prompt = hub.pull("hwchase17/openai-functions-agent")
llm = ChatGroq(model="mixtral-8x7b-32768", api_key=groq_api_key, streaming=True)
agent = create_openai_tools_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# Initialize session state for messages
if 'messages' not in st.session_state:
    st.session_state['messages'] = [{"role": "assistant", "content": "How can I help you?"}]

# Display chat messages
for msg in st.session_state['messages']:
    st.chat_message(msg["role"]).write(msg["content"])

# Extract conversation history
conversation_history = "\n".join(
    [f"{msg['role']}: {msg['content']}" for msg in st.session_state['messages']]
)

# CSS for input box at bottom of screen
st.markdown("""
    <style>
    .fixed-bottom-input-container {
        position: fixed;
        bottom: 0;
        width: 100%;
        background-color: white;
        padding: 10px 0;
        border-top: 1px solid #ddd;
    }
    .fixed-bottom-input {
        width: 100%;
        padding: 10px;
        font-size: 16px;
    }
    </style>
""", unsafe_allow_html=True)

# Chat input and processing
user_input = st.text_input("Type your message here...", key="user_input", label_visibility="collapsed")

# If there's input, process it
if user_input:
    condensed_question = condense_query(llm, user_input, conversation_history)
    
    # Placeholder for streaming the assistant's response
    response_placeholder = st.empty()
    response_text = ""
    
    # Stream response tokens
    for token in agent_executor.stream({"input": condensed_question}):
        # Ensure the token contains the "output" key
        if "output" in token:
            response_text += token["output"]
            response_placeholder.write(response_text)
        else:
            logging.warning("Received token without 'output' key: %s", token)
    
    # Continue with the chat history and message updates
    st.session_state.messages.append({"role": "user", "content": user_input})
    st.session_state.messages.append({"role": "assistant", "content": response_text})
    
    # Display the user input and response
    st.chat_message("user").write(user_input)
    st.chat_message("assistant").write(response_text)
    
    # Reset the input box content
    user_input = ""