Update dify.py

refactor module, add stream message support, implement dynamic design without preset API

- Refactored Dify module architecture
- Added support for streaming messages
- Implemented new dynamic design removing need for preset API
This commit is contained in:
Alter-xyz 2024-09-13 06:49:59 -04:00
parent a1006f8dff
commit 309d702c08

View File

@ -1,61 +1,41 @@
from os import environ import json
import time
import re
from telebot import TeleBot from telebot import TeleBot
from telebot.types import Message from telebot.types import Message
from expiringdict import ExpiringDict
from . import * from . import *
# TODO: update requirements.txt and setup tools # TODO: update requirements.txt and setup tools
# pip install dify-client # pip install dify-client
from dify_client import ChatClient from dify_client import ChatClient
from telegramify_markdown import convert
from telegramify_markdown.customize import markdown_symbol from telegramify_markdown.customize import markdown_symbol
# If you want, Customizing the head level 1 symbol # If you want, Customizing the head level 1 symbol
markdown_symbol.head_level_1 = "📌" markdown_symbol.head_level_1 = "📌"
markdown_symbol.link = "🔗" # If you want, Customizing the link symbol markdown_symbol.link = "🔗" # If you want, Customizing the link symbol
DIFY_API_KEY = environ.get("DIFY_API_KEY")
if DIFY_API_KEY:
client = ChatClient(api_key=DIFY_API_KEY)
# Global history cache
dify_player_dict = ExpiringDict(max_len=1000, max_age_seconds=600)
dify_player_c = ExpiringDict(
max_len=1000, max_age_seconds=600
) # History cache is supported by dify cloud conversation_id.
def dify_handler(message: Message, bot: TeleBot) -> None: def dify_handler(message: Message, bot: TeleBot) -> None:
"""dify : /dify <question>""" """dify : /dify API_Key <question>"""
m = message.text.strip() m = message.text.strip()
c = None
player_message = []
# restart will lose all TODO
if str(message.from_user.id) not in dify_player_dict:
dify_player_dict[str(message.from_user.id)] = (
player_message # for the imuutable list
)
else:
player_message = dify_player_dict[str(message.from_user.id)]
# get c from dify_player_c
c = dify_player_c.get(str(message.from_user.id), None)
if m.strip() == "clear": if re.match(r"^app-\w+$", m, re.IGNORECASE):
bot.reply_to( bot.reply_to(
message, message,
"just clear your dify messages history", "Thanks!\nFor conversation, please make a space between your API_Key and your question.",
) )
player_message.clear()
c = None
return return
if re.match(r"^app-[a-zA-Z0-9]+ .*$", m, re.IGNORECASE):
if m[:4].lower() == "new ": Dify_API_KEY = m.split(" ", 1)[0]
m = m[4:].strip() m = m.split(" ", 1)[1]
player_message.clear() else:
c = None bot.reply_to(message, "Please provide a valid API key.")
return
client = ChatClient(api_key=Dify_API_KEY)
# Init client with API key
m = enrich_text_with_urls(m) m = enrich_text_with_urls(m)
@ -63,49 +43,45 @@ def dify_handler(message: Message, bot: TeleBot) -> None:
# show something, make it more responsible # show something, make it more responsible
reply_id = bot_reply_first(message, who, bot) reply_id = bot_reply_first(message, who, bot)
player_message.append({"role": "user", "content": m})
# keep the last 5, every has two ask and answer.
if len(player_message) > 10:
player_message = player_message[2:]
dify_reply_text = ""
try: try:
r = client.create_chat_message( r = client.create_chat_message(
inputs={}, inputs={},
query=m, query=m,
user=str(message.from_user.id), user=str(message.from_user.id),
response_mode="blocking", response_mode="streaming",
conversation_id=c,
) )
j = r.json() s = ""
start = time.time()
content = j.get("answer", None) overall_start = time.time()
# get c by j.get then save c to dify_player_c for chunk in r.iter_lines(decode_unicode=True):
dify_player_c[str(message.from_user.id)] = j.get("conversation_id", None) chunk = chunk.split("data:", 1)[-1]
if not content: if chunk.strip():
dify_reply_text = f"{who} did not answer." chunk = json.loads(chunk.strip())
player_message.pop() answer_chunk = chunk.get("answer", "")
else: s += answer_chunk
dify_reply_text = content if time.time() - start > 1.5:
player_message.append( start = time.time()
{ bot_reply_markdown(reply_id, who, s, bot, split_text=False)
"role": "assistant", if time.time() - overall_start > 120: # Timeout
"content": dify_reply_text, s += "\n\nTimeout"
} break
) # maybe not complete
try:
bot_reply_markdown(reply_id, who, s, bot)
except:
pass
except Exception as e: except Exception as e:
print(e) print(e)
bot.reply_to(message, "answer wrong maybe up to the max token") bot.reply_to(message, "answer wrong maybe up to the max token")
# pop my user # pop my user
player_message.pop()
return return
# reply back as Markdown and fallback to plain text if failed. # reply back as Markdown and fallback to plain text if failed.
bot_reply_markdown(reply_id, who, dify_reply_text, bot) bot_reply_markdown(reply_id, who, s, bot)
if DIFY_API_KEY: if True:
def register(bot: TeleBot) -> None: def register(bot: TeleBot) -> None:
bot.register_message_handler(dify_handler, commands=["dify"], pass_bot=True) bot.register_message_handler(dify_handler, commands=["dify"], pass_bot=True)