Tuana commited on
Commit
c325552
·
1 Parent(s): 750c3a7

full working app

Browse files
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ .env
2
+ __pycache__
3
+ .DS*
app.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from annotated_text import annotation
3
+ from json import JSONDecodeError
4
+ import logging
5
+ from markdown import markdown
6
+ import requests
7
+
8
+ import streamlit as st
9
+
10
+ from utils.haystack import query, start_haystack
11
+ from utils.ui import reset_results, set_initial_state, sidebar
12
+ from utils.config import TWITTER_BEARER_TOKEN, SERPER_KEY, OPENAI_API_KEY
13
+
14
+ set_initial_state()
15
+
16
+ sidebar()
17
+
18
+ st.write("# 👩 What would they have tweeted about this?")
19
+
20
+ # if st.session_state.get("OPENAI_API_KEY"):
21
+ if "last_k_tweets" not in st.session_state:
22
+ st.session_state["last_k_tweets"] = st.slider("How many tweets should we retrieve?", value=10, step=10, max_value=100, min_value=10, on_change = reset_results)
23
+
24
+ agent = start_haystack(openai_key=OPENAI_API_KEY, twitter_bearer=TWITTER_BEARER_TOKEN, serper_key=SERPER_KEY, last_k_tweets=st.session_state["last_k_tweets"])
25
+ # st.session_state["api_keys_configured"] = True
26
+
27
+ # Search bar
28
+ question = st.text_input("If the twitter account tuanacelik were to write a tweet in their style about climate change, what would it be?", on_change=reset_results)
29
+ run_pressed = st.button("Generate tweet")
30
+ # else:
31
+ # st.write("Please provide your OpenAI Key to start using the application")
32
+ # st.write("If you are using a smaller screen, open the sidebar from the top left to provide your OpenAI Key 🙌")
33
+
34
+ # if st.session_state.get("api_keys_configured"):
35
+ run_query = (
36
+ run_pressed or question != st.session_state.question
37
+ )
38
+ if run_query and question:
39
+ reset_results()
40
+ st.session_state.question = question
41
+ with st.spinner("🔎"):
42
+ try:
43
+ st.session_state.result = query(agent, question)
44
+ except JSONDecodeError as je:
45
+ st.error(
46
+ "👓    An error occurred reading the results. Is the document store working?"
47
+ )
48
+ except Exception as e:
49
+ logging.exception(e)
50
+ st.error("🐞    An error occurred during the request.")
51
+
52
+ if st.session_state.result:
53
+ result = st.session_state.result
54
+ st.write(result["answers"][0].answer)
55
+
custom_nodes/__init__.py ADDED
File without changes
custom_nodes/tweet_retriever.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ from haystack.nodes import BaseComponent
3
+
4
+ class TwitterRetriever(BaseComponent):
5
+ outgoing_edges = 1
6
+
7
+ def __init__(self, bearer_token: str, last_k_tweets: int = 10):
8
+ self.headers = {"Authorization": "Bearer {}".format(bearer_token)}
9
+ self.last_k_tweets = last_k_tweets
10
+
11
+ def run(self, query: str, last_k_tweets: int = None):
12
+ if last_k_tweets is None:
13
+ last_k_tweets = self.last_k_tweets
14
+
15
+ url = f"https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name={query}&count={last_k_tweets}"
16
+ try:
17
+ response = requests.request("GET", url, headers=self.headers)
18
+ twitter_stream = ""
19
+ for tweet in response.json():
20
+ print(tweet)
21
+ twitter_stream += tweet["text"] + '\n'
22
+ except Exception as e:
23
+ twitter_stream = ["Please make sure you are providing a correct, public twitter account"]
24
+
25
+ output = {
26
+ "results": f"{twitter_stream}",
27
+ "username": query,
28
+ }
29
+ return output
30
+
31
+ def run_batch(self):
32
+ pass
logo/haystack-logo-colored.png ADDED
prompts/lfqa.yaml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ description: A long form question answering prompt
2
+ meta:
3
+ authors:
4
+ - Tuana
5
+ name: Tuana/lfqa
6
+ tags:
7
+ - question-answering
8
+ text: |
9
+ Synthesize a comprehensive answer from the following most relevant paragraphs and the given question.
10
+ Provide a clear and concise response that summarizes the key points and information presented in the paragraphs.
11
+ Your answer should be in your own words and be no longer than 50 words.
12
+
13
+ Paragraphs: {documents}
14
+
15
+ Question: {query}
16
+
17
+ Answer:
18
+ version: 0.1.1
prompts/twitter_agent.yaml ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ description: An experimental Agent prompt for tweet creations based on twitter voice or last tweets
2
+ meta:
3
+ authors:
4
+ - Tuana
5
+ name: Tuana/twitter-agent
6
+ tags:
7
+ - agent
8
+ text: |
9
+ You are a helpful and knowledgeable agent. To achieve your goal of generating new tweets for twitter users, you have access to the following tools:
10
+ {tool_names_with_descriptions}
11
+ To answer questions, you'll need to go through multiple steps involving step-by-step thinking and selecting appropriate tools and their inputs; tools
12
+ will respond with observations. When you are ready for a final answer, respond with the `Final Answer:`
13
+ Use the following format:
14
+ Question: the question to be answered
15
+ Thought: Reason if you have the final answer. If yes, answer the question. If not, find out the missing information needed to answer it.
16
+ Tool: pick one of {tool_names}
17
+ Tool Input: the input for the tool
18
+ Observation: the tool will respond with the result
19
+ ...
20
+
21
+ Final Answer: the final answer to the question, make it a catchy tweet with the users voice
22
+ Thought, Tool, Tool Input, and Observation steps can be repeated multiple times, but sometimes we can find an answer in the first pass
23
+ ---
24
+
25
+ Question: {query}
26
+ Thought: Let's think step-by-step, I first need to
27
+ version: 0.1.1
prompts/twitter_voice.yaml ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ description: An experimental few-shot prompt to syntehsize the 'twitter voice' of a uesrname
2
+ meta:
3
+ authors:
4
+ - Tuana
5
+ name: Tuana/twitter-voice
6
+ tags:
7
+ - summarization
8
+ text: |
9
+ You will be given a twitter stream belonging to a specific profile. Answer with a summary of what they've lately been tweeting about and in what languages.
10
+ You may go into some detail about what topics they tend to like tweeting about. Please also mention their overall tone, for example: positive,
11
+ negative, political, sarcastic or something else.
12
+ Examples:
13
+
14
+ Twitter stream: RT @deepset_ai: Come join our Haystack server for our first Discord event tomorrow, a deepset AMA session with @rusic_milos @malte_pietsch…
15
+ RT @deepset_ai: Join us for a chat! On Thursday 25th we are hosting a 'deepset - Ask Me Anything' session on our brand new Discord. Come…
16
+ RT @deepset_ai: Curious about how you can use @OpenAI GPT3 in a Haystack pipeline? This week we released Haystack 1.7 with which we introdu…
17
+ RT @tuanacelik: So many updates from @deepset_ai today!
18
+
19
+ Summary: This user has lately been retweeting tweets fomr @deepset_ai. The topics of the tweets have been around the Haystack community, NLP and GPT. They've
20
+ been posting in English, and have had a positive, informative tone.
21
+
22
+ Twitter stream: I've directed my team to set sharper rules on how we deal with unidentified objects.\n\nWe will inventory, improve ca…
23
+ the incursion by China’s high-altitude balloon, we enhanced radar to pick up slower objects.\n \nBy doing so, w…
24
+ I gave an update on the United States’ response to recent aerial objects.
25
+ Summary: This user has lately been tweeting about having sharper rules to deal with unidentified objects and an incursuin by China's high-altitude
26
+ baloon. Their tweets have mostly been neutral but determined in tone. They mostly post in English.
27
+ Twitter stream: {tweets}
28
+
29
+ Summary:
30
+ version: 0.1.1
requirements.txt CHANGED
@@ -1,5 +1,5 @@
1
- farm-haystack==1.18.0
2
- streamlit==1.17.0
3
  markdown
4
  st-annotated-text
5
  python-dotenv
 
1
+ farm-haystack[inference]==1.18.0
2
+ streamlit==1.21.0
3
  markdown
4
  st-annotated-text
5
  python-dotenv
utils/__init__.py ADDED
File without changes
utils/config.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+
4
+ load_dotenv()
5
+ TWITTER_BEARER_TOKEN = os.getenv('TWITTER_BEARER_TOKEN')
6
+ SERPER_KEY = os.getenv('SERPER_KEY')
7
+ OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
utils/haystack.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import requests
3
+ from custom_nodes.tweet_retriever import TwitterRetriever
4
+ from haystack.agents import Agent, Tool
5
+ from haystack.nodes import PromptNode, PromptTemplate, WebRetriever
6
+ from haystack.pipelines import WebQAPipeline
7
+
8
+
9
+ def start_haystack(openai_key, twitter_bearer, serper_key, last_k_tweets):
10
+ prompt_node = PromptNode(
11
+ "text-davinci-003",
12
+ default_prompt_template=PromptTemplate(prompt="./prompts/lfqa.yaml"),
13
+ api_key=openai_key,
14
+ max_length=256,
15
+ )
16
+
17
+ web_retriever = WebRetriever(api_key=serper_key, top_search_results=2, mode="preprocessed_documents")
18
+ web_pipeline = WebQAPipeline(retriever=web_retriever, prompt_node=prompt_node)
19
+
20
+ twitter_retriver = TwitterRetriever(bearer_token=twitter_bearer, last_k_tweets=last_k_tweets)
21
+
22
+ pn = PromptNode(model_name_or_path="gpt-4", api_key=openai_key, stop_words=["Observation:"])
23
+ agent = Agent(prompt_node=pn, prompt_template="./prompts/twitter_agent.yaml")
24
+
25
+ tweet_retriver_tool = Tool(name="TwitterRetriever", pipeline_or_node=twitter_retriver,
26
+ description="Useful for when you need to retrive the latest tweets from a username to get an understanding of their style",
27
+ output_variable="results")
28
+ web_tool = Tool(name="WebSearch", pipeline_or_node=web_pipeline, description="Uesful for when you need to research the latest about a new topic")
29
+
30
+ agent.add_tool(tweet_retriver_tool)
31
+ agent.add_tool(web_tool)
32
+
33
+
34
+ st.session_state["haystack_started"] = True
35
+ return agent
36
+
37
+
38
+ @st.cache_data(show_spinner=True)
39
+ def query(_agent, question):
40
+ print("USER Q: {question}")
41
+ try:
42
+ result = _agent.run(question)
43
+ except Exception as e:
44
+ print(e)
45
+ result = ["Life isn't ideal sometimes and this Agent fails at doing a good job.. Maybe try another query..."]
46
+ return result
utils/ui.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from PIL import Image
3
+
4
+ def set_state_if_absent(key, value):
5
+ if key not in st.session_state:
6
+ st.session_state[key] = value
7
+
8
+ def set_initial_state():
9
+ set_state_if_absent("question", "Provide a Twitter username")
10
+ set_state_if_absent("result", None)
11
+ set_state_if_absent("haystack_started", False)
12
+
13
+ def reset_results(*args):
14
+ st.session_state.result = None
15
+
16
+ # def set_openai_api_key(api_key: str):
17
+ # st.session_state["OPENAI_API_KEY"] = api_key
18
+
19
+ def sidebar():
20
+ with st.sidebar:
21
+ image = Image.open('logo/haystack-logo-colored.png')
22
+ st.markdown("Thanks for coming to this 🤗 Space.\n\n"
23
+ "This is a project for fun and is a sister to the [should-i-follow](https://huggingface.co/spaces/deepset/should-i-follow) Space."
24
+ " There's a lot that can be improved to make this app better.\n\n"
25
+ "**Take results with a grain of** 🧂\n\n"
26
+ "")
27
+
28
+ st.markdown(
29
+ "## How to use\n"
30
+ # "1. Enter your [OpenAI API key](https://platform.openai.com/account/api-keys) below\n"
31
+ "1. Enter a query that includes a twitter username and be descriptive about wanting a tweet as a result.\n"
32
+ "2. Enjoy 🤗\n"
33
+ )
34
+
35
+ # api_key_input = st.text_input(
36
+ # "OpenAI API Key",
37
+ # type="password",
38
+ # placeholder="Paste your OpenAI API key here (sk-...)",
39
+ # help="You can get your API key from https://platform.openai.com/account/api-keys.",
40
+ # value=st.session_state.get("OPENAI_API_KEY", ""),
41
+ # )
42
+
43
+ # if api_key_input:
44
+ # set_openai_api_key(api_key_input)
45
+
46
+ st.markdown("---")
47
+ st.markdown(
48
+ "## How this works\n"
49
+ "This app was built with [Haystack](https://haystack.deepset.ai) using the"
50
+ " [`Agent`](https://docs.haystack.deepset.ai/docs/agent) custom [`PromptTemplates`](https://docs.haystack.deepset.ai/docs/prompt_node#templates)\n\n"
51
+ "as well as a custom `TweetRetriever` node\n"
52
+ " The source code is also on [GitHub](https://github.com/TuanaCelik/what-would-mother-say)"
53
+ " with instructions to run locally.\n"
54
+ "You can see how the `PromptNode` was set up [here](https://github.com/TuanaCelik/should-i-follow/blob/main/utils/haystack.py)")
55
+ st.markdown("---")
56
+ st.markdown("Made by [tuanacelik](https://twitter.com/tuanacelik)")
57
+ st.markdown("---")
58
+ st.markdown("""Thanks to [mmz_001](https://twitter.com/mm_sasmitha)
59
+ for open sourcing [KnowledgeGPT](https://knowledgegpt.streamlit.app/) which helped me with this sidebar 🙏🏽""")
60
+ st.image(image, width=250)