Code viewer for World: Would AI buy Apple Stocks?
// Apply CSS to the body using jQuer
$('body').css({
    "margin": "20px",
    "padding": "20px"
});

// Define API keys and their default values
let STOCK_API_KEY = '';
const default_STOCK_API_KEY = 'ct77a19r01qr3sdtsv10ct77a19r01qr3sdtsv1g';

let gemma_API_KEY = '';
const default_gemma_API_KEY = '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';

let llama_API_KEY = '';
const default_API_KEY = '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';

let promptt = '';

// Function to fetch stock data from FinnHub
async function fetchData() {
    const container = document.getElementById("data-container");
    // change state to loading once this function is called
    container.textContent = "Loading...";
    try {
        const response = await fetch(`https://finnhub.io/api/v1/quote?symbol=AAPL&token=${STOCK_API_KEY}`);
        if (!response.ok) {
            throw new Error(`API Error: ${response.statusText}`);
        }
        const data = await response.json();
        // if a response is received remove loading
        container.textContent = "";
        // console.log(data);
        // set prompt for AI models when data is received
        promptt = `Here is the latest data on Apple Stocks ${Object.entries(data)}. This data is stored in a hashmap of key-value pairs separated by ','. C stands for closing price, H for high, L for low, O for opening price, pc for previous close, and t for timestamp in Unix time. 
        Based on this data and the general market sentiment of Apple, would you recommend to buy shares, sell current shares, or hold current shares? 
        Please provide answers based on both short-term and long-term gains.`;
        // console.log(promptt);

        // create and append data retreived from API
        const h3 = document.createElement('h3');
        h3.textContent = 'Apple Stock Data';
        container.appendChild(h3);

        // iterate over the data and display each key-value pair
        for (const [key, value] of Object.entries(data)) {
            const div = document.createElement('div');
            div.textContent = `${key}: ${value}`;
            container.appendChild(div);
        }
    } catch (error) {
        console.error('Error fetching data:', error);
        container.textContent = "An error occurred. Please try again.";
    }
}

// Function to fetch Gemma response
async function fetchResponse1() {
    console.log('Fetching Gemma response...');
    // console.log(promptt);
    // show loading once this function is called
    document.getElementById("response1").textContent = "Loading...";
    if (!promptt) {
        document.getElementById("response1").textContent = "Error. Please ensure all API keys are set correctly.";
        return;
    }

    // create payload of data to be POSTed
    const payload = {
        messages: [
            {
                role: "user",
                content: promptt, // Use the provided prompt
            },
        ],
        model: "gemma2-9b-it",      // "gemma-7b-it", // Specify the model      // MH edit 
        temperature: 1,
        max_tokens: 1024,
        top_p: 1,
        stream: false, // Set to false for a single response
        stop: null,
    };

    
    // post the data
    try {
        const response = await fetch("https://api.groq.com/openai/v1/chat/completions", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                Authorization: `Bearer ${gemma_API_KEY}`, // Use your Gemma API key
            },
            body: JSON.stringify(payload),
        });

        if (!response.ok) {
            throw new Error(`API Error: ${response.statusText}`);
        }

        const data = await response.json();
        // ternary operator to check if data exists
        const content = parseMarkdown(data.choices?.[0]?.message?.content || '');
        console.log(content);
        document.getElementById("response1h").textContent = "Gemma's Opinion";
        document.getElementById("response1").innerHTML = content;
    } catch (error) {
        console.error("Error fetching Llama response:", error);
        document.getElementById("response1h").textContent = "";
        document.getElementById("response1").textContent = `An error occurred: ${error.message}`;
    }
}


// Function to fetch Llama response
async function fetchResponse2() {
    console.log('Fetching Llama response...');
    //console.log(promptt);
    // show loading once this function is called
    document.getElementById("response2").textContent = "Loading...";
    if (!promptt) {
        document.getElementById("response2").textContent = "Error. Please ensure all API keys are set correctly.";
        return;
    }

    const payload = {
        messages: [
            {
                role: "user",
                content: promptt, // Use the provided prompt
            },
        ],
        model: "llama3-8b-8192", // Specify the model
        temperature: 1,
        max_tokens: 1024,
        top_p: 1,
        stream: false, // Set to false for a single response
        stop: null,
    };

    
    // post the data
    try {
        const response = await fetch("https://api.groq.com/openai/v1/chat/completions", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                Authorization: `Bearer ${llama_API_KEY}`, // Use your Llama API key
            },
            body: JSON.stringify(payload),
        });

        if (!response.ok) {
            throw new Error(`API Error: ${response.statusText}`);
        }

        const data = await response.json();
        //console.log("Response Data:", data);
        // ternary operator to check if data exists
        const content = parseMarkdown(data.choices?.[0]?.message?.content || '');
        console.log(content);
        document.getElementById("response2h").textContent = "Llama's Opinion";
        document.getElementById("response2").innerHTML = content;
    } catch (error) {
        console.error("Error fetching Llama response:", error);
        document.getElementById("response2h").textContent = "";
        document.getElementById("response2").textContent = `An error occurred: ${error.message}`;
    }
}


// Function to set FinnHub API key
function setkey_fin() {   
    const container = document.getElementById('data-container');
    container.innerHTML = "";
    console.log('Setting FinnHub API key...');
    const inputVal = $("#apikey_fin").val();
    // ternary operator to set api key value if one has been provided, else use default
    STOCK_API_KEY = inputVal ? inputVal.trim() : default_STOCK_API_KEY;
    console.log(`FinnHub API Key: ${STOCK_API_KEY}`);
    $("#set_fin").fadeIn(1000).html("<b>API key has been set.</b>").delay(500).fadeOut(300);
    // fetch stock data from api once key has been set
    fetchData();
}

// Function to set Gemma API key
function setkey_gemma() {   
    console.log('Setting Gemma API key...');
    const inputVal = $("#apikey_gemma").val();
    
    // MH edit 
    gemma_API_KEY = inputVal ? inputVal.trim() : default_gemma_API_KEY;
    
    console.log(`Gemma API Key: ${gemma_API_KEY}`);
    $("#set_gemma").fadeIn(1000).html("<b>API key has been set.</b>").delay(500).fadeOut(300);
}

// Function to set Llama API key
function setkey_llama() {   
    console.log('Setting Llama API key...');
    const inputVal = $("#apikey_llama").val();
    llama_API_KEY = inputVal ? inputVal.trim() : default_API_KEY;
    console.log(`Llama API Key: ${llama_API_KEY}`);
    $("#set_llama").fadeIn(1000).html("<b>API key has been set.</b>").delay(500).fadeOut(300);
}


