import logging import os import random from typing import Union import openai from dotenv import load_dotenv from flask import Flask, redirect, request from html5lib import HTMLParser from htmls import index_html, prompt_html_template load_dotenv() app = Flask(__name__) openai.api_key = os.getenv("OPENAI_KEY") logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") def format_message(message: str) -> list: """ Formats the message to send to chatgpt Args: message: The message to send to chatgpt Returns: The formatted message in a list Raises: None """ return [ { "role": "system", "content": ( "You are a very knowledgeable person with random knowledge for everything the humanity has ever done" ), }, {"role": "user", "content": message}, ] def extract_html_from_chatgpt_response(returned_string: str) -> Union[str, None]: """ Extracts the HTML from the chatgpt response Args: returned_string: The string returned by chatgpt Returns: The HTML if it is valid, None otherwise Raises: None """ validator = HTMLParser(strict=True) try: validator.parse(returned_string) logging.info("Valid HTML Found") return returned_string except Exception: match = returned_string.split("") if len(match) > 1: logging.info("No valid HTML found, trying to parse...") html = "" + match[1] html_match = html.split("") possible_html = html_match[0] + "" if "(topic)" not in possible_html: return possible_html def get_random_topic() -> str: """ Gets a random topic from chatgpt Args: None Returns: A random topic Raises: None """ try: completion = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=format_message( "Give me a random list of 10 topics to talk about in a comma separated list. Each topic should be less " "than 5 words long and should not contain commas. Be as creative as possible and choose topics from " "multiple genres and industries." ), ) topics = completion.choices[0].message.content.split(",") if len(topics) < 5: logging.info(f"The topics is too short: {topics}. Retrying...") return get_random_topic() logging.info(f"The topics list is: {topics}") topic = random.choice(topics) return topic.strip() except openai.error.RateLimitError: return get_random_topic() def get_chatgpt_html_response(message: str) -> str: """ Gets the response from chatgpt Args: message: The message to send to chatgpt Returns: The HTML response from chatgpt Raises: None """ logging.info(f"The topic to generate is {message}") try: completion = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=format_message( ( f"Give me information about {message}. Now, given this HTML: \n {prompt_html_template} \n replace " "(topic) with the name of the selected topic and (topic_info) with 5 parragraphs talking about the " "topic. Replace (related_links) with 10 links related to the topic. For these links, the href " "should be '/infinite?topic=(slugified_related_topic_name)' and the text should be the name of the " "related topic. The name of these topics should not be more than 5 words. Respond only the code" ) ), ) except openai.error.RateLimitError: return get_chatgpt_html_response(message) response = completion.choices[0].message.content html = extract_html_from_chatgpt_response(response) if not html: logging.info(f"No HTML was found. The response was {response}. Retrying...") return get_chatgpt_html_response(message) return html @app.route("/") def index(): return index_html @app.route("/start") def start(): random_topic = get_random_topic() return get_chatgpt_html_response(random_topic) @app.route("/infinite") def topic(): topic = request.args.get("topic", None) if not topic: return redirect("/start") unslugified_topic = topic.replace("-", " ").replace("_", " ").replace("%20", " ").replace("\n", " ") if len(unslugified_topic.split()) > 5: logging.info(f"The topic is too long: {unslugified_topic}") return "Error! Your topic should be < 5 words" return get_chatgpt_html_response(unslugified_topic)