-
-
Save dshipper/5fcd0de33fea6254833a4e9406df7277 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import React, { Component, useState, useEffect } from 'react'; | |
| class ChatBot extends Component { | |
| constructor(props) { | |
| super(props); | |
| this.state = { | |
| messages: [], | |
| input: '', | |
| persona: 'Socrates', | |
| style: 'default', | |
| isiPhone : false, | |
| inputFocused: false, | |
| chatbotMessageStyles: {}, | |
| waitingForResponse: false | |
| }; | |
| this.initialInnerHeight = window.innerHeight; | |
| // Bind sendMessage function to component instance | |
| this.sendMessage = this.sendMessage.bind(this); | |
| } | |
| componentDidMount() { | |
| // Add initial message from bot to messages list | |
| this.setState({ | |
| messages: [...this.state.messages, { | |
| sender: "bot", | |
| message: "Welcome to Lenny Bot! I answer questions based on information contained in Lenny's Newsletter articles. Here are a few things you can ask me:" | |
| }, { | |
| sender: "bot", | |
| message: "-> What is good retention for a consumer social product?" | |
| },{ | |
| sender: "bot", | |
| message: "-> How do I know if I have product market fit?" | |
| },{ | |
| sender: "bot", | |
| message: "-> How did DoorDash get its first users?" | |
| }, | |
| { | |
| sender: "bot", | |
| type: "disclaimer" | |
| } | |
| ] | |
| }); | |
| this.detectIphone(); | |
| window.addEventListener('resize', this.updateChatbotMessageStyles); | |
| } | |
| componentWillUnmount() { | |
| window.removeEventListener('resize', this.updateChatbotMessageStyles); | |
| } | |
| detectIphone() { | |
| const userAgent = window.navigator.userAgent.toLowerCase(); | |
| if (userAgent.includes('iphone')) { | |
| this.setState({isIphone: true}); | |
| } | |
| } | |
| updateChatbotMessageStyles = () => { | |
| var self = this; | |
| setTimeout(function(){ | |
| var innerHeight = window.innerHeight; | |
| var keyboardHeight = self.initialInnerHeight - innerHeight; | |
| var keyboardHeightString = keyboardHeight + "px"; | |
| if (self.state.isIphone) { | |
| self.setState({ chatbotMessageStyles: { | |
| height: keyboardHeightString, | |
| paddingTop: keyboardHeightString | |
| }}); | |
| } | |
| }, 500) | |
| } | |
| inputFocused = () => { | |
| this.setState({inputFocused: true}); | |
| this.updateChatbotMessageStyles(); | |
| } | |
| inputBlurred = () => { | |
| this.setState({inputFocused: false, chatbotMessageStyles: {}}); | |
| } | |
| updateStyle = (style) => { | |
| this.setState({ | |
| style: style | |
| }); | |
| } | |
| updatePersona = (persona) => { | |
| this.setState({ | |
| persona: persona | |
| }); | |
| } | |
| handleChange = (e) => { | |
| let inputValue = e.target.value; | |
| let inputColor = 'white'; | |
| if (inputValue.startsWith('/')) { | |
| inputColor = 'rgb(255, 154, 236)'; | |
| } | |
| this.setState({input: inputValue, inputColor: inputColor}); | |
| } | |
| handleSubmit = (e) => { | |
| e.preventDefault(); | |
| // Add user message to messages list | |
| this.setState({ | |
| messages: [...this.state.messages, { | |
| sender: "user", | |
| message: this.state.input | |
| }], | |
| input: '' | |
| }, () => { | |
| // Send message to API | |
| this.sendMessage(); | |
| }); | |
| } | |
| handleKeyDown = (e) => { | |
| if (e.key === 'Enter' && !e.shiftKey) { | |
| e.preventDefault(); | |
| this.handleSubmit(e); | |
| } | |
| } | |
| sendMessage() { | |
| // Build transcript string | |
| let transcript = ''; | |
| this.state.messages.forEach((message) => { | |
| transcript += `${message.sender}: ${message.message}\n`; | |
| }); | |
| // Determine promptType based on last message | |
| let promptType = "completion"; | |
| if (this.state.messages[this.state.messages.length - 1].message === "/summarize") { | |
| promptType = "summary"; | |
| } | |
| var messages = this.state.messages; | |
| this.setState({input: '', waitingForResponse: true}) | |
| // Send transcript to API | |
| const response_promise = fetch('/ask-lenny', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ | |
| transcript: transcript, | |
| promptType: promptType, | |
| messages: messages, | |
| persona: this.state.persona, | |
| style: this.state.style | |
| }) | |
| }); | |
| response_promise | |
| .then(res => res.text()) | |
| .then(response => { | |
| this.setState({waitingForResponse: false}) | |
| try { | |
| response = JSON.parse(response); | |
| var message = response.answer; | |
| var topSource = response.sources[0]; | |
| var newMessages = [...this.state.messages]; | |
| newMessages.push({ | |
| sender: "bot", | |
| message: message | |
| }); | |
| if(topSource) { | |
| var url = topSource.url; | |
| newMessages.push({ | |
| sender: "bot", | |
| type: "source", | |
| title: topSource.title, | |
| url: url, | |
| message: "For more information see: " + topSource.title + " (" + url + ")" | |
| }); | |
| } | |
| this.setState({ | |
| messages: newMessages | |
| },() => { | |
| // Scroll to bottom of chatbot-messages div | |
| this.messagesEnd.scrollIntoView({ behavior: 'smooth' }); | |
| }); | |
| } catch (e) { | |
| console.log(e); | |
| var newMessages = [...this.state.messages]; | |
| newMessages.push({ | |
| sender: "bot", | |
| message: "Sorry there was some kind of error getting a response from the server. It's possible OpenAI is overloaded. Please try again." | |
| }); | |
| this.setState({ | |
| messages: newMessages | |
| },() => { | |
| // Scroll to bottom of chatbot-messages div | |
| this.messagesEnd.scrollIntoView({ behavior: 'smooth' }); | |
| }); | |
| } | |
| }); | |
| } | |
| render() { | |
| var loadingIndicator = (this.state.waitingForResponse) ? <div class="lds-ripple"><div></div><div></div><div></div></div> : null; | |
| return ( | |
| <div className="chatbot-container"> | |
| <div className="chatbot-messages" style={this.state.chatbotMessageStyles}> | |
| {this.state.messages.map((message, index) => ( | |
| <div key={index} className={`chatbot-message ${message.sender}`} | |
| ref={(el) => { | |
| if (index === this.state.messages.length - 1 && el) { | |
| this.messagesEnd = el; | |
| this.messagesEnd.scrollIntoView({ behavior: 'smooth' }); | |
| } | |
| }}> | |
| {message.type && message.type === "source" ? ( | |
| <div> | |
| {"For more information see: "} | |
| <a href={message.url} target="_blank" rel="noopener noreferrer"> | |
| {message.title} | |
| </a> | |
| </div> | |
| ) : ( | |
| message.message | |
| )} | |
| </div> | |
| ))} | |
| <div className="chatbot-loading-indicator"> | |
| {loadingIndicator} | |
| </div> | |
| </div> | |
| <form onSubmit={this.handleSubmit}> | |
| <textarea | |
| value={this.state.input} | |
| onChange={this.handleChange} | |
| onKeyDown={this.handleKeyDown} | |
| onFocus={this.inputFocused} | |
| onBlur={this.inputBlurred} | |
| defaultValue="Type your message here" | |
| /> | |
| <button type="submit">Send</button> | |
| </form> | |
| </div> | |
| ); | |
| } | |
| } | |
| export default ChatBot; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment