Spaces:
Runtime error
Runtime error
full working app
Browse files- .gitignore +3 -0
- app.py +55 -0
- custom_nodes/__init__.py +0 -0
- custom_nodes/tweet_retriever.py +32 -0
- logo/haystack-logo-colored.png +0 -0
- prompts/lfqa.yaml +18 -0
- prompts/twitter_agent.yaml +27 -0
- prompts/twitter_voice.yaml +30 -0
- requirements.txt +2 -2
- utils/__init__.py +0 -0
- utils/config.py +7 -0
- utils/haystack.py +46 -0
- utils/ui.py +60 -0
.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.
|
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)
|