Last active
April 3, 2025 21:30
-
-
Save angusdev/1324d10da61abb7ebe020d76eb95e55d to your computer and use it in GitHub Desktop.
Postgresql generate insert statement
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 uuid | |
from abc import ABC, abstractmethod | |
from typing import Any, Optional | |
import dash | |
import flask | |
from dash import Input, Output, State, dcc, html | |
from dash.development.base_component import Component | |
class Chatbot(ABC): | |
"""Chatbot interface.""" | |
@abstractmethod | |
def get_response(self, user_message: str, session_id: str) -> str: | |
""" | |
Generate a markdown formatted response to the user's message. | |
Args: | |
user_message (str): The user's message. | |
session_id (str): The unique session ID for the conversation. | |
Returns: | |
str: The chatbot's response in markdown format. | |
""" | |
pass | |
class DashChatbot: | |
"""A Dash-based user interface for the chatbot.""" | |
def __init__( | |
self, | |
chatbot: Chatbot, | |
title: Optional[str] = None, | |
logo_url: Optional[str] = None, | |
greeting_message: Optional[str] = None, | |
first_question: Optional[str] = None, | |
input_placeholder: Optional[str] = "Type your message...", | |
**kwargs, | |
) -> None: | |
"""Initialize the DashChatbot with optional configurations.""" | |
self.app = dash.Dash(__name__, server=flask.Flask(__name__), **kwargs) | |
self.app.title = "Chatbot" | |
self._chatbot = chatbot | |
self._title = title | |
self._logo_url = logo_url | |
self._greeting_message = greeting_message | |
self._first_question = first_question | |
self._input_placeholder = input_placeholder | |
# Initialize layout and callbacks | |
self._initialize_layout() | |
self._initialize_callbacks() | |
if not self.app.server.secret_key: | |
self.app.server.secret_key = str(uuid.uuid4()) | |
def _initialize_layout(self) -> None: | |
"""Initializes the Dash application layout.""" | |
title_section = [] | |
if self._logo_url or self._title: | |
title_section.append( | |
html.Div( | |
className="title-section", | |
children=[ | |
html.Img(src=self._logo_url, className="chat-logo") if self._logo_url else None, | |
html.H1(self._title, className="chat-title") if self._title else None, | |
], | |
) | |
) | |
initial_messages = [] | |
if self._greeting_message: | |
initial_messages.append( | |
html.Div([dcc.Markdown(self._greeting_message, className="bot-message")]) | |
) | |
self.app.layout = html.Div( | |
className="chat-wrapper", | |
children=[ | |
*title_section, | |
html.Div( | |
id="chat-container", | |
className="chat-container", | |
children=initial_messages, | |
), | |
html.Div( | |
className="input-container", | |
children=[ | |
html.Div( | |
className="input-row", | |
children=[ | |
dcc.Textarea( | |
id="user-input", | |
placeholder=self._input_placeholder, | |
className="chat-input", | |
value=self._first_question, | |
style={"resize": "none"}, | |
), | |
], | |
), | |
html.Div( | |
className="button-row", | |
children=[ | |
html.Div(className="controls-placeholder"), # Placeholder div | |
html.Button("Send", id="send-button", className="chat-button"), | |
], | |
), | |
], | |
), | |
], | |
) | |
def _initialize_callbacks(self) -> None: | |
"""Sets up the Dash application callbacks.""" | |
@self.app.callback( | |
[Output("chat-container", "children"), Output("user-input", "value")], | |
Input("send-button", "n_clicks"), | |
[State("user-input", "value"), State("chat-container", "children")], | |
) | |
def update_chat( | |
n_clicks: Optional[int], | |
user_message: Optional[str], | |
chat_history: Optional[list[Component]], | |
) -> list[Component]: | |
"""Updates the chat container with new messages and clears the input field.""" | |
if n_clicks is None or not user_message: | |
return chat_history, "" | |
chat_history = chat_history or [] | |
# Generate or retrieve the session ID | |
if "session_id" not in flask.session: | |
flask.session["session_id"] = str(uuid.uuid4()) | |
session_id = flask.session["session_id"] | |
# Pass the session ID to the chatbot | |
bot_response = self._chatbot.get_response(user_message, session_id=session_id) | |
chat_history.append( | |
html.Div( | |
[dcc.Markdown(bot_response, className="bot-message markdown-body")] | |
) | |
) | |
return chat_history, "" | |
def run(self, **kwargs: dict[str, Any]) -> None: | |
"""Runs the Dash application server.""" | |
self.app.run(**kwargs) |
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 time | |
from dash_chatbot import DashChatbot, Chatbot | |
class SampleChatbot(Chatbot): | |
"""A simple chatbot that returns markdown formatted responses.""" | |
def get_response(self, user_message: str) -> str: | |
""" | |
Generate a markdown formatted response to the user's message. | |
Simulates processing time with a delay. | |
""" | |
time.sleep(1) | |
return f""" | |
### Response | |
I received your message: *'{user_message}'* | |
Here's what I can do: | |
- Parse **markdown** | |
- Format *text* | |
- Create lists | |
""" | |
if __name__ == "__main__": | |
chatbot = SampleChatbot() | |
dash_chatbot = DashChatbot( | |
chatbot, | |
title="Welcome to Chatbot", | |
logo_url="https://images.ctfassets.net/kftzwdyauwt9/2i61iTTUDpWjwTbl6cdJkL/a60bb9ad83127262f5022aabcede01a6/DON-T_add_any_colors_to_the_Blossom.png?w=3840&q=90&fm=webp", | |
greeting_message="Hello! How can I assist you today?", | |
first_question="What is your name?", | |
input_placeholder="Ask me anything...", | |
) | |
dash_chatbot.run() |
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
/** | |
* Initialize chat functionality. | |
* Sets up event listeners, dynamic input resizing, and response handling. | |
*/ | |
function initializeChat() { | |
const elements = { | |
sendButton: document.getElementById('send-button'), | |
userInput: document.getElementById('user-input'), | |
chatContainer: document.getElementById('chat-container') | |
}; | |
if (!elements.sendButton || !elements.userInput || !elements.chatContainer) { | |
setTimeout(initializeChat, 100); | |
return; | |
} | |
let isWaitingResponse = false; | |
/** | |
* Enable or disable input controls. | |
* @param {boolean} enabled - Whether to enable the controls. | |
*/ | |
function setInputsEnabled(enabled) { | |
elements.userInput.disabled = !enabled; | |
elements.sendButton.disabled = !enabled; | |
if (enabled) { | |
elements.userInput.focus(); | |
} | |
} | |
/** | |
* Add a user message to the chat container. | |
* @param {string} message - The user's message to display. | |
*/ | |
function addUserMessage(message) { | |
const div = document.createElement('div'); | |
div.textContent = message; | |
div.className = 'user-message'; | |
elements.chatContainer.appendChild(div); | |
} | |
/** | |
* Add a "Thinking..." indicator to the chat container. | |
*/ | |
function addThinkingMessage() { | |
const div = document.createElement('div'); | |
div.innerHTML = 'Thinking...'; | |
div.className = 'thinking-message'; | |
div.id = 'thinking-message'; | |
elements.chatContainer.appendChild(div); | |
div.scrollIntoView({ behavior: 'smooth', block: 'start' }); | |
} | |
/** | |
* Dynamically adjust the height of the input field based on its content. | |
* @param {HTMLTextAreaElement} textarea - The textarea element to adjust. | |
*/ | |
function adjustTextareaHeight(textarea) { | |
textarea.style.height = '0'; // Reset height to calculate the new height | |
if (textarea.value) { | |
textarea.style.height = `${textarea.scrollHeight}px`; // Set height based on scrollHeight | |
} | |
} | |
/** | |
* Handle the submission of a user message. | |
* @param {Event} e - The event object triggered by the user action. | |
*/ | |
function handleMessage(e) { | |
if (isWaitingResponse) return; | |
const message = elements.userInput.value.trim(); | |
if (message) { | |
isWaitingResponse = true; | |
setInputsEnabled(false); | |
addUserMessage(message); | |
if (e.isTrusted) { | |
addThinkingMessage(); | |
const clickEvent = new MouseEvent('click', { | |
bubbles: true, | |
cancelable: true, | |
view: window | |
}); | |
elements.sendButton.dispatchEvent(clickEvent); | |
} | |
} | |
} | |
/** | |
* Observe changes in the chat container to handle bot responses. | |
*/ | |
const observer = new MutationObserver((mutations) => { | |
const thinkingMsg = document.getElementById('thinking-message'); | |
if (thinkingMsg && thinkingMsg.nextElementSibling) { | |
const newMessage = thinkingMsg.nextElementSibling; | |
thinkingMsg.remove(); | |
newMessage.scrollIntoView({ behavior: 'smooth', block: 'start' }); | |
isWaitingResponse = false; | |
adjustTextareaHeight(elements.userInput); | |
setInputsEnabled(true); | |
} | |
}); | |
observer.observe(elements.chatContainer, { childList: true }); | |
// Event listeners | |
elements.sendButton.addEventListener('click', handleMessage); | |
elements.userInput.addEventListener('keypress', (e) => { | |
if (e.key === 'Enter' && !e.shiftKey) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
handleMessage(e); | |
} | |
}); | |
elements.userInput.addEventListener('input', () => { | |
adjustTextareaHeight(elements.userInput); | |
}); | |
// Initialize the height of the input field | |
adjustTextareaHeight(elements.userInput); | |
} | |
window.addEventListener('load', initializeChat); |
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
/* Root Variables */ | |
:root { | |
--system-fonts: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, | |
Helvetica, Arial, sans-serif, "Apple Color Emoji", | |
"Segoe UI Emoji", "Segoe UI Symbol"; | |
--primary-color: #007bff; | |
--disabled-color: #6c757d; | |
--bg-light: #f9f9f9; | |
--border-color: #ccc; | |
--user-msg-bg: #eee; | |
--thinking-msg-bg: #e9ecef; | |
--disabled-bg: #e9ecef; | |
--container-width: 800px; | |
--border-radius: 10px; | |
--spacing-normal: 10px; | |
--spacing-large: 20px; | |
} | |
/* Import GitHub Markdown CSS */ | |
@import url('https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.1.0/github-markdown.min.css'); | |
/* Global Styles */ | |
body { | |
margin: 0; | |
padding: 0; | |
height: 100vh; | |
font-family: var(--system-fonts); | |
} | |
/* Layout */ | |
.chat-wrapper { | |
height: 100vh; | |
display: flex; | |
flex-direction: column; | |
max-width: var(--container-width); | |
margin: 0 auto; | |
position: relative; | |
} | |
.chat-container { | |
padding: var(--spacing-large); | |
padding-bottom: 140px; | |
flex: 1; | |
display: flex; | |
flex-direction: column; | |
} | |
.input-container { | |
display: flex; | |
flex-direction: column; | |
gap: var(--spacing-normal); | |
position: fixed; | |
bottom: var(--spacing-large); | |
left: 50%; | |
transform: translateX(-50%); | |
width: 100%; | |
max-width: var(--container-width); | |
background: white; | |
padding: var(--spacing-normal); | |
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1); | |
border-radius: 15px; | |
z-index: 1000; | |
} | |
/* Components */ | |
.title-section { | |
display: flex; | |
align-items: center; | |
margin: var(--spacing-large) 0; | |
} | |
.chat-logo { | |
height: 40px; | |
margin-right: var(--spacing-normal); | |
} | |
.chat-title { | |
font-size: 24px; | |
margin: 0; | |
} | |
.user-message, | |
.bot-message, | |
.thinking-message { | |
margin-bottom: var(--spacing-normal); | |
padding: var(--spacing-normal); | |
border-radius: var(--border-radius); | |
max-width: 70%; | |
} | |
.user-message { | |
background-color: var(--user-msg-bg); | |
align-self: flex-end; | |
width: fit-content; | |
} | |
.bot-message { | |
margin-right: auto; | |
padding: var(--spacing-normal); | |
white-space: pre-wrap; /* Preserve formatting for markdown */ | |
} | |
.thinking-message { | |
background-color: var(--thinking-msg-bg); | |
margin-right: auto; | |
font-style: italic; | |
color: var(--disabled-color); | |
} | |
.input-row { | |
display: flex; | |
flex: 1; | |
} | |
.chat-input { | |
flex: 1; | |
padding: var(--spacing-normal); | |
min-height: 20px; | |
max-height: 150px; | |
overflow-y: auto; | |
line-height: 1.5; | |
} | |
.button-row { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
} | |
.controls-placeholder { | |
flex: 1; /* Fills the remaining space */ | |
} | |
.chat-button { | |
padding: var(--spacing-normal) var(--spacing-large); | |
font-size: 16px; | |
background-color: var(--primary-color); | |
color: white; | |
border: none; | |
border-radius: 5px; | |
cursor: pointer; | |
margin-left: auto; /* Aligns the button to the right */ | |
} | |
.chat-button.small { | |
padding: var(--spacing-normal); | |
font-size: 14px; | |
margin-right: var(--spacing-normal); | |
} | |
/* States */ | |
.chat-input:disabled { | |
background-color: var(--disabled-bg); | |
cursor: not-allowed; | |
} | |
.chat-button:disabled { | |
background-color: var(--disabled-color); | |
cursor: not-allowed; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment