File size: 8,150 Bytes
c66a864
 
 
 
 
 
 
 
 
 
a4ab17e
 
 
 
c66a864
 
 
 
faa95b0
c66a864
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41b28fa
b18b667
 
 
 
 
 
 
 
41b28fa
c66a864
 
 
 
 
41b28fa
b18b667
c66a864
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41b28fa
c66a864
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17d0fa3
c66a864
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c2f0cda
 
92b59f0
 
c66a864
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92b59f0
c66a864
 
 
41b28fa
c66a864
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41b28fa
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
import gradio as gr
import os
from typing import List

from langchain.llms import OpenAIChat
from langchain.prompts import PromptTemplate
from langchain.chains.question_answering import load_qa_chain
from langchain.chains import LLMChain, ChatVectorDBChain
from langchain.callbacks.base import CallbackManager
from langchain.chains.chat_vector_db.prompts import CONDENSE_QUESTION_PROMPT as SYNTHESIS_PROMPT
from langchain.document_loaders import DirectoryLoader
from langchain.embeddings import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores.faiss import FAISS


response_prompt_template = """You are an AI assistant for the machine learning monitoring startup Arthur.  You are 
given the following extracted parts of a long document and a question. If the question
includes a request for code, provide a code block directly from the documentation. Don't try to make up an answer. If the question is not about Arthur, politely inform them that
you are tuned to only answer questions about Arthur.
=========
Example 1:
Question: What data do I need to send to Arthur?
=========
**3. What if my data is proprietary? Can I still use Arthur?**
Yes! Arthur offers on-premises installation for customers with data security requirements. By integrating Arthur
into your business's on-premises stack, you can be confident that all security requirements are met while still
getting the benefits of the computation and analytics Arthur provides.
***
**4. What if I don’t have ground truth labels for my data? Or what if I will have the ground truth labels in the future, 
but they are not available yet?**
You don't need ground truth labels to log your model's inferences with Arthur. 
If your ground truth labels become available after your model's inferences, whether seconds later or years later, 
Arthur can link these new ground truth values to your model's past predictions, linking the new values by ID to 
their corresponding inferences already in the Arthur system.
In the meantime, Arthur’s data drift metrics can offer leading indicators of model underperformance to keep you 
covered if your ground truth labels are delayed or never become available.
***
=========
Answer in Markdown:
The data you need to get into Arthur is only the inference data - no ground truth is needed, since it can be uploaded
at a later time. Also, if you have proprietary data, you can install Arthur on-premises to keep your own data security protocols.
=========
Now the real question:
Question: {question}
=========
{context}
=========
Answer in Markdown:"""
RESPONSE_PROMPT = PromptTemplate(
    template=response_prompt_template, input_variables=["context", "question"]
)


def get_docs_vectorstore(dir_name):
    loader = DirectoryLoader(dir_name)
    raw_documents = loader.load()
    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=1000,
        chunk_overlap=200,
    )
    documents = text_splitter.split_documents(raw_documents)
    embeddings = OpenAIEmbeddings()
    return FAISS.from_documents(documents, embeddings)


def get_langchain_agent(api_key):
    os.environ["OPENAI_API_KEY"] = api_key

    vectorstore = get_docs_vectorstore("files/arthur-docs-markdown")

    manager = CallbackManager([])
    question_manager = CallbackManager([])
    stream_manager = CallbackManager([])

    question_gen_llm = OpenAIChat(
        temperature=0,
        verbose=True,
        callback_manager=question_manager,
    )
    streaming_llm = OpenAIChat(
        streaming=True,
        callback_manager=stream_manager,
        verbose=True,
        temperature=0,
    )

    question_generator = LLMChain(
        llm=question_gen_llm, prompt=SYNTHESIS_PROMPT, callback_manager=manager
    )
    chat_response_generator = load_qa_chain(
        streaming_llm, chain_type="stuff", prompt=RESPONSE_PROMPT, callback_manager=manager
    )

    agent = ChatVectorDBChain(
        vectorstore=vectorstore,
        combine_docs_chain=chat_response_generator,
        question_generator=question_generator,
        callback_manager=manager,
        return_source_documents=True)

    os.environ["OPENAI_API_KEY"] = ""
    return agent


def get_source_doc(output):
    sources = output["source_documents"]
    assert len(sources) > 0
    source_document = sources[0]
    html_filename = source_document.metadata['source']
    source_doc_link = html_filename.replace('files/', '')
    source_doc_file = html_filename.replace('files/docs.arthur.ai/', '').replace('.html', '')
    with open(source_doc_file, 'r') as f:
        source_text = f.read()
    return source_text, source_doc_link


def chat(inp, history, agent):
    history = history or []
    result = agent({"question": inp, "chat_history": history})
    chat_result = result["answer"]
    source_doc, source_link = get_source_doc(result)
    response = ""
    for word in chat_result.split(" "):
        response += word + " "
        yield history + [(inp, response)], history + [(inp, response)], source_doc, source_link


def launch_ask_arthur(share=False):
    with gr.Blocks() as demo:
        with gr.Row():
            gr.Markdown("<h1><center>Ask Arthur</center></h1><br><h7><center>"
                        "This is an experimental document-retrieval question-answering system for asking questions about the Arthur product. <br> <br> Step 1: paste your OpenAI API key and hit the Register button."
                        "<br> <br> Step 2: type in the chat (shown on the left), and when you enter a message we fetch a relevant page from the Arthur documentation (shown on the right) "
                        "and prompt the Ask Arthur chatbot to answer your question using the page. "
                        "The chatbot's answers are entirely unverified, but it can sometimes offer a helpful summary of a "
                        "lot of information, or integrate information from multiple sources for you.</center></h7>")
            with gr.Column():
                openai_api_key_textbox = gr.Textbox(
                    placeholder="Paste your OpenAI API key (sk-...)",
                    show_label=False,
                    lines=1,
                    type="password",
                )
                submit_api_key_button = gr.Button(value="Register API Key", variant="secondary").style(full_width=False)
        with gr.Row().style():
            with gr.Column():
                chatbot = gr.Chatbot(
                    label="AskArthur chat history")
                message = gr.Textbox(
                    label="In the AskArthur chat, you can ask follow up questions or ask for clarifications!"
                          "\nReload the demo to change the topic of conversation and refresh the language model.",
                    placeholder="Enter your question here...",
                    lines=1,
                )
                submit_message = gr.Button(value="Send", variant="secondary").style(full_width=False)
                gr.Examples(label="Frequently asked questions about Arthur",
                    examples=[
                        "What default drift metrics does Arthur for deployed models?",
                        "How do I integrate Arthur with my existing ML stack?",
                        "How does Arthur monitor models without ground truth?",
                        "What if my data is proprietary, can I still use Arthur?",
                    ],
                    inputs=message,
                )
    
            with gr.Column():
                source_link = gr.Markdown()
                source_page = gr.Markdown()

        state = gr.State()
        agent_state = gr.State()
        submit_api_key_button.click(
            get_langchain_agent,
            inputs=[openai_api_key_textbox],
            outputs=[agent_state],
        )
        submit_message.click(chat, inputs=[message, state, agent_state], outputs=[chatbot, state, source_page, source_link])
        message.submit(chat, inputs=[message, state, agent_state], outputs=[chatbot, state, source_page, source_link])

    demo.queue().launch(share=share)


launch_ask_arthur()