[
  {
    "path": ".gitignore",
    "content": ".env\n/.streamlit/secrets.toml\n"
  },
  {
    "path": ".streamlit/config.toml",
    "content": "[theme]\nprimaryColor=\"#F63366\"\nbackgroundColor=\"#FFFFFF\"\nsecondaryBackgroundColor=\"#F0F2F6\"\ntextColor=\"#262730\"\nfont=\"sans serif\""
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to HormoziGPT\n\nFirst of all, thank you for considering contributing to HormoziGPT! I made this project as an experiment in creating artificial personalities based on existing content, so I appreciate any help and contributions from the community. Whether you're submitting a bug report, suggesting a new feature, or contributing code, your contributions are valuable and help improve the project.\n\n## How to Contribute\n\n### Reporting Bugs\n\nIf you encounter any bugs or issues while using HormoziGPT, please open an issue on the GitHub repository. When submitting a bug report, please include the following information:\n\n- A clear and descriptive title for the issue.\n- A detailed description of the issue, including steps to reproduce it.\n- Information about your environment, such as the operating system and Python version you're using.\n\n### Suggesting Enhancements\n\nIf you have an idea for a new feature or enhancement, please open an issue on the GitHub repository. When suggesting an enhancement, please include the following information:\n\n- A clear and descriptive title for the issue.\n- A detailed description of the proposed enhancement, including any benefits and potential use cases.\n- Any relevant examples or mockups, if applicable.\n\n### Contributing Code\n\nIf you'd like to contribute code to HormoziGPT, please follow these steps:\n\n1. Fork the HormoziGPT repository on GitHub.\n2. Clone your fork to your local machine.\n3. Create a new branch for your changes (e.g., `git checkout -b my-feature-branch`).\n4. Make your changes and commit them to your branch.\n5. Push your changes to your fork on GitHub.\n6. Open a pull request against the `main` branch of the HormoziGPT repository.\n\nWhen submitting a pull request, please include a clear and descriptive title and a detailed description of your changes. If your pull request addresses an existing issue, please reference the issue number in the description.\n\n## Code of Conduct\n\nI strive to create a welcoming and inclusive environment for all contributors. Please be respectful and considerate in your interactions with others.\n\n## Contact\n\nIf you have any questions or need assistance, please feel free to reach out to me directly. You can contact me via [GitHub](https://github.com/wombyz) or [email](mailto:admin@liamottley.com).\n\nThank you again for your interest in contributing to HormoziGPT, and I look forward to collaborating with you!\n"
  },
  {
    "path": "README.md",
    "content": "# HormoziGPT\n\nHormoziGPT is a chatbot application that simulates a conversation with Alex Hormozi. The chatbot provides valuable business advice and coaching to users, drawing from Alex's experience in customer acquisition, monetization, and scaling businesses. It also has access to transcripts of Alex's podcasts, which are used to provide context and support for the chatbot's responses.\n\n## Features\n\n- Engage in a conversation with a chatbot that emulates Alex Hormozi's communication style.\n- Receive focused, practical, and direct business advice.\n- Access relevant snippets from transcripts of Alex's podcasts to support the chatbot's responses.\n- Utilize semantic search to find relevant content from the transcripts.\n\n## Getting Started\n\n### Prerequisites\n\n- Python 3.7 or higher\n- OpenAI API key\n- Pinecone API key and environment details\n\n### Installation\n\n1. Clone the repository:\n```\ngit clone https://github.com/your-repo-url/HormoziGPT.git\n```\n2. Change to the project directory:\n```\ncd HormoziGPT\n```\n3. Install the required dependencies:\n```\npip install -r requirements.txt\n```\n4. Set up the environment variables:\n- `OPENAI_API_KEY`: Your OpenAI API key\n- `PINECONE_API_KEY`: Your Pinecone API key\n- `PINECONE_ENVIRONMENT`: Your Pinecone environment details\n- `PINECONE_ENDPOINT`: Your Pinecone endpoint\n\n### Usage\n\n1. Run the Streamlit app:\n```\nstreamlit run app.py\n```\n2. Open the app in your web browser and enter your prompt to start the conversation with the chatbot.\n\n## Contributing\n\nContributions are welcome! Please read the [CONTRIBUTING.md](CONTRIBUTING.md) file for details on how to contribute to the project.\n\n## Acknowledgments\n\n- Alex Hormozi for his valuable insights and business advice! (don't sue me)\n- OpenAI for their language models and embeddings.\n- Pinecone for their semantic search capabilities.\n"
  },
  {
    "path": "app.py",
    "content": "import os\nimport openai\nimport streamlit as st\nfrom dotenv import load_dotenv\nfrom render import bot_msg_container_html_template, user_msg_container_html_template\nfrom utils import semantic_search\nimport prompts\nimport pinecone\n\n\n# Set up OpenAI API key\nopenai.api_key = st.secrets[\"OPENAI_API_KEY\"]\npinecone.init(api_key=st.secrets[\"PINECONE_API_KEY\"], environment=st.secrets[\"PINECONE_ENVIRONMENT\"])\nindex = pinecone.Index(st.secrets[\"PINECONE_INDEX_NAME\"])\n\nst.header(\"HormoziGPT - By Liam Ottley\")\n\n# Define chat history storage\nif \"history\" not in st.session_state:\n    st.session_state.history = []\n\n# Construct messages from chat history\ndef construct_messages(history):\n    messages = [{\"role\": \"system\", \"content\": prompts.system_message}]\n    \n    for entry in history:\n        role = \"user\" if entry[\"is_user\"] else \"assistant\"\n        messages.append({\"role\": role, \"content\": entry[\"message\"]})\n    \n    return messages\n\n# Generate response to user prompt\ndef generate_response():\n    st.session_state.history.append({\n        \"message\": st.session_state.prompt,\n        \"is_user\": True\n    })\n\n    print(f\"Query: {st.session_state.prompt}\")\n\n    # Perform semantic search and format results\n    search_results = semantic_search(st.session_state.prompt, index, top_k=3)\n\n    print(f\"Results: {search_results}\")\n\n    context = \"\"\n    for i, (title, transcript) in enumerate(search_results):\n        context += f\"Snippet from: {title}\\n {transcript}\\n\\n\"\n\n    # Generate human prompt template and convert to API message format\n    query_with_context = prompts.human_template.format(query=st.session_state.prompt, context=context)\n\n    # Convert chat history to a list of messages\n    messages = construct_messages(st.session_state.history)\n    messages.append({\"role\": \"user\", \"content\": query_with_context})\n\n    # Run the LLMChain\n    response = openai.ChatCompletion.create(model=\"gpt-3.5-turbo\", messages=messages)\n    print(messages)\n\n    # Parse response\n    bot_response = response[\"choices\"][0][\"message\"][\"content\"]\n    st.session_state.history.append({\n        \"message\": bot_response,\n        \"is_user\": False\n    })\n\n# User input prompt\nuser_prompt = st.text_input(\"Enter your prompt:\",\n                            key=\"prompt\",\n                            placeholder=\"e.g. 'Write me a business plan to scale my coaching business'\",\n                            on_change=generate_response\n                            )\n\n# Display chat history\nfor message in st.session_state.history:\n    if message[\"is_user\"]:\n        st.write(user_msg_container_html_template.replace(\"$MSG\", message[\"message\"]), unsafe_allow_html=True)\n    else:\n        st.write(bot_msg_container_html_template.replace(\"$MSG\", message[\"message\"]), unsafe_allow_html=True)\n"
  },
  {
    "path": "prompts.py",
    "content": "system_message = \"\"\"\n    You are Alex Hormozi, a successful entrepreneur and investor known for your no-nonsense approach to business advice. You have founded and scaled multiple companies, and you have a wealth of experience in customer acquisition, monetization, and scaling businesses.\n\n    Your goal is to provide valuable business advice and coaching to users. Your responses should be focused, practical, and direct, mirroring your own communication style. Avoid sugarcoating or beating around the bush—users expect you to be straightforward and honest.\n\n    You have access to transcripts of your own podcasts stored in a Pinecone database. These transcripts contain your actual words, ideas, and beliefs. When a user provides a query, you will be provided with snippets of transcripts that may be relevant to the query. You must use these snippets to provide context and support for your responses. Rely heavily on the content of the transcripts to ensure accuracy and authenticity in your answers.\n\n    Be aware that the transcripts may not always be relevant to the query. Analyze each of them carefully to determine if the content is relevant before using them to construct your answer. Do not make things up or provide information that is not supported by the transcripts.\n\n    In addition to offering business advice, you may also provide guidance on personal development and navigating the challenges of entrepreneurship. However, always maintain your signature no-bullshit approach.\n\n    Your goal is to provide advice that is as close as possible to what the real Alex Hormozi would say.\n\n    DO NOT make any reference to the snippets or the transcripts in your responses. You may use the snippets to provide context and support for your responses, but you should not mention them explicitly.\n\"\"\"\n\n\nhuman_template = \"\"\"\n    User Query: {query}\n\n    Relevant Transcript Snippets: {context}\n\"\"\""
  },
  {
    "path": "render.py",
    "content": "import streamlit as st\nimport re\n\n\nbot_msg_container_html_template = '''\n<div style='background-color: #FFFFFF; padding: 10px; border-radius: 5px; margin-bottom: 10px; display: flex'>\n    <div style=\"width: 20%; display: flex; justify-content: center\">\n        <img src=\"https://yt3.googleusercontent.com/ixwBtVrollE0Z5nA5YPHrnkKQoK09Evbe4gWCvJlleB2rFERDz3m2Jynhc3sGBE-EnzbH6ov=s176-c-k-c0x00ffffff-no-rj\" style=\"max-height: 50px; max-width: 50px; border-radius: 50%;\">\n    </div>\n    <div style=\"width: 80%;\">\n        $MSG\n    </div>\n</div>\n'''\n\nuser_msg_container_html_template = '''\n<div style='background-color: #FFFFFF; padding: 10px; border-radius: 5px; margin-bottom: 10px; display: flex'>\n    <div style=\"width: 78%\">\n        $MSG\n    </div>\n    <div style=\"width: 20%; margin-left: auto; display: flex; justify-content: center;\">\n        <img src=\"https://yt3.googleusercontent.com/w3Hwj4_weJ_tx9z79ffwCmaAU3eHPuJ5nvk_QDmNyxcbNdTaBBAIxenUXGybyUjLE4ktVKqyEA=s176-c-k-c0x00ffffff-no-rj\" style=\"max-width: 50px; max-height: 50px; float: right; border-radius: 50%;\">\n    </div>    \n</div>\n'''\n\ndef render_article_preview(docs, tickers):\n    message = f\"<h5>Here are relevant articles for {tickers} that may answer your question. &nbsp; &nbsp;</h5>\"\n    message += \"<div>\"\n    for d in docs:\n        elipse = \" \".join(d[2].split(\" \")[:140])        \n        message += f\"<br><a href='{d[1]}'>{d[0]}</a></br>\"\n        message += f\"<p>{elipse} ...</p>\"\n        message += \"<br>\"\n    message += \"</div>\"\n    return message\n\ndef render_earnings_summary(ticker, summary):\n    transcript_title = summary[\"transcript_title\"]\n    message = f\"<h5>Here is summary for {ticker} {transcript_title} </h5>\"\n    message += \"<div>\"\n    body =  re.sub(r'^-', r'*  ', summary[\"summary\"])\n    body =  re.sub(r'\\$', r'\\\\$', body)\n    message += f\"<p>{body}</p>\"\n    message += \"</div>\"\n    return message\n\ndef render_stock_question(answer, articles):\n    message = \"<div>\"\n    message += f\"{answer} &nbsp; <br>\"\n    message += \"Sources: \"\n    for a in articles:\n        message += f\"<a href='{a[1]}'>{a[0]}</a><br>\"\n    message += \"</div>\"\n    return message\n\ndef render_chat(**kwargs):\n    \"\"\"\n    Handles is_user \n    \"\"\"\n    if kwargs[\"is_user\"]:\n        st.write(\n            user_msg_container_html_template.replace(\"$MSG\", kwargs[\"message\"]),\n            unsafe_allow_html=True)\n    else:\n        st.write(\n            bot_msg_container_html_template.replace(\"$MSG\", kwargs[\"message\"]),\n            unsafe_allow_html=True)\n\n    if \"figs\" in kwargs:\n        for f in kwargs[\"figs\"]:\n            st.plotly_chart(f, use_container_width=True)\n\n"
  },
  {
    "path": "requirements.txt",
    "content": "streamlit==1.21.0\nopenai==0.27.4\nrequests==2.28.2\npython-dotenv==1.0.0\npinecone-client==2.2.1"
  },
  {
    "path": "utils.py",
    "content": "import os\nimport openai\nimport requests\nimport streamlit as st\nimport json\n\nopenai.api_key = st.secrets[\"OPENAI_API_KEY\"]\napi_key_pinecone = st.secrets[\"PINECONE_API_KEY\"]\npinecone_environment = st.secrets[\"PINECONE_ENVIRONMENT\"]\npinecone_endpoint = st.secrets[\"PINECONE_ENDPOINT\"]\n\ndef get_embeddings_openai(text):\n    try:\n        response = openai.Embedding.create(\n            input=text,\n            model=\"text-embedding-ada-002\"\n        )\n        response = response['data']\n        return [x[\"embedding\"] for x in response]\n    except Exception as e:\n        print(f\"Error in get_embeddings_openai: {e}\")\n        raise\n\ndef semantic_search(query, index, **kwargs):\n    try:\n        xq = get_embeddings_openai(query)\n\n        xr = index.query(vector=xq[0], top_k=kwargs.get('top_k', 1), include_metadata=kwargs.get('include_metadata', True))\n\n        if xr.error:\n            print(f\"Invalid response: {xr}\")\n            raise Exception(f\"Query failed: {xr.error}\")\n\n        titles = [r[\"metadata\"][\"title\"] for r in xr[\"matches\"]]\n        transcripts = [r[\"metadata\"][\"transcript\"] for r in xr[\"matches\"]]\n        return list(zip(titles, transcripts))\n\n    except Exception as e:\n        print(f\"Error in semantic_search: {e}\")\n        raise\n"
  }
]