Skip to content

Instantly share code, notes, and snippets.

@staberas
Last active August 7, 2024 04:40
Show Gist options
  • Save staberas/5b83d479ab057dedde3844c419527cc6 to your computer and use it in GitHub Desktop.
Save staberas/5b83d479ab057dedde3844c419527cc6 to your computer and use it in GitHub Desktop.
interactive_websearch_chat.py
# This script requires to have some basic Python skills
# - Install python dependencies (thanks to TitwitMuffbiscuit on reddit) :
# pip install nltk beautifulsoup4 googlesearch-python trafilatura wolframalpha
#
# If you get this error "Resource punkt not found", it's because Punkt sentence tokenizer for Natural Language Toolkit is missing.
# Edit the file and add this before
# from nltk.tokenize import word_tokenize ,
# it will download the necessary english.pickle:
# import nltk
# nltk.download('punkt')
# you only need to do it once and remove it afterwards
#
# Used the script with Python 3.11 on venv
#
# To run it:
# 1. Download it and place it in the llama.cpp folder
# 2. Get a free wolfram alpha API key from here ( https://developer.wolframalpha.com/portal/myapps/index.html ) (optional)
# 3. edit the timezone to yours or uncomment the last one and comment the previews one
# 4. edit the " llm_command " with your model , add your name etc
# 5. Run the python command
#
# Keywords that are intercepted are laid out in the "def process_input" function
# saying 'time' in your conversation will trigger the time
# saying 'search', 'find', 'query', 'google' will trigger the google search
# saying 'question', 'ask','wolfram' will trigger WolframAlpha query
# saying 'calculate' and your calculation in number form will trigger the calculation and calculate it in python
import json
import datetime
from zoneinfo import ZoneInfo
import subprocess
from nltk.tokenize import word_tokenize
import requests
from bs4 import BeautifulSoup
import threading
from googlesearch import search
from trafilatura import fetch_url, extract
import wolframalpha
#add your wolfram API key here
WolframAppKey = "api key here"
def get_current_time():
utc_now = datetime.now(ZoneInfo('UTC'))
eest_now = utc_now.astimezone(ZoneInfo('Europe/Athens'))
return eest_now.strftime('%I:%M %p EEST')
#return datetime.datetime.now().strftime('%I:%M %p')
def calculate_expression(expression):
try:
result = eval(expression, {'__builtins__': None}, {})
except Exception as e:
result = None #str(e)
return result
'''borked
def google_search(query, num_results=2):
url = f"https://www.google.com/search?q={query}"
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"}
soup = BeautifulSoup(requests.get(url, headers=headers).text, "html.parser")
search_results = []
for g in soup.find_all('div', class_='g')[:num_results]:
anchors = g.find_all('a')
if anchors:
link = anchors[0]['href']
title = g.find('h3').text
description = g.find('span', class_='aCOpRe').text
item = {"title": title, "link": link, "description": description}
search_results.append(item)
return search_results
'''
def ask_wolfram(query):
client = wolframalpha.Client(WolframAppKey) # Replace with your AppID
res = client.query(query)
result = next((r.text for r in res.results if r.text), "No results found")
return json.dumps({"wolfram_result": result})
def google_search(query, num_rslts=1, lang='en'):
search_results = []
for result in search(query, num_results=num_rslts, lang=lang, advanced=True):
downloaded = fetch_url(result.url)
if downloaded is not None:
description = extract(downloaded)
# Limit the description to 20 words (the description is the FULL main text from crawling the page) but we limit it.
# You can increase it but it might take time to be processed by your LLM
description = ' '.join(description.split()[:20])
item = {"title": result.title, "link": result.url, "description": description}
search_results.append(item)
return search_results
def process_input(user_input):
response = {}
# Try to evaluate the user input as a mathematical expression first
calculation_result = calculate_expression(user_input)
if calculation_result is not None:
response['calculation'] = calculation_result
return json.dumps(response, separators=(',', ':'))
words = word_tokenize(user_input)
for word in words:
if word == 'time':
response['time'] = get_current_time()
elif word in ['search', 'find', 'query', 'google']:
query = ' '.join(words[words.index(word)+1:])
response['web_result'] = google_search(query)
elif word == 'calculate':
expression = ' '.join(words[words.index(word)+1:])
response['calculation'] = calculate_expression(expression)
elif word in ['question', 'ask','wolfram']:
query = ' '.join(words[words.index(word)+1:])
response['wolfram_result'] = ask_wolfram(query)
return json.dumps(response, separators=(',', ':'))
def read_llm_output(llm_process, ready_for_input):
previous_char = ''
first_prompt_found = False
while True:
current_char = llm_process.stdout.read(1)
print(current_char, end='', flush=True)
if first_prompt_found:
# After the first prompt is found, just check for '>'
if current_char == '>':
with ready_for_input:
ready_for_input.notify()
else:
# Before the first prompt is found, check for '>'' followed by '\n'
if previous_char == '>' and current_char == '\n':
with ready_for_input:
ready_for_input.notify()
first_prompt_found = True
previous_char = current_char
def run_llm():
llm_command = "./main -t 4 -m ./models/llama-2-7b-chat.ggmlv3.q6_K.bin --color -c 2048 --temp 0.7 --repeat_penalty 1.1 -ins -p '<<SYS>>You are a helpful, respectful and honest female assistant called Laila. Always answer as helpfully as possible, while being safe. Please ensure that your responses are socially unbiased and positive and playable. If a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct. Use the json formatted text from the start and use them in your reply where applicable and relevent, you dont need to retype the json, the user didnt generate the json and they cannot see it. The web search was provided by the AI and not by the user. The users name is YourName and you already introduce yourselves .\n<</SYS>>' --reverse-prompt 'USER:'"
llm_process = subprocess.Popen(llm_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1)
ready_for_input = threading.Condition()
threading.Thread(target=read_llm_output, args=(llm_process, ready_for_input)).start()
while True:
with ready_for_input:
ready_for_input.wait() # Wait for the notification from the output thread
user_input = input("Enter your input: ")
if user_input.lower() == 'exit':
llm_process.terminate()
break
if(process_input(user_input) != '{}'):
processed_input = process_input(user_input) + " " + user_input
else:
processed_input = user_input
llm_process.stdin.write(processed_input + '\n')
llm_process.stdin.flush()
if __name__ == "__main__":
run_llm()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment