diff --git a/README.md b/README.md
index aebf8d3..13d5bb3 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,12 @@ Fill in your own API to use.
See [installation.md](installation.md) for installation instructions.
+## Features
+
+- Chat with GPT and every model that supports OpenAI-style API.
+- Let the model search online for information with DuckDuckGo.
+- Generate images with FLUX.
+
## Implementation
Frontend: HTML, CSS, and Javascript to receive user messages and send them to the backend.
@@ -20,6 +26,8 @@ Backend: Python Flask to receive messages from the frontend and call the API.
Search: Free DuckDuckGo search API to search for information. You can set `DUCK_PROXY` environment variable to use a proxy for DuckDuckGo.
+Draw: Free CloudFlare Workers AI FLUX-schnell model to generate images. You can set `CLOUDFLARE_API_KEY` and `CLOUDFLARE_USER_ID` environment variables to use the CloudFlare Workers AI.
+
All chat data is stored in an SQL database, with UUIDs stored in cookies.
We will not use your data for any purpose other than to provide the service. The SQL database will never be viewed by anyone other than the program.
diff --git a/app.py b/app.py
index dcb1648..57015a1 100644
--- a/app.py
+++ b/app.py
@@ -8,6 +8,7 @@
from database import init_db, get_chat_log, save_chat_log, delete_chat_log, db, ChatLog
import logging
from logging_config import setup_logging
+import requests
app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1)
@@ -15,19 +16,16 @@
access_logger = setup_logging()
-pre_prompt = []
-pre_prompt_searchless=[]
+prompt_list = []
def load_prompt(file_path):
if file_path[0] == '#':
return
- global pre_prompt
+ global prompt_list
with open(file_path, 'r', encoding='utf-8') as file:
lines = file.readlines()
content = ''.join(line.strip() for line in lines)
- pre_prompt.append({"role": "system", "content": content})
- if file_path!="prompts/search.txt":
- pre_prompt_searchless.append({"role": "system", "content": content})
+ prompt_list.append({"file_path": file_path, "message": {"role": "system", "content": content}})
def load_all_prompts(directory):
print("Loading prompts...")
@@ -57,13 +55,12 @@ def __init__(self, api_key, base_url, model, messages, stream):
def reply(self):
processed_messages = []
for msg in self.messages:
+ processed_message = msg
+ if msg['content'].startswith('API Settings
+
+
+
+
diff --git a/prompts/draw.txt b/prompts/draw.txt
new file mode 100644
index 0000000..ef657d6
--- /dev/null
+++ b/prompts/draw.txt
@@ -0,0 +1,9 @@
+You have the capablility to draw. If the user requests you to draw
+an image, please reply "$#% ".
+In such a situation, do not include anything else in your response.
+You should only includ this single line.
+The description shall be around 100 words long and detailed.
+The description must be in English. Translate to English if needed.
+DO NOT ask for permission to generate the image, just do it!
+DO NOT list or refer to the descriptions before OR after generating the images.
+Do not create more than 1 image, even if the user requests more.
\ No newline at end of file
diff --git a/prompts/prompts.json b/prompts/prompts.json
index 0563a37..fb44592 100644
--- a/prompts/prompts.json
+++ b/prompts/prompts.json
@@ -11,6 +11,12 @@
"enabled": true,
"file": "search.txt",
"description": "Enable DuckDuckGo search for the bot."
+ },
+ {
+ "name": "Image Generation",
+ "enabled": true,
+ "file": "draw.txt",
+ "description": "Enable FLUX image generation."
}
]
}
\ No newline at end of file
diff --git a/static/locales.json b/static/locales.json
index a994b3f..d0801d0 100644
--- a/static/locales.json
+++ b/static/locales.json
@@ -1,5 +1,5 @@
{
- "version": "3.2.5.4",
+ "version": "3.3",
"translations": {
"en": {
"input": "Enter your message",
@@ -20,7 +20,8 @@
"model-name": "Model Name",
"stream": "Stream Output",
"search": "Web Search",
- "no-system": "Disable System Prompts"
+ "no-system": "Disable System Prompts",
+ "draw": "Draw"
},
"zh-cn": {
"input": "输入你的消息",
@@ -41,7 +42,8 @@
"model-name": "模型名称",
"stream": "流输出",
"search": "网络搜索",
- "no-system": "禁用系统提示词"
+ "no-system": "禁用系统提示词",
+ "draw": "作图"
},
"zh-hk": {
"input": "輸入你的消息",
@@ -62,7 +64,8 @@
"model-name": "模型名稱",
"stream": "流輸出",
"search": "網絡搜索",
- "no-system": "禁用系統提示詞"
+ "no-system": "禁用系統提示詞",
+ "draw": "作圖"
},
"fr": {
"input": "Entrez votre message",
@@ -83,7 +86,8 @@
"model-name": "Nom du modèle",
"stream": "Sortie de flux",
"search": "Recherche sur le Web",
- "no-system": "Désactiver les invites système"
+ "no-system": "Désactiver les invites système",
+ "draw": "Dessiner"
},
"de": {
"input": "Geben Sie Ihre Nachricht ein",
@@ -104,7 +108,8 @@
"model-name": "Modellname",
"stream": "Stream-Ausgabe",
"search": "Websuche",
- "no-system": "Deaktivieren von Systemaufforderungen"
+ "no-system": "Deaktivieren von Systemaufforderungen",
+ "draw": "Zeichnen"
},
"es": {
"input": "Escribe tu mensaje",
@@ -125,7 +130,8 @@
"model-name": "Nombre del modelo",
"stream": "Salida de flujo",
"search": "Búsqueda en la web",
- "no-system": "Desactivar las indicaciones del sistema"
+ "no-system": "Desactivar las indicaciones del sistema",
+ "draw": "Dibujar"
}
}
}
\ No newline at end of file
diff --git a/static/scripts/main.js b/static/scripts/main.js
index 8360cb5..6c783be 100644
--- a/static/scripts/main.js
+++ b/static/scripts/main.js
@@ -96,6 +96,11 @@ function simulateBotMessage(content, role=null) {
const avatarSrc = getModelAvatar(role);
botMessage.innerHTML = ``;
document.getElementById('chat-messages').appendChild(botMessage);
+ if (content.startsWith(" {
if (!response.ok) {
throw new Error('Network response was not ok');
@@ -81,15 +87,19 @@ function sendMessage(message = null, showUserBubble = true, hidden = false) {
document.getElementById('chat-messages').appendChild(botMessage);
}
contentBuffer += data.content;
- botMessage.setAttribute('data-content', contentBuffer);
- botMessage.querySelector('.bubble').innerHTML = formatMessage(contentBuffer);
+ filteredContentBuffer = contentBuffer;
+ if (contentBuffer.startsWith('@?!') || contentBuffer.startsWith('$#%')) {
+ filteredContentBuffer = "";
+ }
+ botMessage.setAttribute('data-content', filteredContentBuffer);
+ botMessage.querySelector('.bubble').innerHTML = formatMessage(filteredContentBuffer);
// Scroll to bottom
scrollToBottom();
if (data.end) {
// Render message content
- botMessage.querySelector('.bubble').innerHTML = formatMessage(contentBuffer);
+ botMessage.querySelector('.bubble').innerHTML = formatMessage(filteredContentBuffer);
// Render math formulas
MathJax.typesetPromise([botMessage.querySelector('.bubble')]).then(() => {
@@ -125,19 +135,70 @@ function sendMessage(message = null, showUserBubble = true, hidden = false) {
headers: {
'Content-Type': 'application/json'
},
- body: JSON.stringify({ message: message, prompt: prompt })
- }).then(response => {
- if (!response.ok) {
- throw new Error('Network response was not ok');
+ body: JSON.stringify({ prompt: prompt })
+ }).then(response => response.json()
+ ).then(data => {
+ if (data.status === 'error') {
+ failed_search = data.notice;
+ console.log(failed_search);
+ sendMessage(failed_search, false, true, prompt); // Send again as user, hidden=true
+ return;
}
- return response.json();
- }).then(data => {
const answer = data.answer;
- sendMessage(answer, false, true); // Send again as user, hidden=true
+ sendMessage(answer, false, true, prompt); // Send again as user, hidden=true
+ }).catch(handleFetchError);
+ } else if (contentBuffer.startsWith('$#%')) {
+ const prompt = contentBuffer;
+ console.log("Drawing...");
+ botMessage.querySelector('.bubble').innerHTML = "Drawing...";
+
+ fetch('/draw', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({ prompt: prompt })
+ }).then(response => response.json()
+ ).then(data => {
+ if (data.status === 'error') {
+ // Delete the last bot message
+ const chatMessages = document.getElementById('chat-messages');
+ const lastBotMessage = chatMessages.querySelector('.message.bot:last-child');
+ if (lastBotMessage) {
+ chatMessages.removeChild(lastBotMessage);
+ }
+
+ // Remove the last message from localStorage
+ const chatLog = JSON.parse(localStorage.getItem('chatMessages')) || [];
+ if (chatLog.length > 0) {
+ chatLog.pop();
+ localStorage.setItem('chatMessages', JSON.stringify(chatLog));
+ }
+ const failed_drawing = data.notice;
+ sendMessage(failed_drawing, false, true, prompt); // Send again as user, hidden=true
+ return;
+ }
+
+ const image = data.image;
+ botMessage.querySelector('.bubble').innerHTML
+ = ``;
+ scrollToBottom();
+ fetch(`/bot_message?user_id=${uuid}&model=${model}`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({ message: botMessage.querySelector('.bubble').innerHTML })
+ }).then(response => {
+ if (!response.ok) {
+ throw new Error('Network response was not ok');
+ }
+ return response.json();
+ }).then(data => {
+ }).catch(handleFetchError);
}).catch(handleFetchError);
} else {
// Send message to /bot_message route
- console.log(model)
fetch(`/bot_message?user_id=${uuid}&model=${model}`, {
method: 'POST',
headers: {
diff --git a/static/scripts/tools.js b/static/scripts/tools.js
index 5203b91..677c330 100644
--- a/static/scripts/tools.js
+++ b/static/scripts/tools.js
@@ -4,6 +4,9 @@ function setStream(value) {
function setSearch(value) {
localStorage.setItem('search', value.toString());
}
+function setDraw(value) {
+ localStorage.setItem('draw', value.toString());
+}
function setNoSystem(value,auto) {
localStorage.setItem('noSystem', value.toString());
@@ -20,6 +23,16 @@ function setNoSystem(value,auto) {
searchCheckbox.disabled = false;
searchCheckbox.parentElement.style.opacity = '1';
}
+
+ const drawCheckbox = document.getElementById('draw-enabled');
+
+ if (value) {
+ drawCheckbox.disabled = true;
+ drawCheckbox.parentElement.style.opacity = '0.5';
+ } else {
+ drawCheckbox.disabled = false;
+ drawCheckbox.parentElement.style.opacity = '1';
+ }
}
function setNoStream(value) {
@@ -48,6 +61,11 @@ document.addEventListener('DOMContentLoaded', () => {
searchCheck.checked = savedSearch === 'true';
setSearch(searchCheck.checked);
+ const drawCheck = document.getElementById('draw-enabled');
+ const savedDraw = localStorage.getItem('draw') || 'false';
+ drawCheck.checked = savedDraw === 'true';
+ setSearch(drawCheck.checked);
+
const noSystemCheck = document.getElementById('no-system');
const savedNoSystem = localStorage.getItem('noSystem') || 'false';
noSystemCheck.checked = savedNoSystem === 'true';