WIP - Streamlit UI, Porting CLI

This commit is contained in:
ajaysi
2024-05-29 16:56:08 +05:30
parent bf83ff7a6b
commit 10b7326044
9 changed files with 72042 additions and 79 deletions

View File

@@ -1,5 +1,10 @@
# How to Use AI Content Generation Toolkit - Alwrity
# How to Alwrity - Getting Started
Alwrity assists content creators and digital marketers in generating, formatting, and uploading blog content with unprecedented efficiency.
Our toolkit integrates advanced AI models for text generation, image creation, and data analysis, streamlining your content creation pipeline and ensuring high-quality output with minimal effort.
---
1). [Visit alwrity.com](https://www.alwrity.com/ai-writing-tools), You will find AI content writing tools, which are Free & No-Signup.
**Note:** Although, this is limited, as is our wallet & Resources.

View File

@@ -6,11 +6,13 @@ import streamlit as st
# Load .env file
load_dotenv()
from lib.chatbot_custom.chatbot_local_docqa import alwrity_chat_docqa
from lib.utils.alwrity_streamlit_utils import (
blog_from_keyword, ai_agents_team,
blog_from_audio, write_story,
essay_writer, ai_news_writer,
ai_finance_ta_writer, ai_social_writer
ai_finance_ta_writer, ai_social_writer,
do_web_research, competitor_analysis,
)
# Custom CSS for styling
@@ -291,8 +293,10 @@ def main():
alwrity_brain()
with tab5:
st.title("🙎 Ask Alwrity 🤦")
st.write("Oh, you decided to talk to a chatbot? I guess even Netflix can't... Shall we get this over with?")
st.info("Chatbot")
st.markdown("Create a collection by uploading files (PDF, MD, CSV, etc), or crawl a data source (Websites, more sources coming soon.")
st.markdown("One can ask/chat, summarize and do semantic search over the uploaded data")
#alwrity_chat_docqa()
# Sidebar for prompt modification
st.sidebar.title("📝 Modify Prompts")
@@ -350,19 +354,17 @@ def content_planning_tools():
Provide few keywords to get Google, Neural, pytrends analysis. Know keywords, blog titles to target.
Generate months long content calender around given keywords.""")
options = [
"Keywords web research🤓",
"Competitor Analysis🧐",
"Give me content calendar 🥹🥹"
"Keywords Researcher",
"Competitor Analysis"
]
choice = st.selectbox("Select a content planning tool:", options, index=0, format_func=lambda x: f"🔍 {x}")
if st.button("Plan Content"):
if choice == "Keywords web research🤓":
do_web_research()
elif choice == "Competitor Analysis🧐":
competitor_analysis()
elif choice == "Give me content calendar 🥹🥹":
content_planning_agents()
if choice == "Keywords Researcher":
do_web_research()
elif choice == "Competitor Analysis":
competitor_analysis()
#elif choice == "Get Content Calender":
# planning_agents()
def alwrity_brain():

View File

@@ -187,7 +187,8 @@ def get_related_topics_and_save_csv(search_keywords):
data = pytrends.related_topics()
except Exception as err:
logger.error(f"Failed to get pytrends realted topics: {err}")
return
return None
# Extract data from the result
top_topics = list(data.values())[0]['top']
rising_topics = list(data.values())[0]['rising']

View File

@@ -7,6 +7,7 @@ from pathlib import Path
from metaphor_python import Metaphor
from datetime import datetime, timedelta
import streamlit as st
from loguru import logger
from tqdm import tqdm
from tabulate import tabulate
@@ -71,29 +72,46 @@ def metaphor_find_similar(similar_url):
raise
competitors = search_response.results
urls = {}
# Initialize lists to store titles and URLs
titles = []
urls = []
# Initialize lists to store titles, URLs, and contents
titles = []
urls = []
contents = []
# Extract titles, URLs, and contents from the competitors
for c in competitors:
print(c.title + ':' + c.url)
for acompetitor in tqdm(competitors, desc="Processing URL content", unit="competitor"):
titles.append(c.title)
urls.append(c.url)
# Simulate web content fetching and summarization (replace with actual logic)
all_contents = ""
try:
search_response = metaphor.search_and_contents(
acompetitor.url,
c.url,
type="keyword",
num_results=3
num_results=1
)
research_response = search_response.results
for r in research_response:
all_contents += r.text
c.text = summarize_competitor_content(all_contents) # Replace with actual summarization function
except Exception as err:
logger.error(f"Failed to do metaphor keyword/url research: {err}")
research_response = search_response.results
# Add a progress bar for the inner loop
for r in tqdm(research_response, desc=f"{acompetitor.url}", unit="research"):
all_contents += r.text
try:
acompetitor.text = summarize_competitor_content(all_contents)
except Exception as err:
logger.error(f"Failed to summarize_web_content: {err}")
c.text = f"Failed to summarize content: {err}"
contents.append(c.text)
# Create a DataFrame from the titles, URLs, and contents
df = pd.DataFrame({
"Title": titles,
"URL": urls,
"Content Summary": contents
})
# Display the DataFrame as a table
if not df.empty:
st.write("### Competitor Analysis Results")
st.table(df)
print_search_result(competitors)
return search_response
@@ -179,6 +197,12 @@ def print_search_result(contents_response):
tablefmt="fancy_grid",
colalign=["left", "left", "left"],
maxcolwidths=[20, 20, 70])
# Convert table_data to DataFrame
import pandas as pd
df = pd.DataFrame(table_data, columns=["URL", "Title", "Summary"])
import streamlit as st
st.table(df)
print(table)
# Save the combined table to a file
try:

View File

@@ -0,0 +1,104 @@
import time
import os
import joblib
import streamlit as st
import google.generativeai as genai
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
GOOGLE_API_KEY = os.environ.get('GOOGLE_API_KEY')
genai.configure(api_key=os.environ.get('GEMINI_API_KEY'))
# Constants
MODEL_ROLE = 'ai'
AI_AVATAR_ICON = '👄'
DATA_DIR = 'data/'
def history_chatbot():
# Ensure the data/ directory exists
os.makedirs(DATA_DIR, exist_ok=True)
# Generate a new chat ID
new_chat_id = f'{time.time()}'
# Load past chats if available
try:
past_chats = joblib.load(os.path.join(DATA_DIR, 'past_chats_list'))
except FileNotFoundError:
past_chats = {}
# Sidebar for past chats
with st.sidebar:
st.write('# Past Chats')
if 'chat_id' not in st.session_state:
st.session_state.chat_id = st.selectbox(
label='Pick a past chat',
options=[new_chat_id] + list(past_chats.keys()),
format_func=lambda x: past_chats.get(x, 'New Chat'),
placeholder='_'
)
else:
st.session_state.chat_id = st.selectbox(
label='Pick a past chat',
options=[new_chat_id, st.session_state.chat_id] + list(past_chats.keys()),
index=1,
format_func=lambda x: past_chats.get(x, 'New Chat' if x != st.session_state.chat_id else st.session_state.chat_title),
placeholder='_'
)
st.session_state.chat_title = f'ChatSession-{st.session_state.chat_id}'
# Load chat history if available
try:
st.session_state.messages = joblib.load(os.path.join(DATA_DIR, f'{st.session_state.chat_id}-st_messages'))
st.session_state.gemini_history = joblib.load(os.path.join(DATA_DIR, f'{st.session_state.chat_id}-gemini_messages'))
print('Loaded existing chat history')
except FileNotFoundError:
st.session_state.messages = []
st.session_state.gemini_history = []
print('Initialized new chat history')
# Configure the AI model
st.session_state.model = genai.GenerativeModel('gemini-pro')
st.session_state.chat = st.session_state.model.start_chat(history=st.session_state.gemini_history)
# Display past messages
for message in st.session_state.messages:
with st.chat_message(name=message['role'], avatar=message.get('avatar')):
st.markdown(message['content'])
# Handle user input
if prompt := st.chat_input('Ask Alwrity...'):
if st.session_state.chat_id not in past_chats:
past_chats[st.session_state.chat_id] = st.session_state.chat_title
joblib.dump(past_chats, os.path.join(DATA_DIR, 'past_chats_list'))
# Display and save user message
with st.chat_message('user'):
st.markdown(prompt)
st.session_state.messages.append({'role': 'user', 'content': prompt})
# Send message to AI and stream the response
response = st.session_state.chat.send_message(prompt, stream=True)
full_response = ''
with st.chat_message(name=MODEL_ROLE, avatar=AI_AVATAR_ICON):
message_placeholder = st.empty()
for chunk in response:
for ch in chunk.text.split(' '):
full_response += ch + ' '
time.sleep(0.05)
message_placeholder.write(full_response + '')
message_placeholder.write(full_response)
# Save the AI response
st.session_state.messages.append({
'role': MODEL_ROLE,
'content': full_response,
'avatar': AI_AVATAR_ICON
})
st.session_state.gemini_history = st.session_state.chat.history
# Persist chat history to disk
joblib.dump(st.session_state.messages, os.path.join(DATA_DIR, f'{st.session_state.chat_id}-st_messages'))
joblib.dump(st.session_state.gemini_history, os.path.join(DATA_DIR, f'{st.session_state.chat_id}-gemini_messages'))

View File

@@ -0,0 +1,101 @@
import os
import streamlit as st
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, ServiceContext, Document
from llama_index.llms.openai import OpenAI
import openai
from pathlib import Path
from dotenv import load_dotenv
# Load environment variables
load_dotenv(Path("../../.env"))
openai.api_key = os.getenv("OPENAI_API_KEY")
def initialize_session_state():
"""Initialize the chat message history in session state."""
if "messages" not in st.session_state:
st.session_state.messages = [
{"role": "assistant", "content": f"Ask me a question about documents from {LOCAL_BRAIN_DATA} or from the Web."}
]
@st.cache_resource(show_spinner=False)
def load_data(input_dir):
"""Load and index documents from the specified directory."""
with st.spinner("Loading and indexing your docs hang tight! This should take 1-2 minutes."):
reader = SimpleDirectoryReader(input_dir=input_dir, recursive=True)
docs = reader.load_data()
service_context = ServiceContext.from_defaults(
llm=OpenAI(
model="gpt-3.5-turbo",
temperature=0.5,
system_prompt=(
"You are an expert on content & digital marketing and your job is to answer technical questions."
"Assume that all questions are related to provided documents, as context."
"Keep your answers technical and based on facts do not hallucinate features."
)
)
)
index = VectorStoreIndex.from_documents(docs, service_context=service_context)
return index
def display_chat_history():
"""Display the chat message history."""
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.write(message["content"])
def generate_response(prompt, chat_engine):
"""Generate a response from the chat engine and update the chat history."""
if prompt:
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("assistant"):
with st.spinner("Thinking..."):
response = chat_engine.chat(prompt)
st.write(response.response)
st.session_state.messages.append({"role": "assistant", "content": response.response})
def alwrity_chat_docqa():
"""Main function to run the Streamlit app."""
st.header("Ask Alwrity 💬 📚")
initialize_session_state()
option = st.radio(
"Choose Data Source To Ask From:",
("Ask Your Local Docs", "Ask Your PDFs", "Ask Your Videos", "Ask Your Audio Files")
)
if option == "Ask Your Local Docs":
input_dir = st.text_input("Enter the path to the folder:")
if input_dir:
st.session_state.input_dir = input_dir
elif option == "Ask Your PDFs":
pdf_file = st.file_uploader("Upload a PDF file or enter a URL:", type=["pdf"])
if pdf_file:
st.session_state.input_file = pdf_file
elif option == "Ask Your Videos":
video_dir = st.text_input("Enter the path to the video folder:")
if video_dir:
st.session_state.input_dir = video_dir
elif option == "Ask Your Audio Files":
audio_dir = st.text_input("Enter the path to the audio folder:")
if audio_dir:
st.session_state.input_dir = audio_dir
if 'input_dir' in st.session_state:
index = load_data(st.session_state.input_dir)
chat_engine = index.as_chat_engine(chat_mode="condense_question", verbose=True)
display_chat_history()
prompt = st.chat_input("Your question")
if st.session_state.messages[-1]["role"] != "assistant":
generate_response(prompt, chat_engine)
elif 'input_file' in st.session_state:
# Handle PDF file or URL input here
st.write("Handling PDF file or URL input is not implemented yet.")

View File

@@ -1,4 +1,6 @@
import os
import streamlit as st
from crewai import Agent, Task, Crew
from crewai_tools import SerperDevTool
from langchain_google_genai import ChatGoogleGenerativeAI
@@ -162,7 +164,7 @@ def create_agents(search_keywords, already_written_on):
def create_tasks(agents, search_keywords, already_written_on):
research_task = Task(
description=f"""Conduct web analysis on "{search_keywords}",for content calender.
Set the input parameter as : search_query""",
Set the input parameter 'search_query' to query""",
expected_output=f"""Provide comprehensive content calender ideas to Senior Content Strategist & planner - Ted XingPi""",
agent=agents[0] # Assign to the researcher agent
)
@@ -170,7 +172,7 @@ def create_tasks(agents, search_keywords, already_written_on):
google_trends_task = Task(
description=f"""Conduct Google Trends analysis, on keywords: {search_keywords}, from the file({os.getenv('SEARCH_SAVE_FILE')}).
Suggest blog titles for content calender. Recommend high-volume, low-competition keywords with strong user intent.
Set the input parameter as : file_path""",
Set the input parameter 'file_path' to {os.getenv('SEARCH_SAVE_FILE')}""",
expected_output=f"Provide comprehensive content calender ideas to Senior Content Strategist & planner - Ted XingPi",
agent=agents[1] # Assign to the researcher agent
)
@@ -189,7 +191,7 @@ def create_tasks(agents, search_keywords, already_written_on):
description=f"""Make sure the content calender is optimised for keywords: '{search_keywords}'.
Make sure the titles are unique, semantically unique and mitigate keyword cannabilization.
Use context & insights from Aisha Sharma, Ted XingPi & Sarah Qureshi.
Set the input parameter file_path to {already_written_on}
Set the input parameter 'file_path' to {already_written_on}
""",
expected_output=f"""Final content calender for the next 2 months. Targeting 5 articles per week.
Make sure to present the content calender in tabular format. Include details of how to use the content calender.
@@ -201,6 +203,8 @@ def create_tasks(agents, search_keywords, already_written_on):
def execute_tasks(agents, tasks):
""" WIP """
result = None
crew = Crew(
agents=agents,
tasks=tasks,
@@ -209,17 +213,23 @@ def execute_tasks(agents, tasks):
#memory=True,
language="en"
)
result = crew.kickoff()
return result
try:
result = crew.kickoff()
return result
except Exception as err:
print(err)
def ai_agents_planner(search_keywords):
already_written_on = os.path.join(os.getcwd(), "lib", "content_planning_calender", "content_already_planned.txt")
do_google_trends_analysis(search_keywords)
result = None
#setup_environment()
agents = create_agents(search_keywords, already_written_on)
tasks = create_tasks(agents, search_keywords, already_written_on)
result = execute_tasks(agents, tasks)
print("########## Final Output Result ############")
print(result)
try:
agents = create_agents(search_keywords, already_written_on)
tasks = create_tasks(agents, search_keywords, already_written_on)
result = execute_tasks(agents, tasks)
except Exception as err:
print(err)
st.markdown("### Final Content Calender:")
st.markdown(result)

View File

@@ -3,6 +3,7 @@ import streamlit as st
from pathlib import Path
import configparser
from datetime import datetime
import uuid
from rich import print
from lib.ai_web_researcher.gpt_online_researcher import gpt_web_researcher
@@ -29,43 +30,42 @@ def blog_from_keyword():
""" Input blog keywords, research and write a factual blog."""
st.markdown("<div class='sub-header'>Blog from Keywords</div>", unsafe_allow_html=True)
content_keywords = st.text_input('Enter Keywords/Blog Title',
blog_keywords = st.text_input('Enter Keywords/Blog Title',
help='Check your keywords against Google search, if you get good search results, you will get good content with Alwrity.',
placeholder='Shit in, Shit Out; Better keywords, better research, hence better content.\n👋 :')
if content_keywords and len(content_keywords.split()) < 2:
if blog_keywords and len(blog_keywords.split()) < 2:
st.error('🚫 Blog keywords should be at least two words long. Please try again.')
content_type = st.selectbox("Select content type:", ["Normal-length content", "Long-form content", "Experimental - AI Agents team"])
if st.button("Write Blog"):
# Clear the previous results from the screen
st.empty()
if content_keywords and len(content_keywords.split()) >= 2:
if blog_keywords and len(blog_keywords.split()) >= 2:
if content_type == "Normal-length content":
try:
short_blog = write_blog_from_keywords(content_keywords)
short_blog = write_blog_from_keywords(blog_keywords)
st.markdown(short_blog)
st.success(f"Successfully wrote blog on: {content_keywords}")
except Exception as err:
st.error(f"🚫 Failed to write blog on {content_keywords}, Error: {err}")
st.error(f"🚫 Failed to write blog on {blog_keywords}, Error: {err}")
elif content_type == "Long-form content":
try:
st.empty()
long_form_generator(content_keywords)
st.success(f"Successfully wrote long-form blog on: {content_keywords}")
long_form_generator(blog_keywords)
st.success(f"Successfully wrote long-form blog on: {blog_keywords}")
except Exception as err:
st.error(f"🚫 Failed to write blog on {content_keywords}, Error: {err}")
st.error(f"🚫 Failed to write blog on {blog_keywords}, Error: {err}")
elif content_type == "Experimental - AI Agents team":
try:
ai_agents_writers(content_keywords)
st.success(f"Successfully wrote content with AI agents on: {content_keywords}")
ai_agents_writers(blog_keywords)
st.success(f"Successfully wrote content with AI agents on: {blog_keywords}")
except Exception as err:
st.error(f"🚫 Failed to Write content with AI agents: {err}")
def ai_agents_team():
# Define options for AI Content Teams
st.title("🧚🐲 AI Agents Content Teams")
st.title("🧚🐲 Your AI Agents Teams")
st.markdown("""Alwrity offers AI agents team for content creators to easily modify them for their needs.
Abstracting tech & plumbing, easily define role, goal, task. Use different AI agents framework.""")
options = [
@@ -77,16 +77,33 @@ def ai_agents_team():
selected_team = st.selectbox("**Choose AI Agents Team:**", options)
if selected_team == "AI Content Ideation & Planning Team":
content_planning_agents()
st.title("AI Agents for Content Ideation")
plan_keywords = st.text_input(
"Enter Keywords to get 2 months content calender:",
placeholder="Enter keywords to generate AI content calendar:",
help="Enter at least two words for better results."
)
if st.button("Get calender"):
if plan_keywords and len(plan_keywords.split()) >= 2:
with st.spinner("Get Content Plan..."):
try:
plan_content = ai_agents_planner(plan_keywords)
st.success(f"Successfully generated content plan for: {plan_keywords}")
st.markdown(plan_content)
except Exception as err:
st.error(f"Failed to generate content plan: {err}")
else:
st.error("🚫 Single keywords are just too vague. Try again.")
elif selected_team == "AI Content Creation Team":
content_creation_agents()
content_agents()
def content_creation_agents():
st.markdown("<div class='sub-header'>AI Agents Team for Content Creation</div>", unsafe_allow_html=True)
def content_agents():
st.markdown("AI Agents Team for Content Writing")
content_keywords = st.text_input(
"Enter Main Domain Keywords of your business:",
placeholder="Better keywords, Better content calendar:",
placeholder="Better keywords, Better content. Get keywords from Google search",
help="These keywords define your main business sector, blogging niche, Industry, domain etc"
)
@@ -103,27 +120,6 @@ def content_creation_agents():
st.error("🚫 Single keywords are just too vague. Try again.")
def content_planning_agents():
st.markdown("<div class='sub-header'>AI Agents Team for Content Ideation</div>", unsafe_allow_html=True)
content_keywords = st.text_input(
"Enter Main Domain Keywords of your business:",
placeholder="Better keywords will generate better content calendar:",
help="Enter at least two words for better results."
)
if st.button("Generate Content Plan"):
if content_keywords and len(content_keywords.split()) >= 2:
with st.spinner("Generating Content Plan..."):
try:
plan_content = ai_agents_planner(content_keywords)
st.success(f"Successfully generated content plan for: {content_keywords}")
st.markdown(plan_content)
except Exception as err:
st.error(f"Failed to generate content plan: {err}")
else:
st.error("🚫 Single keywords are just too vague. Try again.")
def blog_from_audio():
"""
@@ -372,6 +368,52 @@ def ai_news_writer():
st.error(f"Failed to generate news report: {err}")
def competitor_analysis():
st.title("Competitor Analysis")
st.markdown("""**Use Cases:**
- Know similar companies and alternatives for the given URL.
- Write listicles, similar companies, Top tools, alternative-to, similar products, similar websites, etc.
[Read More Here](https://docs.exa.ai/reference/company-analyst)
""")
similar_url = st.text_input("👋 Enter a single valid URL for web analysis:",
placeholder="Provide a competitor's URL and get details of similar/alternative companies.")
if st.button("Analyze"):
if similar_url:
try:
st.info(f"Starting analysis for the URL: {similar_url}")
with st.spinner("Performing competitor analysis..."):
result = metaphor_find_similar(similar_url)
st.success("Analysis completed successfully!")
st.write(result)
except Exception as err:
st.error(f"✖ 🚫 Failed to do similar search.\nError: {err}")
else:
st.error("Please enter a valid URL.")
def do_web_research():
""" Input keywords and do web research and present a report."""
st.title("Web Research Assistant")
st.write("Enter keywords for web research. The keywords should be at least three words long.")
search_keywords = st.text_input("Search Keywords", placeholder="Enter keywords for web research...")
if st.button("Start Web Research"):
if search_keywords and len(search_keywords.split()) >= 3:
try:
st.info(f"Starting web research on given keywords: {search_keywords}")
with st.spinner("Performing web research..."):
web_research_result = gpt_web_researcher(search_keywords)
st.success("Web research completed successfully!")
st.write(web_research_result)
except Exception as err:
st.error(f"ERROR: Failed to do web research: {err}")
else:
st.warning("Search keywords should be at least three words long. Please try again.")
def ai_finance_ta_writer():
st.markdown("<div class='sub-header'>AI Financial Technical Analysis Writer</div>", unsafe_allow_html=True)

File diff suppressed because one or more lines are too long