Skip to content

Commit

Permalink
Version 3.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Davidasx committed Dec 24, 2024
1 parent 4e47dfe commit c42fa0e
Show file tree
Hide file tree
Showing 10 changed files with 222 additions and 42 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down
89 changes: 70 additions & 19 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,24 @@
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)
init_db(app)

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...")
Expand Down Expand Up @@ -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('<img src="data:image/png;base64,'):
processed_message['content'] = 'Image generated successfully for you.'
if msg['role'].startswith('assistant-'):
processed_messages.append({
'role': 'assistant',
'content': msg['content']
})
else:
processed_messages.append(msg)
processed_message['role'] = 'assistant'
processed_messages.append(processed_message)
try:
completion = self.client.chat.completions.create(
model=self.model,
Expand All @@ -85,13 +82,15 @@ def chat():
base_url = request.args.get('base_url')
hidden = request.args.get('hidden', 'false').lower() == 'true'
search = request.args.get('search', 'true').lower() == 'true'
draw = request.args.get('draw', 'true').lower() == 'true'
stream = request.args.get('stream', 'true').lower() == 'true'
no_system = request.args.get('no_system', 'false').lower() == 'true'
time_param = request.args.get('time')
model = request.args.get('model')
if request.method == 'POST':
data = request.get_json()
message = data.get('message')
previous_message = data.get('previous')

if not user_id:
return jsonify({'status': 'error', 'message': 'Missing user_id'}), 400
Expand All @@ -104,7 +103,8 @@ def chat():
chat_log_entry = ChatLog(user_id=user_id, chat_log=json.dumps([]))

chat_log = json.loads(chat_log_entry.chat_log)

if previous_message!="":
chat_log.append({'role': 'assistant-'+model, 'content': previous_message})
chat_log.append({'role': 'user', 'content': message})

save_chat_log(user_id, chat_log)
Expand Down Expand Up @@ -133,13 +133,18 @@ def chat():
"datetime."}
if no_system:
full_log=chat_log
elif not search:
full_log = pre_prompt_searchless + [timeprompt] + chat_log
else:
full_log = pre_prompt + [timeprompt] + chat_log
full_log = []
for system_prompt in prompt_list:
if system_prompt['file_path'] == 'prompts/search.txt' and search == False:
continue
if system_prompt['file_path'] == 'prompts/draw.txt' and draw == False:
continue
full_log.append(system_prompt['message'])
full_log = full_log + [timeprompt] + chat_log
bot = ChatBot(stream=stream, api_key=api_key, base_url=base_url, model=model, messages=full_log)
if hidden:
original_chat_log = chat_log[:-1]
original_chat_log = chat_log[:-2]
save_chat_log(user_id, original_chat_log)
completion = bot.reply()
if completion is None:
Expand Down Expand Up @@ -248,9 +253,17 @@ def validate_uuid():

@app.route('/search_chat', methods=['POST'])
def search_chat():
notice = (
"This is a system message sent in the user's name. "
"You have attempted to search the internet "
"but the search has failed. Please notify the user. "
"Please do not reply to this message "
"but to the previous message by the user. Remember to "
"always reply in the user's language."
)
data = request.get_json()

if not data or 'message' not in data or 'prompt' not in data:
if not data or 'prompt' not in data:
return jsonify({'status': 'error', 'message': 'Missing Data'}), 400

prompts = data.get('prompt')
Expand All @@ -264,7 +277,10 @@ def search_chat():
duck = DDGS(proxy=os.environ["DUCK_PROXY"])
else:
duck = DDGS()
results = duck.text(prompt, max_results=10)
try:
results = duck.text(prompt, max_results=10)
except:
return jsonify({'status': 'error', 'message': 'Error searching the internet', 'notice': notice}), 500
bodies = [result['body'] for result in results]
titles = [result['title'] for result in results]
concat = ''.join(f"{id} -- {title}:{body}\n\n" for id, title, body in zip(range(1, len(bodies)+1), titles, bodies))
Expand All @@ -287,6 +303,41 @@ def search_chat():

return jsonify({'status': 'success','answer': answer}), 200

@app.route('/draw', methods=['POST'])
def draw():
data = request.get_json()

if not data or 'prompt' not in data:
return jsonify({'status': 'error', 'message': 'Missing Data'}), 400