// Dynamically write HTML content to the page
document.write(`
<style> 
  .section {
      margin-top: 100px;
  }
  #top {
      margin-top: 50px;
  }
  #disclaimer {
      color: #DCDCDC;
      font-size: 20px;
  }
  .enterkey h4 {
      margin: 0;
  }
  .ab-normbutton {
      margin-left: 10px;
      padding: 5px 10px;
      cursor: pointer;
  }
</style>

<p id="disclaimer">(This is for informational purposes only and should not be taken as financial advice!)</p>
 <h5>References:</h5>
 <p> Financial Markets API: <a href = "https://finnhub.io/"> Finnhub Stock API</a> </p>
 <p>Pop-up Modal: <a href = "https://www.youtube.com/watch?v=TAB_v6yBXIE">Kevin Powell YouTube Video </a> </p>
 <p>Where we got our AI models, <a href = "https://ai.google.dev/gemma">Gemma</a> and <a href = "https://www.llama.com/"> LLama </a>:<a href ="https://console.groq.com/"> Groq Cloud</p></a>
 <p>Parser MD to HTML code by Chalarangelo: <a href= "https://github.com/Chalarangelo/parse-md-js/blob/master/parsemd.js"> GitHub parse-md.js</a></p>

<h1>Would AI Invest in Apple Stocks?</h1>

<div id="top" class="section">
    <h3>Enter FinnHub API Key</h3>
    <div class="enterkey">
        <h4>Enter API key:</h4> 
        <input style="width:25vw;" maxlength="2000" id="apikey_fin" value="" placeholder="(Or leave empty to use default)" />  
        <button onclick="setkey_fin();" class="ab-normbutton">Set API Key</button>
        <p id="set_fin"></p>
    </div>
</div>

<div id="data-container"></div>

<div class="section">
    <h3>Ask What Gemma Thinks. Should You <b>Buy</b> or <b>Sell</b>?</h3>
    <h4>Enter Gemma API Key</h4>
    <div class="enterkey">
        <h4>Enter API key:</h4> 
        <input style="width:25vw;" maxlength="2000" id="apikey_gemma" value="" placeholder="(Or leave empty to use default)" />  
        <button onclick="setkey_gemma();" class="ab-normbutton">Set API Key</button>
        <p id="set_gemma"></p>
    </div>
    <div id="response1_container">
            <h4 style="display: inline;">Run Prompt</h4>
            <button onclick="fetchResponse1();" class="ab-normbutton">Run</button>
            <button class="ab-normbutton open-button">View Prompt</button>
        <dialog class ="modal1"> 
            <h2>Prompt</h2>
            <p id ="prompt1"></p>
            <button class = "ab-normbutton close-button">Close</button>       
        </dialog>
        <h4 id="response1h"></h4>
        <p id="response1"></p>
    </div>
</div>

<div class="section">
    <h3>Ask What Llama Thinks. Should You <b>Buy</b> or <b>Sell</b>?</h3>
    <h4>Enter Llama API Key</h4>
    <div class="enterkey">
        <h4>Enter API key:</h4> 
        <input style="width:25vw;" maxlength="2000" id="apikey_llama" value="" placeholder="(Or leave empty to use default)" />  
        <button onclick="setkey_llama();" class="ab-normbutton">Set API Key</button>
        <p id="set_llama"></p>
    </div>
    <div id="response2_container">
            <h4 style="display: inline;">Run Prompt</h4>
            <button onclick="fetchResponse2();" class="ab-normbutton">Run</button>
            <button class="ab-normbutton open-button2">View Prompt</button>
            <dialog class ="modal2"> 
                <h2>Prompt</h2>
                <p id ="prompt2"></p>
                <button class = "ab-normbutton close-button2">Close</button>       
        </dialog>
        <h4 id="response2h"></h4>
        <p id="response2"></p>
    </div>
</div>
<br />
`);

// functions and variables below to show the prompt that we are 
const modal1 = document.querySelector(".modal1");
const openModal1 = document.querySelector(".open-button");
const closeModal1 = document.querySelector(".close-button");

openModal1.addEventListener('click', () => {
    document.getElementById('prompt1').textContent = promptt
    //console.log(promptt);
    modal1.showModal();
})

closeModal1.addEventListener('click', () => {
    modal1.close();
})

const modal2 = document.querySelector(".modal2");
const openModal2 = document.querySelector(".open-button2");
const closeModal2 = document.querySelector(".close-button2");

openModal2.addEventListener('click', () => {
    document.getElementById('prompt2').textContent = promptt
    //console.log(promptt);
    modal2.showModal();
})

closeModal2.addEventListener('click', () => {
    modal2.close();
})

// md parser

/***   Regex Markdown Parser by chalarangelo   ***/
// Replaces 'regex' with 'replacement' in 'str'
// Curry function, usage: replaceRegex(regexVar, replacementVar) (strVar)
const replaceRegex = function(regex, replacement){
	return function(str){
		return str.replace(regex, replacement);
	}
}
// Regular expressions for Markdown (a bit strict, but they work)
const codeBlockRegex = /((\n\t)(.*))+/g;
const inlineCodeRegex = /(`)(.*?)\1/g;
const imageRegex = /!\[([^\[]+)\]\(([^\)]+)\)/g;
const linkRegex = /\[([^\[]+)\]\(([^\)]+)\)/g;
const headingRegex = /\n(#+\s*)(.*)/g;
const boldItalicsRegex = /(\*{1,2})(.*?)\1/g;
const strikethroughRegex = /(\~\~)(.*?)\1/g;
const blockquoteRegex = /\n(&gt;|\>)(.*)/g;
const horizontalRuleRegex = /\n((\-{3,})|(={3,}))/g;
const unorderedListRegex = /(\n\s*(\-|\+)\s.*)+/g;
const orderedListRegex = /(\n\s*([0-9]+\.)\s.*)+/g;
const paragraphRegex = /\n+(?!<pre>)(?!<h)(?!<ul>)(?!<blockquote)(?!<hr)(?!\t)([^\n]+)\n/g;
// Replacer functions for Markdown
const codeBlockReplacer = function(fullMatch){
	return '\n<pre>' + fullMatch + '</pre>';
}
const inlineCodeReplacer = function(fullMatch, tagStart, tagContents){
	return '<code>' + tagContents + '</code>';
}
const imageReplacer = function(fullMatch, tagTitle, tagURL){
	return '<img src="' + tagURL + '" alt="' + tagTitle + '" />';
}
const linkReplacer = function(fullMatch, tagTitle, tagURL){
	return '<a href="' + tagURL + '">' + tagTitle + '</a>';
}
const headingReplacer = function(fullMatch, tagStart, tagContents){
	return '\n<h' + tagStart.trim().length + '>' + tagContents + '</h' + tagStart.trim().length + '>';
}
const boldItalicsReplacer = function(fullMatch, tagStart, tagContents){
	return '<' + ( (tagStart.trim().length==1)?('em'):('strong') ) + '>'+ tagContents + '</' + ( (tagStart.trim().length==1)?('em'):('strong') ) + '>';
}
const strikethroughReplacer = function(fullMatch, tagStart, tagContents){
	return '<del>' + tagContents + '</del>';
}
const blockquoteReplacer = function(fullMatch, tagStart, tagContents){
	return '\n<blockquote>' + tagContents + '</blockquote>';
}
const horizontalRuleReplacer = function(fullMatch){
	return '\n<hr />';
}
const unorderedListReplacer = function(fullMatch){
	let items = '';
	fullMatch.trim().split('\n').forEach( item => { items += '<li>' + item.substring(2) + '</li>'; } );
	return '\n<ul>' + items + '</ul>';
}
const orderedListReplacer = function(fullMatch){
	let items = '';
	fullMatch.trim().split('\n').forEach( item => { items += '<li>' + item.substring(item.indexOf('.')+2) + '</li>'; } );
	return '\n<ol>' + items + '</ol>';
}
const paragraphReplacer = function(fullMatch, tagContents){
	return '<p>' + tagContents + '</p>';
}
// Rules for Markdown parsing (use in order of appearance for best results)
const replaceCodeBlocks = replaceRegex(codeBlockRegex, codeBlockReplacer);
const replaceInlineCodes = replaceRegex(inlineCodeRegex, inlineCodeReplacer);
const replaceImages = replaceRegex(imageRegex, imageReplacer);
const replaceLinks = replaceRegex(linkRegex, linkReplacer);
const replaceHeadings = replaceRegex(headingRegex, headingReplacer);
const replaceBoldItalics = replaceRegex(boldItalicsRegex, boldItalicsReplacer);
const replaceceStrikethrough = replaceRegex(strikethroughRegex, strikethroughReplacer);
const replaceBlockquotes = replaceRegex(blockquoteRegex, blockquoteReplacer);
const replaceHorizontalRules = replaceRegex(horizontalRuleRegex, horizontalRuleReplacer);
const replaceUnorderedLists = replaceRegex(unorderedListRegex, unorderedListReplacer);
const replaceOrderedLists = replaceRegex(orderedListRegex, orderedListReplacer);
const replaceParagraphs = replaceRegex(paragraphRegex, paragraphReplacer);
// Fix for tab-indexed code blocks
const codeBlockFixRegex = /\n(<pre>)((\n|.)*)(<\/pre>)/g;
const codeBlockFixer = function(fullMatch, tagStart, tagContents, lastMatch, tagEnd){
	let lines = '';
	tagContents.split('\n').forEach( line => { lines += line.substring(1) + '\n'; } );
	return tagStart + lines + tagEnd;
}
const fixCodeBlocks = replaceRegex(codeBlockFixRegex, codeBlockFixer);
// Replacement rule order function for Markdown
// Do not use as-is, prefer parseMarkdown as seen below
const replaceMarkdown = function(str) {
  return replaceParagraphs(replaceOrderedLists(replaceUnorderedLists(
		replaceHorizontalRules(replaceBlockquotes(replaceceStrikethrough(
			replaceBoldItalics(replaceHeadings(replaceLinks(replaceImages(
				replaceInlineCodes(replaceCodeBlocks(str))
      ))))
    )))
	)));
}
// Parser for Markdown (fixes code, adds empty lines around for parsing)
// Usage: parseMarkdown(strVar)
const parseMarkdown = function(str) {
	return fixCodeBlocks(replaceMarkdown('\n' + str + '\n')).trim();
}