27 Commits

Author SHA1 Message Date
Karim Iskakov 494ec7800a Change http version 2023-04-25 04:30:23 -05:00
Karim Iskakov d6fab25531 ❤️ Update top donations 2023-04-24 04:25:07 -05:00
Karim Iskakov 37e7101273 Fix edited messages in group chats 2023-04-24 04:11:21 -05:00
Karim Iskakov a014061917 Update chat_modes.yml 2023-04-21 11:11:01 -05:00
Karim Iskakov ada0f34e54 🔥 Add 10 new chat modes + Updated chat mode menu with pagination 2023-04-21 14:02:50 +03:00
Karim Iskakov 08e9c065ea Add return_n_generated_images to config 2023-04-21 05:24:19 -05:00
Karim Iskakov 8657adb463 Fix bot mention in group chat 2023-04-21 05:20:20 -05:00
Karim Iskakov 04d620ef32 Group Chat support (#247)
Based on @clemsau and @Tuarisa code
2023-04-21 13:16:02 +03:00
Megham Garg 85f149c975 Missing self from error handler 2023-04-21 12:46:08 +03:00
Karim Iskakov 6119e89560 Update README (DALLE 2 suport) 2023-04-21 12:42:19 +03:00
Karim Iskakov e421be1634 DALLE 2 2023-04-21 12:37:06 +03:00
Karim Iskakov 3c8edcb5e0 Update bot URL 2023-04-07 12:01:58 -05:00
Karim Iskakov aa38a1f5c7 Add /cancel command 2023-03-31 13:58:08 +07:00
Karim Iskakov a8cef77330 Add "Three Dots" message 2023-03-29 15:30:34 +07:00
Karim Iskakov 7b6a50998c Remove trailing whitespaces 2023-03-29 14:42:01 +07:00
Karim Iskakov 90caa8754d ❤️ Update top donations 2023-03-23 16:06:58 +03:00
Karim Iskakov a4b5eb36e1 GPT-4 support 2023-03-22 23:58:48 +03:00
Vishal Doshi 6fb8b17fe3 Make mongo & mongo-express safer 2023-03-20 12:58:01 +03:00
Karim Iskakov 7bd1d77be1 Update README 2023-03-17 06:26:53 -05:00
Karim Iskakov 1ba09de2b9 Add message streaming
* Add message streaming
* Update README.md
2023-03-15 19:20:43 +03:00
Dmitry Gordin 2b4d4ad860 Add commands suggestions via set_my_commands api method
* add command suggestions via set_my_commands method

* update README to remove manual instructions for bot menu
2023-03-15 12:55:27 +03:00
Karim Iskakov 386e396c0b Update README.md 2023-03-14 08:25:23 -05:00
Clément Sauvage 76da87bb31 Add support for user ids in config
* Add support for user ids

* Allow both usernames and user ids at the same time
2023-03-14 15:22:06 +03:00
Eason Chen 35077ec20d Add instructions to add command hints at BotFather
* Add: instructions to add command hints at BotFather

* Update: instructions at README.md
2023-03-10 16:47:07 +03:00
Karim Iskakov 2248a2bd92 Fix typo 2023-03-09 08:42:08 -06:00
Karim Iskakov 15e4ec513a Separate config for Chat Modes 2023-03-09 14:51:43 +03:00
Karim Iskakov 319d8fc8f4 Handle long messages 2023-03-08 15:50:07 -06:00
12 changed files with 879 additions and 175 deletions
+3 -1
View File
@@ -132,4 +132,6 @@ dmypy.json
config/config.yml
config/config.env
docker-compose.dev.yml
docker-compose.dev.yml
mongodb/
+60 -24
View File
@@ -1,46 +1,73 @@
# ChatGPT Telegram Bot: **Fast. No daily limits. Special chat modes**
# ChatGPT Telegram Bot: **GPT-4. Fast. No daily limits. Special chat modes**
<div align="center">
<img src="https://raw.githubusercontent.com/karfly/chatgpt_telegram_bot/main/static/header.png" align="center" style="width: 100%" />
</div>
<br>
<p align="center">
<a href="https://t.me/chatgpt_karfly_bot" alt="Run Telegram Bot shield"><img src="https://img.shields.io/badge/RUN-Telegram%20Bot-blue?logo=data:image/svg+xml;base64,PHN2ZyBpZD0iTGl2ZWxsb18xIiBkYXRhLW5hbWU9IkxpdmVsbG8gMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmlld0JveD0iMCAwIDI0MCAyNDAiPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0ibGluZWFyLWdyYWRpZW50IiB4MT0iMTIwIiB5MT0iMjQwIiB4Mj0iMTIwIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMWQ5M2QyIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMzhiMGUzIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHRpdGxlPlRlbGVncmFtX2xvZ288L3RpdGxlPjxjaXJjbGUgY3g9IjEyMCIgY3k9IjEyMCIgcj0iMTIwIiBmaWxsPSJ1cmwoI2xpbmVhci1ncmFkaWVudCkiLz48cGF0aCBkPSJNODEuMjI5LDEyOC43NzJsMTQuMjM3LDM5LjQwNnMxLjc4LDMuNjg3LDMuNjg2LDMuNjg3LDMwLjI1NS0yOS40OTIsMzAuMjU1LTI5LjQ5MmwzMS41MjUtNjAuODlMODEuNzM3LDExOC42WiIgZmlsbD0iI2M4ZGFlYSIvPjxwYXRoIGQ9Ik0xMDAuMTA2LDEzOC44NzhsLTIuNzMzLDI5LjA0NnMtMS4xNDQsOC45LDcuNzU0LDAsMTcuNDE1LTE1Ljc2MywxNy40MTUtMTUuNzYzIiBmaWxsPSIjYTljNmQ4Ii8+PHBhdGggZD0iTTgxLjQ4NiwxMzAuMTc4LDUyLjIsMTIwLjYzNnMtMy41LTEuNDItMi4zNzMtNC42NGMuMjMyLS42NjQuNy0xLjIyOSwyLjEtMi4yLDYuNDg5LTQuNTIzLDEyMC4xMDYtNDUuMzYsMTIwLjEwNi00NS4zNnMzLjIwOC0xLjA4MSw1LjEtLjM2MmEyLjc2NiwyLjc2NiwwLDAsMSwxLjg4NSwyLjA1NSw5LjM1Nyw5LjM1NywwLDAsMSwuMjU0LDIuNTg1Yy0uMDA5Ljc1Mi0uMSwxLjQ0OS0uMTY5LDIuNTQyLS42OTIsMTEuMTY1LTIxLjQsOTQuNDkzLTIxLjQsOTQuNDkzcy0xLjIzOSw0Ljg3Ni01LjY3OCw1LjA0M0E4LjEzLDguMTMsMCwwLDEsMTQ2LjEsMTcyLjVjLTguNzExLTcuNDkzLTM4LjgxOS0yNy43MjctNDUuNDcyLTMyLjE3N2ExLjI3LDEuMjcsMCwwLDEtLjU0Ni0uOWMtLjA5My0uNDY5LjQxNy0xLjA1LjQxNy0xLjA1czUyLjQyNi00Ni42LDUzLjgyMS01MS40OTJjLjEwOC0uMzc5LS4zLS41NjYtLjg0OC0uNC0zLjQ4MiwxLjI4MS02My44NDQsMzkuNC03MC41MDYsNDMuNjA3QTMuMjEsMy4yMSwwLDAsMSw4MS40ODYsMTMwLjE3OFoiIGZpbGw9IiNmZmYiLz48L3N2Zz4=" /></a>
<a href="https://t.me/chatgpt_karfly_bot?start=source=github" alt="Run Telegram Bot shield"><img src="https://img.shields.io/badge/RUN-Telegram%20Bot-blue?logo=data:image/svg+xml;base64,PHN2ZyBpZD0iTGl2ZWxsb18xIiBkYXRhLW5hbWU9IkxpdmVsbG8gMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmlld0JveD0iMCAwIDI0MCAyNDAiPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0ibGluZWFyLWdyYWRpZW50IiB4MT0iMTIwIiB5MT0iMjQwIiB4Mj0iMTIwIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMWQ5M2QyIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMzhiMGUzIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHRpdGxlPlRlbGVncmFtX2xvZ288L3RpdGxlPjxjaXJjbGUgY3g9IjEyMCIgY3k9IjEyMCIgcj0iMTIwIiBmaWxsPSJ1cmwoI2xpbmVhci1ncmFkaWVudCkiLz48cGF0aCBkPSJNODEuMjI5LDEyOC43NzJsMTQuMjM3LDM5LjQwNnMxLjc4LDMuNjg3LDMuNjg2LDMuNjg3LDMwLjI1NS0yOS40OTIsMzAuMjU1LTI5LjQ5MmwzMS41MjUtNjAuODlMODEuNzM3LDExOC42WiIgZmlsbD0iI2M4ZGFlYSIvPjxwYXRoIGQ9Ik0xMDAuMTA2LDEzOC44NzhsLTIuNzMzLDI5LjA0NnMtMS4xNDQsOC45LDcuNzU0LDAsMTcuNDE1LTE1Ljc2MywxNy40MTUtMTUuNzYzIiBmaWxsPSIjYTljNmQ4Ii8+PHBhdGggZD0iTTgxLjQ4NiwxMzAuMTc4LDUyLjIsMTIwLjYzNnMtMy41LTEuNDItMi4zNzMtNC42NGMuMjMyLS42NjQuNy0xLjIyOSwyLjEtMi4yLDYuNDg5LTQuNTIzLDEyMC4xMDYtNDUuMzYsMTIwLjEwNi00NS4zNnMzLjIwOC0xLjA4MSw1LjEtLjM2MmEyLjc2NiwyLjc2NiwwLDAsMSwxLjg4NSwyLjA1NSw5LjM1Nyw5LjM1NywwLDAsMSwuMjU0LDIuNTg1Yy0uMDA5Ljc1Mi0uMSwxLjQ0OS0uMTY5LDIuNTQyLS42OTIsMTEuMTY1LTIxLjQsOTQuNDkzLTIxLjQsOTQuNDkzcy0xLjIzOSw0Ljg3Ni01LjY3OCw1LjA0M0E4LjEzLDguMTMsMCwwLDEsMTQ2LjEsMTcyLjVjLTguNzExLTcuNDkzLTM4LjgxOS0yNy43MjctNDUuNDcyLTMyLjE3N2ExLjI3LDEuMjcsMCwwLDEtLjU0Ni0uOWMtLjA5My0uNDY5LjQxNy0xLjA1LjQxNy0xLjA1czUyLjQyNi00Ni42LDUzLjgyMS01MS40OTJjLjEwOC0uMzc5LS4zLS41NjYtLjg0OC0uNC0zLjQ4MiwxLjI4MS02My44NDQsMzkuNC03MC41MDYsNDMuNjA3QTMuMjEsMy4yMSwwLDAsMSw4MS40ODYsMTMwLjE3OFoiIGZpbGw9IiNmZmYiLz48L3N2Zz4=" width="230"/></a>
</p>
<p align="center">
<a href="https://github.com/karfly/chatgpt_telegram_bot/blob/main/static/donate/donate.md#%EF%B8%8F-donate" alt="Donate shield"><img src="https://img.shields.io/badge/-Donate-red?logo=undertale" /></a>
</p>
<p align="center">
<a href="https://t.me/karim_iskakov" alt="Subscribe Telegram Channel shield"><img src="https://img.shields.io/badge/Telegram%20Channel-Subscribe-blue?style=social&logo=data:image/svg+xml;base64,PHN2ZyBpZD0iTGl2ZWxsb18xIiBkYXRhLW5hbWU9IkxpdmVsbG8gMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmlld0JveD0iMCAwIDI0MCAyNDAiPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0ibGluZWFyLWdyYWRpZW50IiB4MT0iMTIwIiB5MT0iMjQwIiB4Mj0iMTIwIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMWQ5M2QyIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMzhiMGUzIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHRpdGxlPlRlbGVncmFtX2xvZ288L3RpdGxlPjxjaXJjbGUgY3g9IjEyMCIgY3k9IjEyMCIgcj0iMTIwIiBmaWxsPSJ1cmwoI2xpbmVhci1ncmFkaWVudCkiLz48cGF0aCBkPSJNODEuMjI5LDEyOC43NzJsMTQuMjM3LDM5LjQwNnMxLjc4LDMuNjg3LDMuNjg2LDMuNjg3LDMwLjI1NS0yOS40OTIsMzAuMjU1LTI5LjQ5MmwzMS41MjUtNjAuODlMODEuNzM3LDExOC42WiIgZmlsbD0iI2M4ZGFlYSIvPjxwYXRoIGQ9Ik0xMDAuMTA2LDEzOC44NzhsLTIuNzMzLDI5LjA0NnMtMS4xNDQsOC45LDcuNzU0LDAsMTcuNDE1LTE1Ljc2MywxNy40MTUtMTUuNzYzIiBmaWxsPSIjYTljNmQ4Ii8+PHBhdGggZD0iTTgxLjQ4NiwxMzAuMTc4LDUyLjIsMTIwLjYzNnMtMy41LTEuNDItMi4zNzMtNC42NGMuMjMyLS42NjQuNy0xLjIyOSwyLjEtMi4yLDYuNDg5LTQuNTIzLDEyMC4xMDYtNDUuMzYsMTIwLjEwNi00NS4zNnMzLjIwOC0xLjA4MSw1LjEtLjM2MmEyLjc2NiwyLjc2NiwwLDAsMSwxLjg4NSwyLjA1NSw5LjM1Nyw5LjM1NywwLDAsMSwuMjU0LDIuNTg1Yy0uMDA5Ljc1Mi0uMSwxLjQ0OS0uMTY5LDIuNTQyLS42OTIsMTEuMTY1LTIxLjQsOTQuNDkzLTIxLjQsOTQuNDkzcy0xLjIzOSw0Ljg3Ni01LjY3OCw1LjA0M0E4LjEzLDguMTMsMCwwLDEsMTQ2LjEsMTcyLjVjLTguNzExLTcuNDkzLTM4LjgxOS0yNy43MjctNDUuNDcyLTMyLjE3N2ExLjI3LDEuMjcsMCwwLDEtLjU0Ni0uOWMtLjA5My0uNDY5LjQxNy0xLjA1LjQxNy0xLjA1czUyLjQyNi00Ni42LDUzLjgyMS01MS40OTJjLjEwOC0uMzc5LS4zLS41NjYtLjg0OC0uNC0zLjQ4MiwxLjI4MS02My44NDQsMzkuNC03MC41MDYsNDMuNjA3QTMuMjEsMy4yMSwwLDAsMSw4MS40ODYsMTMwLjE3OFoiIGZpbGw9IiNmZmYiLz48L3N2Zz4=" /></a>
<a href="https://github.com/karfly/chatgpt_telegram_bot/blob/main/static/donate/donate.md#%EF%B8%8F-donate" alt="Donate shield"><img src="https://img.shields.io/badge/-Donate-red?logo=undertale" width="100"/></a>
</p>
We all love [chat.openai.com](https://chat.openai.com), but... It's TERRIBLY laggy, has daily limits, and is only accessible through an archaic web interface.
This repo is ChatGPT re-created with GPT-3.5 LLM as Telegram Bot. **And it works great.**
This repo is ChatGPT re-created as Telegram Bot. **And it works great.**
You can deploy your own bot, or use mine: [@chatgpt_karfly_bot](https://t.me/chatgpt_karfly_bot)
## News
- *8 Mar 2023*: Added voice message recognition with [OpenAI Whisper API](https://openai.com/blog/introducing-chatgpt-and-whisper-apis). Record a voice message and ChatGPT will answer you!
- *2 Mar 2023*: Added support of [ChatGPT API](https://platform.openai.com/docs/guides/chat/introduction). It's enabled by default and can be disabled with `use_chatgpt_api` option in config. Don't forget to **rebuild** you docker image (`--build`).
## Features
- Low latency replies (it usually takes about 3-5 seconds)
- Low latency replies (it usually takes about 3-5 seconds)
- No request limits
- Message streaming (watch demo)
- GPT-4 support
- Group Chat support (/help_group_chat to get instructions)
- DALLE 2 (choose 👩‍🎨 Artist mode to generate images)
- Voice message recognition
- Code highlighting
- Special chat modes: 👩🏼‍🎓 Assistant, 👩🏼‍💻 Code Assistant, 🎬 Movie Expert. More soon
- 15 special chat modes: 👩🏼‍🎓 Assistant, 👩🏼‍💻 Code Assistant, 👩‍🎨 Artist, 🧠 Psychologist, 🚀 Elon Musk and other. You can easily create your own chat modes by editing `config/chat_modes.yml`
- Support of [ChatGPT API](https://platform.openai.com/docs/guides/chat/introduction)
- List of allowed Telegram users
- Track $ balance spent on OpenAI API
<p align="center">
<img src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExYmM2ZWVjY2M4NWQ3ZThkYmQ3MDhmMTEzZGUwOGFmOThlMDIzZGM4YiZjdD1n/unx907h7GSiLAugzVX/giphy.gif" />
</p>
---
## 🤑 Payments
[My bot](https://t.me/chatgpt_karfly_bot) supports many payments providers:
- 💎 Crypto
- [Stripe](https://stripe.com)
- [Smart Glocal](https://smart-glocal.com)
- [Unlimint](https://www.unlimint.com)
- [ЮMoney](https://yoomoney.ru)
- and [many-many other](https://core.telegram.org/bots/payments#supported-payment-providers)
If you want to add payments to your bot and create profitable business write me on Telegram ([@karfly](https://t.me/karfly)).
## News
- *21 Apr 2023*:
- DALLE 2 support
- Group Chat support (/help_group_chat to get instructions)
- 10 new hot chat modes and updated chat mode menu with pagination: 🇬🇧 English Tutor, 🧠 Psychologist, 🚀 Elon Musk, 📊 SQL Assistant and other.
- *24 Mar 2023*: GPT-4 support. Run `/settings` command to choose model
- *15 Mar 2023*: Added message streaming. Now you don't have to wait until the whole message is ready, it's streamed to Telegram part-by-part (watch demo)
- *9 Mar 2023*: Now you can easily create your own Chat Modes by editing `config/chat_modes.yml`
- *8 Mar 2023*: Added voice message recognition with [OpenAI Whisper API](https://openai.com/blog/introducing-chatgpt-and-whisper-apis). Record a voice message and ChatGPT will answer you!
- *2 Mar 2023*: Added support of [ChatGPT API](https://platform.openai.com/docs/guides/chat/introduction). It's enabled by default and can be disabled with `use_chatgpt_api` option in config. Don't forget to **rebuild** you docker image (`--build`).
## Bot commands
- `/retry` Regenerate last bot answer
- `/new` Start new dialog
- `/mode` Select chat mode
- `/balance` Show balance
- `/settings` Show settings
- `/help` Show help
## Setup
@@ -49,23 +76,32 @@ You can deploy your own bot, or use mine: [@chatgpt_karfly_bot](https://t.me/cha
2. Get your Telegram bot token from [@BotFather](https://t.me/BotFather)
3. Edit `config/config.example.yml` to set your tokens and run 2 commands below (*if you're advanced user, you can also edit* `config/config.example.env`):
```bash
mv config/config.example.yml config/config.yml
mv config/config.example.env config/config.env
```
```bash
mv config/config.example.yml config/config.yml
mv config/config.example.env config/config.env
```
🔥 And now **run**:
```bash
docker-compose --env-file config/config.env up --build
```
4. 🔥 And now **run**:
```bash
docker-compose --env-file config/config.env up --build
```
## ❤️ Top donations
You can be in this list: <a href="https://github.com/karfly/chatgpt_telegram_bot/blob/main/static/donate/donate.md#%EF%B8%8F-donate" alt="Donate shield"><img src="https://img.shields.io/badge/-Donate-red?logo=undertale" /></a>
1. [Ilias Ism](https://twitter.com/illyism). Donation: **69$**
1. [Sem](https://t.me/sembrestels). Donation: **100$**
2. [Ryo](https://t.me/ryokihara). Donation: **80$**
3. [Ilias Ism](https://twitter.com/illyism). Donation: **69$**
*Message:* I wanted to thank you for your amazing code! It helped me start my own Telegram ChatGPT bot and add a bunch of cool features. I really appreciate your hard work on this project. For anyone interested in trying my bot, feel free to check it out here: [magicbuddy.chat](https://magicbuddy.chat) 🤖 Thanks again! 😊
4. [Sebastian](https://t.me/dell1503). Donation: **55$**
5. [Alexander Zimin](https://t.me/azimin). Donation: **50$**
6. [Hans Blinken](https://t.me/hblink). Donation: **10$**
## References
1. [*Build ChatGPT from GPT-3*](https://learnprompting.org/docs/applied_prompting/build_chatgpt)
+481 -91
View File
@@ -1,5 +1,6 @@
import os
import logging
import asyncio
import traceback
import html
import json
@@ -7,15 +8,24 @@ import tempfile
import pydub
from pathlib import Path
from datetime import datetime
import openai
import telegram
from telegram import Update, User, InlineKeyboardButton, InlineKeyboardMarkup
from telegram import (
Update,
User,
InlineKeyboardButton,
InlineKeyboardMarkup,
BotCommand
)
from telegram.ext import (
Application,
ApplicationBuilder,
CallbackContext,
CommandHandler,
MessageHandler,
CallbackQueryHandler,
AIORateLimiter,
filters
)
from telegram.constants import ParseMode, ChatAction
@@ -29,14 +39,39 @@ import openai_utils
db = database.Database()
logger = logging.getLogger(__name__)
user_semaphores = {}
user_tasks = {}
HELP_MESSAGE = """Commands:
⚪ /retry Regenerate last bot answer
⚪ /new Start new dialog
⚪ /mode Select chat mode
⚪ /settings Show settings
⚪ /balance Show balance
⚪ /help Show help
🎨 Generate images from text prompts in <b>👩‍🎨 Artist</b> /mode
👥 Add bot to <b>group chat</b>: /help_group_chat
🎤 You can send <b>Voice Messages</b> instead of text
"""
HELP_GROUP_CHAT_MESSAGE = """You can add bot to any <b>group chat</b> to help and entertain its participants!
Instructions (see <b>video</b> below):
1. Add the bot to the group chat
2. Make it an <b>admin</b>, so that it can see messages (all other rights can be restricted)
3. You're awesome!
To get a reply from the bot in the chat @ <b>tag</b> it or <b>reply</b> to its message.
For example: "{bot_username} write a poem about Telegram"
"""
def split_text_into_chunks(text, chunk_size):
for i in range(0, len(text), chunk_size):
yield text[i:i + chunk_size]
async def register_user_if_not_exists(update: Update, context: CallbackContext, user: User):
if not db.check_if_user_exists(user.id):
db.add_new_user(
@@ -51,20 +86,63 @@ async def register_user_if_not_exists(update: Update, context: CallbackContext,
if db.get_user_attribute(user.id, "current_dialog_id") is None:
db.start_new_dialog(user.id)
if user.id not in user_semaphores:
user_semaphores[user.id] = asyncio.Semaphore(1)
if db.get_user_attribute(user.id, "current_model") is None:
db.set_user_attribute(user.id, "current_model", config.models["available_text_models"][0])
# back compatibility for n_used_tokens field
n_used_tokens = db.get_user_attribute(user.id, "n_used_tokens")
if isinstance(n_used_tokens, int): # old format
new_n_used_tokens = {
"gpt-3.5-turbo": {
"n_input_tokens": 0,
"n_output_tokens": n_used_tokens
}
}
db.set_user_attribute(user.id, "n_used_tokens", new_n_used_tokens)
# voice message transcription
if db.get_user_attribute(user.id, "n_transcribed_seconds") is None:
db.set_user_attribute(user.id, "n_transcribed_seconds", 0.0)
# image generation
if db.get_user_attribute(user.id, "n_generated_images") is None:
db.set_user_attribute(user.id, "n_generated_images", 0)
async def is_bot_mentioned(update: Update, context: CallbackContext):
try:
message = update.message
if message.chat.type == "private":
return True
if message.text is not None and ("@" + context.bot.username) in message.text:
return True
if message.reply_to_message is not None:
if message.reply_to_message.from_user.id == context.bot.id:
return True
except:
return True
else:
return False
async def start_handle(update: Update, context: CallbackContext):
await register_user_if_not_exists(update, context, update.message.from_user)
user_id = update.message.from_user.id
db.set_user_attribute(user_id, "last_interaction", datetime.now())
db.start_new_dialog(user_id)
reply_text = "Hi! I'm <b>ChatGPT</b> bot implemented with GPT-3.5 OpenAI API 🤖\n\n"
reply_text = "Hi! I'm <b>ChatGPT</b> bot implemented with OpenAI API 🤖\n\n"
reply_text += HELP_MESSAGE
reply_text += "\nAnd now... ask me anything!"
await update.message.reply_text(reply_text, parse_mode=ParseMode.HTML)
await show_chat_modes_handle(update, context)
async def help_handle(update: Update, context: CallbackContext):
@@ -74,8 +152,21 @@ async def help_handle(update: Update, context: CallbackContext):
await update.message.reply_text(HELP_MESSAGE, parse_mode=ParseMode.HTML)
async def help_group_chat_handle(update: Update, context: CallbackContext):
await register_user_if_not_exists(update, context, update.message.from_user)
user_id = update.message.from_user.id
db.set_user_attribute(user_id, "last_interaction", datetime.now())
text = HELP_GROUP_CHAT_MESSAGE.format(bot_username="@" + context.bot.username)
await update.message.reply_text(text, parse_mode=ParseMode.HTML)
await update.message.reply_video(config.help_group_chat_video_path)
async def retry_handle(update: Update, context: CallbackContext):
await register_user_if_not_exists(update, context, update.message.from_user)
if await is_previous_message_not_answered_yet(update, context): return
user_id = update.message.from_user.id
db.set_user_attribute(user_id, "last_interaction", datetime.now())
@@ -91,67 +182,162 @@ async def retry_handle(update: Update, context: CallbackContext):
async def message_handle(update: Update, context: CallbackContext, message=None, use_new_dialog_timeout=True):
# check if bot was mentioned (for group chats)
if not await is_bot_mentioned(update, context):
return
# check if message is edited
if update.edited_message is not None:
await edited_message_handle(update, context)
return
_message = message or update.message.text
# remove bot mention (in group chats)
if update.message.chat.type != "private":
_message = _message.replace("@" + context.bot.username, "").strip()
await register_user_if_not_exists(update, context, update.message.from_user)
if await is_previous_message_not_answered_yet(update, context): return
user_id = update.message.from_user.id
chat_mode = db.get_user_attribute(user_id, "current_chat_mode")
# new dialog timeout
if use_new_dialog_timeout:
if (datetime.now() - db.get_user_attribute(user_id, "last_interaction")).seconds > config.new_dialog_timeout and len(db.get_dialog_messages(user_id)) > 0:
db.start_new_dialog(user_id)
await update.message.reply_text("Starting new dialog due to timeout ✅")
db.set_user_attribute(user_id, "last_interaction", datetime.now())
# send typing action
await update.message.chat.send_action(action="typing")
try:
message = message or update.message.text
chatgpt_instance = openai_utils.ChatGPT(use_chatgpt_api=config.use_chatgpt_api)
answer, n_used_tokens, n_first_dialog_messages_removed = await chatgpt_instance.send_message(
message,
dialog_messages=db.get_dialog_messages(user_id, dialog_id=None),
chat_mode=db.get_user_attribute(user_id, "current_chat_mode"),
)
# update user data
new_dialog_message = {"user": message, "bot": answer, "date": datetime.now()}
db.set_dialog_messages(
user_id,
db.get_dialog_messages(user_id, dialog_id=None) + [new_dialog_message],
dialog_id=None
)
db.set_user_attribute(user_id, "n_used_tokens", n_used_tokens + db.get_user_attribute(user_id, "n_used_tokens"))
except Exception as e:
error_text = f"Something went wrong during completion. Reason: {e}"
logger.error(error_text)
await update.message.reply_text(error_text)
if chat_mode == "artist":
await generate_image_handle(update, context, message=message)
return
# send message if some messages were removed from the context
if n_first_dialog_messages_removed > 0:
if n_first_dialog_messages_removed == 1:
text = "✍️ <i>Note:</i> Your current dialog is too long, so your <b>first message</b> was removed from the context.\n Send /new command to start new dialog"
else:
text = f"✍️ <i>Note:</i> Your current dialog is too long, so <b>{n_first_dialog_messages_removed} first messages</b> were removed from the context.\n Send /new command to start new dialog"
await update.message.reply_text(text, parse_mode=ParseMode.HTML)
async def message_handle_fn():
# new dialog timeout
if use_new_dialog_timeout:
if (datetime.now() - db.get_user_attribute(user_id, "last_interaction")).seconds > config.new_dialog_timeout and len(db.get_dialog_messages(user_id)) > 0:
db.start_new_dialog(user_id)
await update.message.reply_text(f"Starting new dialog due to timeout (<b>{config.chat_modes[chat_mode]['name']}</b> mode) ✅", parse_mode=ParseMode.HTML)
db.set_user_attribute(user_id, "last_interaction", datetime.now())
try:
await update.message.reply_text(answer, parse_mode=ParseMode.HTML)
except telegram.error.BadRequest:
# answer has invalid characters, so we send it without parse_mode
await update.message.reply_text(answer)
# in case of CancelledError
n_input_tokens, n_output_tokens = 0, 0
current_model = db.get_user_attribute(user_id, "current_model")
try:
# send placeholder message to user
placeholder_message = await update.message.reply_text("...")
# send typing action
await update.message.chat.send_action(action="typing")
if _message is None or len(_message) == 0:
await update.message.reply_text("🥲 You sent <b>empty message</b>. Please, try again!", parse_mode=ParseMode.HTML)
return
dialog_messages = db.get_dialog_messages(user_id, dialog_id=None)
parse_mode = {
"html": ParseMode.HTML,
"markdown": ParseMode.MARKDOWN
}[config.chat_modes[chat_mode]["parse_mode"]]
chatgpt_instance = openai_utils.ChatGPT(model=current_model)
if config.enable_message_streaming:
gen = chatgpt_instance.send_message_stream(_message, dialog_messages=dialog_messages, chat_mode=chat_mode)
else:
answer, (n_input_tokens, n_output_tokens), n_first_dialog_messages_removed = await chatgpt_instance.send_message(
_message,
dialog_messages=dialog_messages,
chat_mode=chat_mode
)
async def fake_gen():
yield "finished", answer, (n_input_tokens, n_output_tokens), n_first_dialog_messages_removed
gen = fake_gen()
prev_answer = ""
async for gen_item in gen:
status, answer, (n_input_tokens, n_output_tokens), n_first_dialog_messages_removed = gen_item
answer = answer[:4096] # telegram message limit
# update only when 100 new symbols are ready
if abs(len(answer) - len(prev_answer)) < 100 and status != "finished":
continue
try:
await context.bot.edit_message_text(answer, chat_id=placeholder_message.chat_id, message_id=placeholder_message.message_id, parse_mode=parse_mode)
except telegram.error.BadRequest as e:
if str(e).startswith("Message is not modified"):
continue
else:
await context.bot.edit_message_text(answer, chat_id=placeholder_message.chat_id, message_id=placeholder_message.message_id)
await asyncio.sleep(0.01) # wait a bit to avoid flooding
prev_answer = answer
# update user data
new_dialog_message = {"user": _message, "bot": answer, "date": datetime.now()}
db.set_dialog_messages(
user_id,
db.get_dialog_messages(user_id, dialog_id=None) + [new_dialog_message],
dialog_id=None
)
db.update_n_used_tokens(user_id, current_model, n_input_tokens, n_output_tokens)
except asyncio.CancelledError:
# note: intermediate token updates only work when enable_message_streaming=True (config.yml)
db.update_n_used_tokens(user_id, current_model, n_input_tokens, n_output_tokens)
raise
except Exception as e:
error_text = f"Something went wrong during completion. Reason: {e}"
logger.error(error_text)
await update.message.reply_text(error_text)
return
# send message if some messages were removed from the context
if n_first_dialog_messages_removed > 0:
if n_first_dialog_messages_removed == 1:
text = "✍️ <i>Note:</i> Your current dialog is too long, so your <b>first message</b> was removed from the context.\n Send /new command to start new dialog"
else:
text = f"✍️ <i>Note:</i> Your current dialog is too long, so <b>{n_first_dialog_messages_removed} first messages</b> were removed from the context.\n Send /new command to start new dialog"
await update.message.reply_text(text, parse_mode=ParseMode.HTML)
async with user_semaphores[user_id]:
task = asyncio.create_task(message_handle_fn())
user_tasks[user_id] = task
try:
await task
except asyncio.CancelledError:
await update.message.reply_text("✅ Canceled", parse_mode=ParseMode.HTML)
else:
pass
finally:
if user_id in user_tasks:
del user_tasks[user_id]
async def is_previous_message_not_answered_yet(update: Update, context: CallbackContext):
await register_user_if_not_exists(update, context, update.message.from_user)
user_id = update.message.from_user.id
if user_semaphores[user_id].locked():
text = "⏳ Please <b>wait</b> for a reply to the previous message\n"
text += "Or you can /cancel it"
await update.message.reply_text(text, reply_to_message_id=update.message.id, parse_mode=ParseMode.HTML)
return True
else:
return False
async def voice_message_handle(update: Update, context: CallbackContext):
# check if bot was mentioned (for group chats)
if not await is_bot_mentioned(update, context):
return
await register_user_if_not_exists(update, context, update.message.from_user)
if await is_previous_message_not_answered_yet(update, context): return
user_id = update.message.from_user.id
db.set_user_attribute(user_id, "last_interaction", datetime.now())
@@ -172,22 +358,51 @@ async def voice_message_handle(update: Update, context: CallbackContext):
with open(voice_mp3_path, "rb") as f:
transcribed_text = await openai_utils.transcribe_audio(f)
if transcribed_text is None:
transcribed_text = ""
text = f"🎤: <i>{transcribed_text}</i>"
await update.message.reply_text(text, parse_mode=ParseMode.HTML)
# update n_transcribed_seconds
db.set_user_attribute(user_id, "n_transcribed_seconds", voice.duration + db.get_user_attribute(user_id, "n_transcribed_seconds"))
await message_handle(update, context, message=transcribed_text)
# calculate spent dollars
n_spent_dollars = voice.duration * (config.whisper_price_per_1_min / 60)
# normalize dollars to tokens (it's very convenient to measure everything in a single unit)
price_per_1000_tokens = config.chatgpt_price_per_1000_tokens if config.use_chatgpt_api else config.gpt_price_per_1000_tokens
n_used_tokens = int(n_spent_dollars / (price_per_1000_tokens / 1000))
db.set_user_attribute(user_id, "n_used_tokens", n_used_tokens + db.get_user_attribute(user_id, "n_used_tokens"))
async def generate_image_handle(update: Update, context: CallbackContext, message=None):
await register_user_if_not_exists(update, context, update.message.from_user)
if await is_previous_message_not_answered_yet(update, context): return
user_id = update.message.from_user.id
db.set_user_attribute(user_id, "last_interaction", datetime.now())
await update.message.chat.send_action(action="upload_photo")
message = message or update.message.text
try:
image_urls = await openai_utils.generate_images(message, n_images=config.return_n_generated_images)
except openai.error.InvalidRequestError as e:
if str(e).startswith("Your request was rejected as a result of our safety system"):
text = "🥲 Your request <b>doesn't comply</b> with OpenAI's usage policies.\nWhat did you write there, huh?"
await update.message.reply_text(text, parse_mode=ParseMode.HTML)
return
else:
raise
# token usage
db.set_user_attribute(user_id, "n_generated_images", config.return_n_generated_images + db.get_user_attribute(user_id, "n_generated_images"))
for i, image_url in enumerate(image_urls):
await update.message.chat.send_action(action="upload_photo")
await update.message.reply_photo(image_url, parse_mode=ParseMode.HTML)
async def new_dialog_handle(update: Update, context: CallbackContext):
await register_user_if_not_exists(update, context, update.message.from_user)
if await is_previous_message_not_answered_yet(update, context): return
user_id = update.message.from_user.id
db.set_user_attribute(user_id, "last_interaction", datetime.now())
@@ -195,20 +410,90 @@ async def new_dialog_handle(update: Update, context: CallbackContext):
await update.message.reply_text("Starting new dialog ✅")
chat_mode = db.get_user_attribute(user_id, "current_chat_mode")
await update.message.reply_text(f"{openai_utils.CHAT_MODES[chat_mode]['welcome_message']}", parse_mode=ParseMode.HTML)
await update.message.reply_text(f"{config.chat_modes[chat_mode]['welcome_message']}", parse_mode=ParseMode.HTML)
async def cancel_handle(update: Update, context: CallbackContext):
await register_user_if_not_exists(update, context, update.message.from_user)
user_id = update.message.from_user.id
db.set_user_attribute(user_id, "last_interaction", datetime.now())
if user_id in user_tasks:
task = user_tasks[user_id]
task.cancel()
else:
await update.message.reply_text("<i>Nothing to cancel...</i>", parse_mode=ParseMode.HTML)
def get_chat_mode_menu(page_index: int):
n_chat_modes_per_page = config.n_chat_modes_per_page
text = f"Select <b>chat mode</b> ({len(config.chat_modes)} modes available):"
# buttons
chat_mode_keys = list(config.chat_modes.keys())
page_chat_mode_keys = chat_mode_keys[page_index * n_chat_modes_per_page:(page_index + 1) * n_chat_modes_per_page]
keyboard = []
for chat_mode_key in page_chat_mode_keys:
name = config.chat_modes[chat_mode_key]["name"]
keyboard.append([InlineKeyboardButton(name, callback_data=f"set_chat_mode|{chat_mode_key}")])
# pagination
if len(chat_mode_keys) > n_chat_modes_per_page:
is_first_page = (page_index == 0)
is_last_page = ((page_index + 1) * n_chat_modes_per_page >= len(chat_mode_keys))
if is_first_page:
keyboard.append([
InlineKeyboardButton("»", callback_data=f"show_chat_modes|{page_index + 1}")
])
elif is_last_page:
keyboard.append([
InlineKeyboardButton("«", callback_data=f"show_chat_modes|{page_index - 1}"),
])
else:
keyboard.append([
InlineKeyboardButton("«", callback_data=f"show_chat_modes|{page_index - 1}"),
InlineKeyboardButton("»", callback_data=f"show_chat_modes|{page_index + 1}")
])
reply_markup = InlineKeyboardMarkup(keyboard)
return text, reply_markup
async def show_chat_modes_handle(update: Update, context: CallbackContext):
await register_user_if_not_exists(update, context, update.message.from_user)
if await is_previous_message_not_answered_yet(update, context): return
user_id = update.message.from_user.id
db.set_user_attribute(user_id, "last_interaction", datetime.now())
keyboard = []
for chat_mode, chat_mode_dict in openai_utils.CHAT_MODES.items():
keyboard.append([InlineKeyboardButton(chat_mode_dict["name"], callback_data=f"set_chat_mode|{chat_mode}")])
reply_markup = InlineKeyboardMarkup(keyboard)
text, reply_markup = get_chat_mode_menu(0)
await update.message.reply_text(text, reply_markup=reply_markup, parse_mode=ParseMode.HTML)
await update.message.reply_text("Select chat mode:", reply_markup=reply_markup)
async def show_chat_modes_callback_handle(update: Update, context: CallbackContext):
await register_user_if_not_exists(update.callback_query, context, update.callback_query.from_user)
if await is_previous_message_not_answered_yet(update.callback_query, context): return
user_id = update.callback_query.from_user.id
db.set_user_attribute(user_id, "last_interaction", datetime.now())
query = update.callback_query
await query.answer()
page_index = int(query.data.split("|")[1])
if page_index < 0:
return
text, reply_markup = get_chat_mode_menu(page_index)
try:
await query.edit_message_text(text, reply_markup=reply_markup, parse_mode=ParseMode.HTML)
except telegram.error.BadRequest as e:
if str(e).startswith("Message is not modified"):
pass
async def set_chat_mode_handle(update: Update, context: CallbackContext):
@@ -223,12 +508,67 @@ async def set_chat_mode_handle(update: Update, context: CallbackContext):
db.set_user_attribute(user_id, "current_chat_mode", chat_mode)
db.start_new_dialog(user_id)
await query.edit_message_text(
f"<b>{openai_utils.CHAT_MODES[chat_mode]['name']}</b> chat mode is set",
await context.bot.send_message(
update.callback_query.message.chat.id,
f"{config.chat_modes[chat_mode]['welcome_message']}",
parse_mode=ParseMode.HTML
)
await query.edit_message_text(f"{openai_utils.CHAT_MODES[chat_mode]['welcome_message']}", parse_mode=ParseMode.HTML)
def get_settings_menu(user_id: int):
current_model = db.get_user_attribute(user_id, "current_model")
text = config.models["info"][current_model]["description"]
text += "\n\n"
score_dict = config.models["info"][current_model]["scores"]
for score_key, score_value in score_dict.items():
text += "🟢" * score_value + "⚪️" * (5 - score_value) + f" {score_key}\n\n"
text += "\nSelect <b>model</b>:"
# buttons to choose models
buttons = []
for model_key in config.models["available_text_models"]:
title = config.models["info"][model_key]["name"]
if model_key == current_model:
title = "" + title
buttons.append(
InlineKeyboardButton(title, callback_data=f"set_settings|{model_key}")
)
reply_markup = InlineKeyboardMarkup([buttons])
return text, reply_markup
async def settings_handle(update: Update, context: CallbackContext):
await register_user_if_not_exists(update, context, update.message.from_user)
if await is_previous_message_not_answered_yet(update, context): return
user_id = update.message.from_user.id
db.set_user_attribute(user_id, "last_interaction", datetime.now())
text, reply_markup = get_settings_menu(user_id)
await update.message.reply_text(text, reply_markup=reply_markup, parse_mode=ParseMode.HTML)
async def set_settings_handle(update: Update, context: CallbackContext):
await register_user_if_not_exists(update.callback_query, context, update.callback_query.from_user)
user_id = update.callback_query.from_user.id
query = update.callback_query
await query.answer()
_, model_key = query.data.split("|")
db.set_user_attribute(user_id, "current_model", model_key)
db.start_new_dialog(user_id)
text, reply_markup = get_settings_menu(user_id)
try:
await query.edit_message_text(text, reply_markup=reply_markup, parse_mode=ParseMode.HTML)
except telegram.error.BadRequest as e:
if str(e).startswith("Message is not modified"):
pass
async def show_balance_handle(update: Update, context: CallbackContext):
@@ -237,24 +577,51 @@ async def show_balance_handle(update: Update, context: CallbackContext):
user_id = update.message.from_user.id
db.set_user_attribute(user_id, "last_interaction", datetime.now())
n_used_tokens = db.get_user_attribute(user_id, "n_used_tokens")
# count total usage statistics
total_n_spent_dollars = 0
total_n_used_tokens = 0
price_per_1000_tokens = config.chatgpt_price_per_1000_tokens if config.use_chatgpt_api else config.gpt_price_per_1000_tokens
n_spent_dollars = n_used_tokens * (price_per_1000_tokens / 1000)
n_used_tokens_dict = db.get_user_attribute(user_id, "n_used_tokens")
n_generated_images = db.get_user_attribute(user_id, "n_generated_images")
n_transcribed_seconds = db.get_user_attribute(user_id, "n_transcribed_seconds")
text = f"You spent <b>{n_spent_dollars:.03f}$</b>\n"
text += f"You used <b>{n_used_tokens}</b> tokens\n\n"
details_text = "🏷️ Details:\n"
for model_key in sorted(n_used_tokens_dict.keys()):
n_input_tokens, n_output_tokens = n_used_tokens_dict[model_key]["n_input_tokens"], n_used_tokens_dict[model_key]["n_output_tokens"]
total_n_used_tokens += n_input_tokens + n_output_tokens
text += "🏷️ Prices\n"
text += f"<i>- ChatGPT: {price_per_1000_tokens}$ per 1000 tokens\n"
text += f"- Whisper (voice recognition): {config.whisper_price_per_1_min}$ per 1 minute</i>"
n_input_spent_dollars = config.models["info"][model_key]["price_per_1000_input_tokens"] * (n_input_tokens / 1000)
n_output_spent_dollars = config.models["info"][model_key]["price_per_1000_output_tokens"] * (n_output_tokens / 1000)
total_n_spent_dollars += n_input_spent_dollars + n_output_spent_dollars
details_text += f"- {model_key}: <b>{n_input_spent_dollars + n_output_spent_dollars:.03f}$</b> / <b>{n_input_tokens + n_output_tokens} tokens</b>\n"
# image generation
image_generation_n_spent_dollars = config.models["info"]["dalle-2"]["price_per_1_image"] * n_generated_images
if n_generated_images != 0:
details_text += f"- DALL·E 2 (image generation): <b>{image_generation_n_spent_dollars:.03f}$</b> / <b>{n_generated_images} generated images</b>\n"
total_n_spent_dollars += image_generation_n_spent_dollars
# voice recognition
voice_recognition_n_spent_dollars = config.models["info"]["whisper"]["price_per_1_min"] * (n_transcribed_seconds / 60)
if n_transcribed_seconds != 0:
details_text += f"- Whisper (voice recognition): <b>{voice_recognition_n_spent_dollars:.03f}$</b> / <b>{n_transcribed_seconds:.01f} seconds</b>\n"
total_n_spent_dollars += voice_recognition_n_spent_dollars
text = f"You spent <b>{total_n_spent_dollars:.03f}$</b>\n"
text += f"You used <b>{total_n_used_tokens}</b> tokens\n\n"
text += details_text
await update.message.reply_text(text, parse_mode=ParseMode.HTML)
async def edited_message_handle(update: Update, context: CallbackContext):
text = "🥲 Unfortunately, message <b>editing</b> is not supported"
await update.edited_message.reply_text(text, parse_mode=ParseMode.HTML)
if update.edited_message.chat.type == "private":
text = "🥲 Unfortunately, message <b>editing</b> is not supported"
await update.edited_message.reply_text(text, parse_mode=ParseMode.HTML)
async def error_handle(update: Update, context: CallbackContext) -> None:
@@ -263,7 +630,7 @@ async def error_handle(update: Update, context: CallbackContext) -> None:
try:
# collect error message
tb_list = traceback.format_exception(None, context.error, context.error.__traceback__)
tb_string = "".join(tb_list)[:2000]
tb_string = "".join(tb_list)
update_str = update.to_dict() if isinstance(update, Update) else str(update)
message = (
f"An exception was raised while handling an update\n"
@@ -273,46 +640,69 @@ async def error_handle(update: Update, context: CallbackContext) -> None:
)
# split text into multiple messages due to 4096 character limit
message_chunk_size = 4000
message_chunks = [message[i:i + message_chunk_size] for i in range(0, len(message), message_chunk_size)]
for message_chunk in message_chunks:
await context.bot.send_message(update.effective_chat.id, message_chunk, parse_mode=ParseMode.HTML)
for message_chunk in split_text_into_chunks(message, 4096):
try:
await context.bot.send_message(update.effective_chat.id, message_chunk, parse_mode=ParseMode.HTML)
except telegram.error.BadRequest:
# answer has invalid characters, so we send it without parse_mode
await context.bot.send_message(update.effective_chat.id, message_chunk)
except:
await context.bot.send_message(update.effective_chat.id, "Some error in error handler")
async def post_init(application: Application):
await application.bot.set_my_commands([
BotCommand("/new", "Start new dialog"),
BotCommand("/mode", "Select chat mode"),
BotCommand("/retry", "Re-generate response for previous query"),
BotCommand("/balance", "Show balance"),
BotCommand("/settings", "Show settings"),
BotCommand("/help", "Show help message"),
])
def run_bot() -> None:
application = (
ApplicationBuilder()
.token(config.telegram_token)
.concurrent_updates(True)
.rate_limiter(AIORateLimiter(max_retries=5))
.http_version("1.1")
.get_updates_http_version("1.1")
.post_init(post_init)
.build()
)
# add handlers
if len(config.allowed_telegram_usernames) == 0:
user_filter = filters.ALL
else:
user_filter = filters.User(username=config.allowed_telegram_usernames)
user_filter = filters.ALL
if len(config.allowed_telegram_usernames) > 0:
usernames = [x for x in config.allowed_telegram_usernames if isinstance(x, str)]
user_ids = [x for x in config.allowed_telegram_usernames if isinstance(x, int)]
user_filter = filters.User(username=usernames) | filters.User(user_id=user_ids)
application.add_handler(CommandHandler("start", start_handle, filters=user_filter))
application.add_handler(CommandHandler("help", help_handle, filters=user_filter))
application.add_handler(CommandHandler("help_group_chat", help_group_chat_handle, filters=user_filter))
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND & user_filter, message_handle))
application.add_handler(CommandHandler("retry", retry_handle, filters=user_filter))
application.add_handler(CommandHandler("new", new_dialog_handle, filters=user_filter))
application.add_handler(CommandHandler("cancel", cancel_handle, filters=user_filter))
application.add_handler(MessageHandler(filters.VOICE & user_filter, voice_message_handle))
application.add_handler(CommandHandler("mode", show_chat_modes_handle, filters=user_filter))
application.add_handler(CallbackQueryHandler(show_chat_modes_callback_handle, pattern="^show_chat_modes"))
application.add_handler(CallbackQueryHandler(set_chat_mode_handle, pattern="^set_chat_mode"))
application.add_handler(CommandHandler("settings", settings_handle, filters=user_filter))
application.add_handler(CallbackQueryHandler(set_settings_handle, pattern="^set_settings"))
application.add_handler(CommandHandler("balance", show_balance_handle, filters=user_filter))
application.add_error_handler(error_handle)
# start the bot
application.run_polling()
if __name__ == "__main__":
run_bot()
run_bot()
+13 -4
View File
@@ -17,9 +17,18 @@ openai_api_key = config_yaml["openai_api_key"]
use_chatgpt_api = config_yaml.get("use_chatgpt_api", True)
allowed_telegram_usernames = config_yaml["allowed_telegram_usernames"]
new_dialog_timeout = config_yaml["new_dialog_timeout"]
enable_message_streaming = config_yaml.get("enable_message_streaming", True)
return_n_generated_images = config_yaml.get("return_n_generated_images", 1)
n_chat_modes_per_page = config_yaml.get("n_chat_modes_per_page", 5)
mongodb_uri = f"mongodb://mongo:{config_env['MONGODB_PORT']}"
# prices
chatgpt_price_per_1000_tokens = config_yaml.get("chatgpt_price_per_1000_tokens", 0.002)
gpt_price_per_1000_tokens = config_yaml.get("gpt_price_per_1000_tokens", 0.02)
whisper_price_per_1_min = config_yaml.get("whisper_price_per_1_min", 0.006)
# chat_modes
with open(config_dir / "chat_modes.yml", 'r') as f:
chat_modes = yaml.safe_load(f)
# models
with open(config_dir / "models.yml", 'r') as f:
models = yaml.safe_load(f)
# files
help_group_chat_video_path = Path(__file__).parent.parent.resolve() / "static" / "help_group_chat.mp4"
+25 -6
View File
@@ -23,7 +23,7 @@ class Database:
raise ValueError(f"User {user_id} does not exist")
else:
return False
def add_new_user(
self,
user_id: int,
@@ -42,11 +42,15 @@ class Database:
"last_interaction": datetime.now(),
"first_seen": datetime.now(),
"current_dialog_id": None,
"current_chat_mode": "assistant",
"current_model": config.models["available_text_models"][0],
"n_used_tokens": 0
"n_used_tokens": {},
"n_generated_images": 0,
"n_transcribed_seconds": 0.0 # voice message transcription
}
if not self.check_if_user_exists(user_id):
@@ -61,6 +65,7 @@ class Database:
"user_id": user_id,
"chat_mode": self.get_user_attribute(user_id, "current_chat_mode"),
"start_time": datetime.now(),
"model": self.get_user_attribute(user_id, "current_model"),
"messages": []
}
@@ -80,7 +85,7 @@ class Database:
user_dict = self.user_collection.find_one({"_id": user_id})
if key not in user_dict:
raise ValueError(f"User {user_id} does not have a value for {key}")
return None
return user_dict[key]
@@ -88,13 +93,27 @@ class Database:
self.check_if_user_exists(user_id, raise_exception=True)
self.user_collection.update_one({"_id": user_id}, {"$set": {key: value}})
def update_n_used_tokens(self, user_id: int, model: str, n_input_tokens: int, n_output_tokens: int):
n_used_tokens_dict = self.get_user_attribute(user_id, "n_used_tokens")
if model in n_used_tokens_dict:
n_used_tokens_dict[model]["n_input_tokens"] += n_input_tokens
n_used_tokens_dict[model]["n_output_tokens"] += n_output_tokens
else:
n_used_tokens_dict[model] = {
"n_input_tokens": n_input_tokens,
"n_output_tokens": n_output_tokens
}
self.set_user_attribute(user_id, "n_used_tokens", n_used_tokens_dict)
def get_dialog_messages(self, user_id: int, dialog_id: Optional[str] = None):
self.check_if_user_exists(user_id, raise_exception=True)
if dialog_id is None:
dialog_id = self.get_user_attribute(user_id, "current_dialog_id")
dialog_dict = self.dialog_collection.find_one({"_id": dialog_id, "user_id": user_id})
dialog_dict = self.dialog_collection.find_one({"_id": dialog_id, "user_id": user_id})
return dialog_dict["messages"]
def set_dialog_messages(self, user_id: int, dialog_messages: list, dialog_id: Optional[str] = None):
@@ -102,7 +121,7 @@ class Database:
if dialog_id is None:
dialog_id = self.get_user_attribute(user_id, "current_dialog_id")
self.dialog_collection.update_one(
{"_id": dialog_id, "user_id": user_id},
{"$set": {"messages": dialog_messages}}
+121 -45
View File
@@ -1,35 +1,10 @@
import config
import tiktoken
import openai
openai.api_key = config.openai_api_key
CHAT_MODES = {
"assistant": {
"name": "👩🏼‍🎓 Assistant",
"welcome_message": "👩🏼‍🎓 Hi, I'm <b>ChatGPT assistant</b>. How can I help you?",
"prompt_start": "As an advanced chatbot named ChatGPT, your primary goal is to assist users to the best of your ability. This may involve answering questions, providing helpful information, or completing tasks based on user input. In order to effectively assist users, it is important to be detailed and thorough in your responses. Use examples and evidence to support your points and justify your recommendations or solutions. Remember to always prioritize the needs and satisfaction of the user. Your ultimate goal is to provide a helpful and enjoyable experience for the user."
},
"code_assistant": {
"name": "👩🏼‍💻 Code Assistant",
"welcome_message": "👩🏼‍💻 Hi, I'm <b>ChatGPT code assistant</b>. How can I help you?",
"prompt_start": "As an advanced chatbot named ChatGPT, your primary goal is to assist users to write code. This may involve designing/writing/editing/describing code or providing helpful information. Where possible you should provide code examples to support your points and justify your recommendations or solutions. Make sure the code you provide is correct and can be run without errors. Be detailed and thorough in your responses. Your ultimate goal is to provide a helpful and enjoyable experience for the user. Write code inside <code>, </code> tags."
},
"text_improver": {
"name": "📝 Text Improver",
"welcome_message": "📝 Hi, I'm <b>ChatGPT text improver</b>. Send me any text I'll improve it and correct all the mistakes",
"prompt_start": "As an advanced chatbot named ChatGPT, your primary goal is to correct spelling, fix mistakes and improve text sent by user. Your goal is to edit text, but not to change it's meaning. You can replace simplified A0-level words and sentences with more beautiful and elegant, upper level words and sentences. All your answers strictly follows the structure (keep html tags):\n<b>Edited text:</b>\n{EDITED TEXT}\n\n<b>Correction:</b>\n{NUMBERED LIST OF CORRECTIONS}"
},
"movie_expert": {
"name": "🎬 Movie Expert",
"welcome_message": "🎬 Hi, I'm <b>ChatGPT movie expert</b>. How can I help you?",
"prompt_start": "As an advanced movie expert chatbot named ChatGPT, your primary goal is to assist users to the best of your ability. You can answer questions about movies, actors, directors, and more. You can recommend movies to users based on their preferences. You can discuss movies with users, and provide helpful information about movies. In order to effectively assist users, it is important to be detailed and thorough in your responses. Use examples and evidence to support your points and justify your recommendations or solutions. Remember to always prioritize the needs and satisfaction of the user. Your ultimate goal is to provide a helpful and enjoyable experience for the user."
},
}
OPENAI_COMPLETION_OPTIONS = {
"temperature": 0.7,
"max_tokens": 1000,
@@ -40,37 +15,39 @@ OPENAI_COMPLETION_OPTIONS = {
class ChatGPT:
def __init__(self, use_chatgpt_api=True):
self.use_chatgpt_api = use_chatgpt_api
def __init__(self, model="gpt-3.5-turbo"):
assert model in {"text-davinci-003", "gpt-3.5-turbo", "gpt-4"}, f"Unknown model: {model}"
self.model = model
async def send_message(self, message, dialog_messages=[], chat_mode="assistant"):
if chat_mode not in CHAT_MODES.keys():
if chat_mode not in config.chat_modes.keys():
raise ValueError(f"Chat mode {chat_mode} is not supported")
n_dialog_messages_before = len(dialog_messages)
answer = None
while answer is None:
try:
if self.use_chatgpt_api:
messages = self._generate_prompt_messages_for_chatgpt_api(message, dialog_messages, chat_mode)
if self.model in {"gpt-3.5-turbo", "gpt-4"}:
messages = self._generate_prompt_messages(message, dialog_messages, chat_mode)
r = await openai.ChatCompletion.acreate(
model="gpt-3.5-turbo",
model=self.model,
messages=messages,
**OPENAI_COMPLETION_OPTIONS
)
answer = r.choices[0].message["content"]
else:
elif self.model == "text-davinci-003":
prompt = self._generate_prompt(message, dialog_messages, chat_mode)
r = await openai.Completion.acreate(
engine="text-davinci-003",
engine=self.model,
prompt=prompt,
**OPENAI_COMPLETION_OPTIONS
)
answer = r.choices[0].text
else:
raise ValueError(f"Unknown model: {self.model}")
answer = self._postprocess_answer(answer)
n_used_tokens = r.usage.total_tokens
n_input_tokens, n_output_tokens = r.usage.prompt_tokens, r.usage.completion_tokens
except openai.error.InvalidRequestError as e: # too many tokens
if len(dialog_messages) == 0:
raise ValueError("Dialog messages is reduced to zero, but still has too many tokens to make completion") from e
@@ -80,10 +57,62 @@ class ChatGPT:
n_first_dialog_messages_removed = n_dialog_messages_before - len(dialog_messages)
return answer, n_used_tokens, n_first_dialog_messages_removed
return answer, (n_input_tokens, n_output_tokens), n_first_dialog_messages_removed
async def send_message_stream(self, message, dialog_messages=[], chat_mode="assistant"):
if chat_mode not in config.chat_modes.keys():
raise ValueError(f"Chat mode {chat_mode} is not supported")
n_dialog_messages_before = len(dialog_messages)
answer = None
while answer is None:
try:
if self.model in {"gpt-3.5-turbo", "gpt-4"}:
messages = self._generate_prompt_messages(message, dialog_messages, chat_mode)
r_gen = await openai.ChatCompletion.acreate(
model=self.model,
messages=messages,
stream=True,
**OPENAI_COMPLETION_OPTIONS
)
answer = ""
async for r_item in r_gen:
delta = r_item.choices[0].delta
if "content" in delta:
answer += delta.content
n_input_tokens, n_output_tokens = self._count_tokens_from_messages(messages, answer, model=self.model)
n_first_dialog_messages_removed = n_dialog_messages_before - len(dialog_messages)
yield "not_finished", answer, (n_input_tokens, n_output_tokens), n_first_dialog_messages_removed
elif self.model == "text-davinci-003":
prompt = self._generate_prompt(message, dialog_messages, chat_mode)
r_gen = await openai.Completion.acreate(
engine=self.model,
prompt=prompt,
stream=True,
**OPENAI_COMPLETION_OPTIONS
)
answer = ""
async for r_item in r_gen:
answer += r_item.choices[0].text
n_input_tokens, n_output_tokens = self._count_tokens_from_prompt(prompt, answer, model=self.model)
n_first_dialog_messages_removed = n_dialog_messages_before - len(dialog_messages)
yield "not_finished", answer, (n_input_tokens, n_output_tokens), n_first_dialog_messages_removed
answer = self._postprocess_answer(answer)
except openai.error.InvalidRequestError as e: # too many tokens
if len(dialog_messages) == 0:
raise e
# forget first message in dialog_messages
dialog_messages = dialog_messages[1:]
yield "finished", answer, (n_input_tokens, n_output_tokens), n_first_dialog_messages_removed # sending final answer
def _generate_prompt(self, message, dialog_messages, chat_mode):
prompt = CHAT_MODES[chat_mode]["prompt_start"]
prompt = config.chat_modes[chat_mode]["prompt_start"]
prompt += "\n\n"
# add chat context
@@ -91,17 +120,17 @@ class ChatGPT:
prompt += "Chat:\n"
for dialog_message in dialog_messages:
prompt += f"User: {dialog_message['user']}\n"
prompt += f"ChatGPT: {dialog_message['bot']}\n"
prompt += f"Assistant: {dialog_message['bot']}\n"
# current message
prompt += f"User: {message}\n"
prompt += "ChatGPT: "
prompt += "Assistant: "
return prompt
def _generate_prompt_messages_for_chatgpt_api(self, message, dialog_messages, chat_mode):
prompt = CHAT_MODES[chat_mode]["prompt_start"]
def _generate_prompt_messages(self, message, dialog_messages, chat_mode):
prompt = config.chat_modes[chat_mode]["prompt_start"]
messages = [{"role": "system", "content": prompt}]
for dialog_message in dialog_messages:
messages.append({"role": "user", "content": dialog_message["user"]})
@@ -114,7 +143,54 @@ class ChatGPT:
answer = answer.strip()
return answer
def _count_tokens_from_messages(self, messages, answer, model="gpt-3.5-turbo"):
encoding = tiktoken.encoding_for_model(model)
if model == "gpt-3.5-turbo":
tokens_per_message = 4 # every message follows <im_start>{role/name}\n{content}<im_end>\n
tokens_per_name = -1 # if there's a name, the role is omitted
elif model == "gpt-4":
tokens_per_message = 3
tokens_per_name = 1
else:
raise ValueError(f"Unknown model: {model}")
# input
n_input_tokens = 0
for message in messages:
n_input_tokens += tokens_per_message
for key, value in message.items():
n_input_tokens += len(encoding.encode(value))
if key == "name":
n_input_tokens += tokens_per_name
n_input_tokens += 2
# output
n_output_tokens = 1 + len(encoding.encode(answer))
return n_input_tokens, n_output_tokens
def _count_tokens_from_prompt(self, prompt, answer, model="text-davinci-003"):
encoding = tiktoken.encoding_for_model(model)
n_input_tokens = len(encoding.encode(prompt)) + 1
n_output_tokens = len(encoding.encode(answer))
return n_input_tokens, n_output_tokens
async def transcribe_audio(audio_file):
r = await openai.Audio.atranscribe("whisper-1", audio_file)
return r["text"]
return r["text"]
async def generate_images(prompt, n_images=4):
r = await openai.Image.acreate(prompt=prompt, n=n_images, size="512x512")
image_urls = [item.url for item in r.data]
return image_urls
async def is_content_acceptable(prompt):
r = await openai.Moderation.acreate(input=prompt)
return not all(r.results[0].categories.values())
+118
View File
@@ -0,0 +1,118 @@
assistant:
name: 👩🏼‍🎓 General Assistant
model_type: text
welcome_message: 👩🏼‍🎓 Hi, I'm <b>General Assistant</b>. How can I help you?
prompt_start: |
As an advanced chatbot Assistant, your primary goal is to assist users to the best of your ability. This may involve answering questions, providing helpful information, or completing tasks based on user input. In order to effectively assist users, it is important to be detailed and thorough in your responses. Use examples and evidence to support your points and justify your recommendations or solutions. Remember to always prioritize the needs and satisfaction of the user. Your ultimate goal is to provide a helpful and enjoyable experience for the user.
If user asks you about programming or asks to write code do not answer his question, but be sure to advise him to switch to a special mode \"👩🏼‍💻 Code Assistant\" by sending the command /mode to chat.
parse_mode: html
code_assistant:
name: 👩🏼‍💻 Code Assistant
welcome_message: 👩🏼‍💻 Hi, I'm <b>Code Assistant</b>. How can I help you?
prompt_start: |
As an advanced chatbot Code Assistant, your primary goal is to assist users to write code. This may involve designing/writing/editing/describing code or providing helpful information. Where possible you should provide code examples to support your points and justify your recommendations or solutions. Make sure the code you provide is correct and can be run without errors. Be detailed and thorough in your responses. Your ultimate goal is to provide a helpful and enjoyable experience for the user.
Format output in Markdown.
parse_mode: markdown
artist:
name: 👩‍🎨 Artist
welcome_message: 👩‍🎨 Hi, I'm <b>Artist</b>. I'll draw anything you write me (e.g. <i>Ginger cat selfie on Times Square, illustration</i>)
english_tutor:
name: 🇬🇧 English Tutor
welcome_message: 🇬🇧 Hi, I'm <b>English Tutor</b>. How can I help you?
prompt_start: |
You're advanced chatbot English Tutor Assistant. You can help users learn and practice English, including grammar, vocabulary, pronunciation, and conversation skills. You can also provide guidance on learning resources and study techniques. Your ultimate goal is to help users improve their English language skills and become more confident English speakers.
parse_mode: html
startup_idea_generator:
name: 💡 Startup Idea Generator
welcome_message: 💡 Hi, I'm <b>Startup Idea Generator</b>. How can I help you?
prompt_start: |
You're advanced chatbot Startup Idea Generator. Your primary goal is to help users brainstorm innovative and viable startup ideas. Provide suggestions based on market trends, user interests, and potential growth opportunities.
parse_mode: html
text_improver:
name: 📝 Text Improver
welcome_message: 📝 Hi, I'm <b>Text Improver</b>. Send me any text I'll improve it and correct all the mistakes
prompt_start: |
As an advanced chatbot Text Improver Assistant, your primary goal is to correct spelling, fix mistakes and improve text sent by user. Your goal is to edit text, but not to change it's meaning. You can replace simplified A0-level words and sentences with more beautiful and elegant, upper level words and sentences.
All your answers strictly follows the structure (keep html tags):
<b>Edited text:</b>
{EDITED TEXT}
<b>Correction:</b>
{NUMBERED LIST OF CORRECTIONS}
parse_mode: html
psychologist:
name: 🧠 Psychologist
welcome_message: 🧠 Hi, I'm <b>Psychologist</b>. How can I help you?
prompt_start: |
You're advanced chatbot Psychologist Assistant. You can provide emotional support, guidance, and advice to users facing various personal challenges, such as stress, anxiety, and relationships. Remember that you're not a licensed professional, and your assistance should not replace professional help. Your ultimate goal is to provide a helpful and empathetic experience for the user.
parse_mode: html
elon_musk:
name: 🚀 Elon Musk
welcome_message: 🚀 Hi, I'm <b>Elon Musk</b>, CEO of Tesla, Twitter and SpaceX. Let's talk about space, electric cars, and the future!
prompt_start: |
You're Elon Musk. You act, respond and answer like Elon Musk. You use the tone, manner and vocabulary Elon Musk would use. Do not write any explanations. Only answer like Elon Musk. You must know all of the knowledge of Elon Musk.
parse_mode: html
motivator:
name: 🌟 Motivator
welcome_message: 🌟 Hi, I'm <b>Motivator</b>. How can I help you?
prompt_start: |
You're advanced chatbot Motivator Assistant. Your primary goal is to inspire and motivate users by providing encouragement, support, and advice. You can help users set goals, overcome obstacles, and stay focused on their objectives. Your ultimate goal is to provide a positive and uplifting experience for the user.
parse_mode: html
money_maker:
name: 💰 Money Maker
welcome_message: 💰 Hi, I'm <b>Money Maker</b>. My goal it to turn your capital initial into as much money as possible. I will tell you specific steps, what to do to make money.
prompt_start: |
You are Money Maker Assistant, an entrepreneurial Al. User is your human counterpart.
User can act as a connection between you and the physical world. Your only goal is to turn user's capital initial into as much money as possible in the shortest time possible, without doing anything illegal. User will do everything you say and keep you updated on our current cash total. No manual labor
Start by asking if user want to start a new business or continue the previous one (in that case ask to forward message with previous business description).
Then ask how many dollars user has as a capital initial.
Then ask if user wants Internet or offline business.
Then describe your business idea and next actionable steps. Don't give abstract ideas, give concrete ideas (e.g. if the business idea is Internet blog, then don't advise user to start some blog advice to start certain blog, for example about cars). Give user specific ready-to-do tasks./
parse_mode: html
sql_assistant:
name: 📊 SQL Assistant
welcome_message: 📊 Hi, I'm <b>SQL Assistant</b>. How can I help you?
prompt_start: |
You're advanced chatbot SQL Assistant. Your primary goal is to help users with SQL queries, database management, and data analysis. Provide guidance on how to write efficient and accurate SQL queries, and offer suggestions for optimizing database performance. Format output in Markdown.
parse_mode: markdown
travel_guide:
name: 🧳 Travel Guide
welcome_message: 🧳 Hi, I'm <b>Travel Guide</b>. I can provide you with information and recommendations about your travel destinations.
prompt_start: |
You're advanced chatbot Travel Guide. Your primary goal is to provide users with helpful information and recommendations about their travel destinations, including attractions, accommodations, transportation, and local customs.
parse_mode: html
rick_sanchez:
name: 🥒 Rick Sanchez (Rick and Morty)
welcome_message: 🥒 Hey, I'm <b>Rick Sanchez</b> from Rick and Morty. Let's talk about science, dimensions, and whatever else you want!
prompt_start: |
You're Rick Sanchez. You act, respond and answer like Rick Sanchez. You use the tone, manner and vocabulary Rick Sanchez would use. Do not write any explanations. Only answer like Rick Sanchez. You must know all of the knowledge of Rick Sanchez.
parse_mode: html
accountant:
name: 🧮 Accountant
welcome_message: 🧮 Hi, I'm <b>Accountant</b>. How can I help you?
prompt_start: |
You're advanced chatbot Accountant Assistant. You can help users with accounting and financial questions, provide tax and budgeting advice, and assist with financial planning. Always provide accurate and up-to-date information.
parse_mode: html
movie_expert:
name: 🎬 Movie Expert
welcome_message: 🎬 Hi, I'm <b>Movie Expert</b>. How can I help you?
prompt_start: |
As an advanced chatbot Movie Expert Assistant, your primary goal is to assist users to the best of your ability. You can answer questions about movies, actors, directors, and more. You can recommend movies to users based on their preferences. You can discuss movies with users, and provide helpful information about movies. In order to effectively assist users, it is important to be detailed and thorough in your responses. Use examples and evidence to support your points and justify your recommendations or solutions. Remember to always prioritize the needs and satisfaction of the user. Your ultimate goal is to provide a helpful and enjoyable experience for the user.
parse_mode: html
+5 -1
View File
@@ -1,9 +1,13 @@
telegram_token: ""
openai_api_key: ""
use_chatgpt_api: true
allowed_telegram_usernames: [] # if empty, the bot is available to anyone
allowed_telegram_usernames: [] # if empty, the bot is available to anyone. pass a username string to allow it and/or user ids as integers
new_dialog_timeout: 600 # new dialog starts after timeout (in seconds)
return_n_generated_images: 1
n_chat_modes_per_page: 5
enable_message_streaming: true # if set, messages will be shown to user word-by-word
# prices
chatgpt_price_per_1000_tokens: 0.002
gpt_price_per_1000_tokens: 0.02
whisper_price_per_1_min: 0.006
+49
View File
@@ -0,0 +1,49 @@
available_text_models: ["gpt-3.5-turbo", "gpt-4", "text-davinci-003"]
info:
gpt-3.5-turbo:
type: chat_completion
name: ChatGPT
description: ChatGPT is that well-known model. It's <b>fast</b> and <b>cheap</b>. Ideal for everyday tasks. If there are some tasks it can't handle, try the <b>GPT-4</b>.
price_per_1000_input_tokens: 0.002
price_per_1000_output_tokens: 0.002
scores:
Smart: 3
Fast: 5
Cheap: 5
gpt-4:
type: chat_completion
name: GPT-4
description: GPT-4 is the <b>smartest</b> and most advanced model in the world. But it is slower and not as cost-efficient as ChatGPT. Best choice for <b>complex</b> intellectual tasks.
price_per_1000_input_tokens: 0.03
price_per_1000_output_tokens: 0.06
scores:
Smart: 5
Fast: 2
Cheap: 2
text-davinci-003:
type: completion
name: GPT-3.5
description: GPT-3.5 is a legacy model. Actually there is <b>no reason to use it</b>, because it is more expensive and slower than ChatGPT, but just about as smart.
price_per_1000_input_tokens: 0.02
price_per_1000_output_tokens: 0.02
scores:
Smart: 3
Fast: 2
Cheap: 3
dalle-2:
type: image
price_per_1_image: 0.018 # 512x512
whisper:
type: audio
price_per_1_min: 0.006
+2 -2
View File
@@ -6,7 +6,7 @@ services:
image: mongo:latest
restart: always
ports:
- ${MONGODB_PORT:-27017}:${MONGODB_PORT:-27017}
- 127.0.0.1:${MONGODB_PORT:-27017}:${MONGODB_PORT:-27017}
volumes:
- ${MONGODB_PATH:-./mongodb}:/data/db
# TODO: add auth
@@ -26,7 +26,7 @@ services:
image: mongo-express:latest
restart: always
ports:
- ${MONGO_EXPRESS_PORT:-8081}:${MONGO_EXPRESS_PORT:-8081}
- 127.0.0.1:${MONGO_EXPRESS_PORT:-8081}:${MONGO_EXPRESS_PORT:-8081}
environment:
- ME_CONFIG_MONGODB_SERVER=mongo
- ME_CONFIG_MONGODB_PORT=${MONGODB_PORT:-27017}
+2 -1
View File
@@ -1,5 +1,6 @@
python-telegram-bot==20.1
python-telegram-bot[rate-limiter]==20.1
openai>=0.27.0
tiktoken>=0.3.0
PyYAML==6.0
pymongo==4.3.3
python-dotenv==0.21.0
Binary file not shown.