description = data.get('prompt')[3:]
api_key = os.getenv('CLOUDFLARE_API_KEY')
user_id = os.getenv("CLOUDFLARE_USER_ID")
url = f'https://api.cloudflare.com/client/v4/accounts/{user_id}/ai/run/@cf/black-forest-labs/flux-1-schnell'
headers = {
'Authorization': f'Bearer {api_key}'
}

data = {
'prompt': description
}

response = requests.post(url, headers=headers, json=data)
data = response.json()

notice = (
"This is a system message sent in the user's name. "
"You have requested an image generation "
"but it failed. Please notify the user. "
"Please do not reply to this message "
"but to the previous message by the user. Remember to "
"always reply in the user's language. Do not attempt to "
"try again unless the user asks for it."
)
if response.status_code != 200 or data['success'] == False:
return jsonify({'status': 'error', 'message': 'Error generating image', 'notice': notice}), 400
return jsonify({'status': 'success','image': data['result']['image']}), 200

@app.after_request
def log_request(response):
ip = request.remote_addr
Expand Down
14 changes: 13 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,21 @@

## Update Log (only after 3.1.0):

### 3.3.0

#### [![](https://img.shields.io/badge/Dev-20241224-blue)](https://github.com/Davidasx/flask-gpt)

This is an revolutionary release.

Now has the ability to call CloudFlare AI's FLUX-schnell model to create images. The chatbot will aotumatically generate a image description and feed it to FLUX. The image will be generated in a few seconds and displayed in the message container.

Please set environment variables `CLOUDFLARE_API_KEY` and `CLOUDFLARE_USER_ID` to use this feature. Learn more about CloudFlare AI's FLUX model usage [here](https://developers.cloudflare.com/workers-ai/models/flux-1-schnell/).

Enjoy!

### 3.2.5.5

#### [![](https://img.shields.io/badge/Dev-20241223-blue)](https://github.com/Davidasx/flask-gpt)
#### [![](https://img.shields.io/badge/Dev-20241223-blue)](https://github.com/Davidasx/flask-gpt/tree/4e47dfe)

Emergency fix. Sorry for the inconvenience.

Expand Down
4 changes: 4 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ <h4 data-i18n="api-settings">API Settings</h4>
<input type="checkbox" id="search-enabled" onchange="setSearch(this.checked)">
<label for="search-enabled" data-i18n="search">Search</label>
</div>
<div class="checkbox-container">
<input type="checkbox" id="draw-enabled" onchange="setDraw(this.checked)">
<label for="draw-enabled" data-i18n="draw">Draw</label>
</div>
<div class="checkbox-container">
<input type="checkbox" id="no-system" onchange="setNoSystem(this.checked,false)">
<label for="no-system" data-i18n="no-system">Disable System Prompts</label>
Expand Down
9 changes: 9 additions & 0 deletions prompts/draw.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
You have the capablility to draw. If the user requests you to draw
an image, please reply "$#% <image description>".
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.
6 changes: 6 additions & 0 deletions prompts/prompts.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
]
}
20 changes: 13 additions & 7 deletions static/locales.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "3.2.5.4",
"version": "3.3",
"translations": {
"en": {
"input": "Enter your message",
Expand All @@ -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": "输入你的消息",
Expand All @@ -41,7 +42,8 @@
"model-name": "模型名称",
"stream": "流输出",
"search": "网络搜索",
"no-system": "禁用系统提示词"
"no-system": "禁用系统提示词",
"draw": "作图"
},
"zh-hk": {
"input": "輸入你的消息",
Expand All @@ -62,7 +64,8 @@
"model-name": "模型名稱",
"stream": "流輸出",
"search": "網絡搜索",
"no-system": "禁用系統提示詞"
"no-system": "禁用系統提示詞",
"draw": "作圖"
},
"fr": {
"input": "Entrez votre message",
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -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"
}
}
}
5 changes: 5 additions & 0 deletions static/scripts/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ function simulateBotMessage(content, role=null) {
const avatarSrc = getModelAvatar(role);
botMessage.innerHTML = `<img src="${avatarSrc}" alt="Bot Avatar"><div class="bubble"></div>`;
document.getElementById('chat-messages').appendChild(botMessage);
if (content.startsWith("<img src=\"data:image/png;base64,")) {
botMessage.querySelector('.bubble').innerHTML = content;
scrollToBottom();
return;
}

// First step: render the message content
botMessage.querySelector('.bubble').innerHTML = formatMessage(content);
Expand Down
Loading

0 comments on commit c42fa0e

Please sign in to comment.