Skip to content

Commit

Permalink
Add function call feature, implement autonomous judgment on whether t…
Browse files Browse the repository at this point in the history
…o search using function call, restructure code directory, add search command to enforce search.
  • Loading branch information
yym68686 committed Nov 8, 2023
1 parent ab63bbc commit a6168de
Show file tree
Hide file tree
Showing 16 changed files with 194 additions and 51 deletions.
File renamed without changes.
2 changes: 1 addition & 1 deletion .github/fly_deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ jobs:
uses: actions/checkout@v2
- name: Deploy
run: |
sh ./deploy.sh
sh ./.github/deploy.sh
88 changes: 72 additions & 16 deletions bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
import config
import logging
import traceback
import decorators
from md2tgmd import escape
from runasync import run_async
import utils.decorators as decorators
from utils.md2tgmd import escape
from utils.runasync import run_async
from chatgpt2api.chatgpt2api import Chatbot as GPT
from telegram.constants import ChatAction
from agent import docQA, get_doc_from_local
from utils.agent import docQA, get_doc_from_local
from telegram import BotCommand, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import CommandHandler, MessageHandler, ApplicationBuilder, filters, CallbackQueryHandler


logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
# logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
logger = logging.getLogger()

# 获取 httpx 的 logger
Expand Down Expand Up @@ -97,12 +98,65 @@ async def getChatGPT(update, context, title, robot, message, use_search=config.S
reply_to_message_id=update.message.message_id,
)
messageid = message.message_id
if use_search and not has_command:
get_answer = robot.search_summary
else:
get_answer = robot.ask_stream
get_answer = robot.ask_stream
# if use_search and not has_command:
# get_answer = robot.search_summary
# else:
# get_answer = robot.ask_stream
if not config.API or (config.USE_G4F and not config.SEARCH_USE_GPT):
import utils.gpt4free as gpt4free
get_answer = gpt4free.get_response

try:
for data in get_answer(text, convo_id=str(update.message.chat_id), pass_history=config.PASS_HISTORY):
result = result + data
tmpresult = result
modifytime = modifytime + 1
if re.sub(r"```", '', result).count("`") % 2 != 0:
tmpresult = result + "`"
if result.count("```") % 2 != 0:
tmpresult = result + "\n```"
if modifytime % 20 == 0 and lastresult != tmpresult:
if 'claude2' in title:
tmpresult = re.sub(r",", ',', tmpresult)
await context.bot.edit_message_text(chat_id=update.message.chat_id, message_id=messageid, text=escape(tmpresult), parse_mode='MarkdownV2', disable_web_page_preview=True)
lastresult = tmpresult
except Exception as e:
print('\033[31m')
print("response_msg", result)
print("error", e)
traceback.print_exc()
print('\033[0m')
if config.API:
robot.reset(convo_id=str(update.message.chat_id), system_prompt=config.systemprompt)
if "You exceeded your current quota, please check your plan and billing details." in str(e):
print("OpenAI api 已过期!")
await context.bot.delete_message(chat_id=update.message.chat_id, message_id=messageid)
messageid = ''
config.API = ''
result += f"`出错啦!{e}`"
print(result)
if lastresult != result and messageid:
if 'claude2' in title:
result = re.sub(r",", ',', result)
await context.bot.edit_message_text(chat_id=update.message.chat_id, message_id=messageid, text=escape(result), parse_mode='MarkdownV2', disable_web_page_preview=True)

async def search(update, context, title, robot):
message = update.message.text if config.NICK is None else update.message.text[botNicKLength:].strip() if update.message.text[:botNicKLength].lower() == botNick else None
result = title
text = message
modifytime = 0
lastresult = ''
message = await context.bot.send_message(
chat_id=update.message.chat_id,
text="搜索中💭",
parse_mode='MarkdownV2',
reply_to_message_id=update.message.message_id,
)
messageid = message.message_id
get_answer = robot.search_summary
if not config.API or (config.USE_G4F and not config.SEARCH_USE_GPT):
import gpt4free
import utils.gpt4free as gpt4free
get_answer = gpt4free.get_response

try:
Expand Down Expand Up @@ -164,16 +218,16 @@ async def delete_message(update, context, messageid, delay=10):
# [
# InlineKeyboardButton("gpt-3.5-turbo-0613", callback_data="gpt-3.5-turbo-0613"),
# ],
[
InlineKeyboardButton("gpt-4", callback_data="gpt-4"),
InlineKeyboardButton("gpt-4-32k", callback_data="gpt-4-32k"),
# InlineKeyboardButton("gpt-4-0314", callback_data="gpt-4-0314"),
],
[
InlineKeyboardButton("gpt-4-1106-preview", callback_data="gpt-4-1106-preview"),
# InlineKeyboardButton("gpt-4-32k", callback_data="gpt-4-32k"),
# InlineKeyboardButton("gpt-4-32k-0314", callback_data="gpt-4-32k-0314"),
],
[
InlineKeyboardButton("gpt-4", callback_data="gpt-4"),
InlineKeyboardButton("gpt-4-32k", callback_data="gpt-4-32k"),
# InlineKeyboardButton("gpt-4-0314", callback_data="gpt-4-0314"),
],
# [
# InlineKeyboardButton("gpt-4-0613", callback_data="gpt-4-0613"),
# InlineKeyboardButton("gpt-4-32k-0613", callback_data="gpt-4-32k-0613"),
Expand Down Expand Up @@ -373,7 +427,7 @@ async def info(update, context):
messageid = message.message_id
await context.bot.delete_message(chat_id=update.effective_chat.id, message_id=update.message.message_id)

from agent import pdfQA, getmd5, persist_emdedding_pdf
from utils.agent import pdfQA, getmd5, persist_emdedding_pdf
@decorators.Authorization
async def handle_pdf(update, context):
# 获取接收到的文件
Expand Down Expand Up @@ -456,14 +510,16 @@ def setup(token):

run_async(application.bot.set_my_commands([
BotCommand('info', 'basic information'),
BotCommand('qa', 'Document Q&A with Embedding Database Search'),
BotCommand('search', 'search Google or duckduckgo'),
BotCommand('en2zh', 'translate to Chinese'),
BotCommand('zh2en', 'translate to English'),
BotCommand('qa', 'Document Q&A with Embedding Database Search'),
BotCommand('start', 'Start the bot'),
BotCommand('reset', 'Reset the bot'),
]))

application.add_handler(CommandHandler("start", start))
application.add_handler(CommandHandler("search", lambda update, context: search(update, context, title=f"`🤖️ {config.GPT_ENGINE}`\n\n", robot=config.ChatGPTbot)))
application.add_handler(CallbackQueryHandler(button_press))
application.add_handler(CommandHandler("reset", reset_chat))
application.add_handler(CommandHandler("en2zh", lambda update, context: command_bot(update, context, "simplified chinese", robot=config.ChatGPTbot)))
Expand Down
37 changes: 27 additions & 10 deletions chatgpt2api/chatgpt2api.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
import config
import threading
import time as record_time
from agent import ThreadWithReturnValue, Web_crawler, pdf_search, getddgsearchurl, getgooglesearchurl, gptsearch, ChainStreamHandler, ChatOpenAI, CallbackManager, PromptTemplate, LLMChain, EducationalLLM
from utils.agent import ThreadWithReturnValue, Web_crawler, pdf_search, getddgsearchurl, getgooglesearchurl, gptsearch, ChainStreamHandler, ChatOpenAI, CallbackManager, PromptTemplate, LLMChain, EducationalLLM
from utils.function_call import function_call_list

def get_filtered_keys_from_object(obj: object, *keys: str) -> Set[str]:
"""
Expand Down Expand Up @@ -240,10 +241,8 @@ def ask_stream(
or "https://api.openai.com/v1/chat/completions"
)
headers = {"Authorization": f"Bearer {kwargs.get('api_key', self.api_key)}"}
response = self.session.post(
url,
headers=headers,
json={

json_post = {
"model": os.environ.get("MODEL_NAME") or model or self.engine,
"messages": self.conversation[convo_id] if pass_history else [{"role": "system","content": self.system_prompt},{"role": role, "content": prompt}],
"stream": True,
Expand All @@ -264,7 +263,13 @@ def ask_stream(
self.get_max_tokens(convo_id=convo_id),
kwargs.get("max_tokens", self.max_tokens),
),
},
}
if config.SEARCH_USE_GPT:
json_post.update(function_call_list["web_search"])
response = self.session.post(
url,
headers=headers,
json=json_post,
timeout=kwargs.get("timeout", self.timeout),
stream=True,
)
Expand All @@ -274,6 +279,7 @@ def ask_stream(
)
response_role: str or None = None
full_response: str = ""
need_function_call = False
for line in response.iter_lines():
if not line:
continue
Expand All @@ -290,11 +296,20 @@ def ask_stream(
continue
if "role" in delta:
response_role = delta["role"]
if "content" in delta:
if "content" in delta and delta["content"]:
need_function_call = False
content = delta["content"]
full_response += content
yield content
self.add_to_conversation(full_response, response_role, convo_id=convo_id)
if "function_call" in delta and config.SEARCH_USE_GPT:
need_function_call = True
function_call_content = delta["function_call"]["arguments"]
full_response += function_call_content
if need_function_call:
keywords = json.loads(full_response)["prompt"]
yield from self.search_summary(keywords, convo_id=convo_id, need_function_call=True)
else:
self.add_to_conversation(full_response, response_role, convo_id=convo_id)

async def ask_stream_async(
self,
Expand Down Expand Up @@ -426,13 +441,15 @@ def search_summary(
convo_id: str = "default",
model: str = None,
pass_history: bool = True,
need_function_call: bool = False,
**kwargs,
):

if convo_id not in self.conversation:
self.reset(convo_id=convo_id, system_prompt=self.system_prompt)
self.add_to_conversation(prompt, "user", convo_id=convo_id)
self.__truncate_conversation(convo_id=convo_id)
if need_function_call == False:
self.add_to_conversation(prompt, "user", convo_id=convo_id)
self.__truncate_conversation(convo_id=convo_id)

start_time = record_time.time()

Expand Down
2 changes: 1 addition & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from bot import setup
from urllib import parse
from waitress import serve
from runasync import run_async
from utils.runasync import run_async
from flask import Flask, request, jsonify
from config import BOT_TOKEN, WEB_HOOK, PORT

Expand Down
42 changes: 23 additions & 19 deletions test/test.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
my_list = [
{"role": "admin", "content": "This is admin content."},
{"role": "user", "content": "This is user content."}
]
# my_list = [
# {"role": "admin", "content": "This is admin content."},
# {"role": "user", "content": "This is user content."}
# ]
a = {"role": "admin"}
b = {"content": "This is user content."}
a.update(b)
print(a)

content_list = [item["content"] for item in my_list]
print(content_list)
# content_list = [item["content"] for item in my_list]
# print(content_list)

engine = "gpt-3.5-turbo-1106"
truncate_limit = (
30500
if "gpt-4-32k" in engine
else 6500
if "gpt-4" in engine
else 14500
if "gpt-3.5-turbo-16k" in engine or "gpt-3.5-turbo-1106" in engine
else 98500
if ("claude-2-web" or "claude-2") in engine
else 3400
)
# engine = "gpt-3.5-turbo-1106"
# truncate_limit = (
# 30500
# if "gpt-4-32k" in engine
# else 6500
# if "gpt-4" in engine
# else 14500
# if "gpt-3.5-turbo-16k" in engine or "gpt-3.5-turbo-1106" in engine
# else 98500
# if ("claude-2-web" or "claude-2") in engine
# else 3400
# )

print(truncate_limit)
# print(truncate_limit)
4 changes: 3 additions & 1 deletion test/test_Faucet.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ def gptsearch(result, llm):
response = response.content
return response

chainllm = ChatOpenAI(temperature=0.5, openai_api_base="https://openkey.cloud/v1", model_name="gpt-4", openai_api_key="sk-ucUnnmqI9DdtsAXG8OKxOFxD5dnSrU3E3ZQh4PJa1dgQ7KzE")
chainllm = ChatOpenAI(temperature=0.5, openai_api_base="https://openkey.cloud/v1", model_name="gpt-3.5-turbo", openai_api_key="sk-ucUnnmqI9DdtsAXG8OKxOFxD5dnSrU3E3ZQh4PJa1dgQ7KzE")
# chainllm = ChatOpenAI(temperature=0.5, openai_api_base="https://openkey.cloud/v1", model_name="gpt-4-1106-preview", openai_api_key="sk-ucUnnmqI9DdtsAXG8OKxOFxD5dnSrU3E3ZQh4PJa1dgQ7KzE")
# chainllm = ChatOpenAI(temperature=0.5, openai_api_base="https://openkey.cloud/v1", model_name="gpt-4", openai_api_key="sk-ucUnnmqI9DdtsAXG8OKxOFxD5dnSrU3E3ZQh4PJa1dgQ7KzE")

print(gptsearch("鲁迅和周树人为什么打架", chainllm))
2 changes: 1 addition & 1 deletion test/test_keyword.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.chat_models import ChatOpenAI
from googlesearch import GoogleSearchAPIWrapper
from utils.googlesearch import GoogleSearchAPIWrapper


def getgooglesearchurl(result, numresults=3):
Expand Down
4 changes: 2 additions & 2 deletions agent.py → utils/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from langchain.text_splitter import CharacterTextSplitter
from langchain.tools import DuckDuckGoSearchRun, DuckDuckGoSearchResults, Tool
from langchain.utilities import WikipediaAPIWrapper
from googlesearch import GoogleSearchAPIWrapper
from utils.googlesearch import GoogleSearchAPIWrapper
from langchain.document_loaders import UnstructuredPDFLoader

def getmd5(string):
Expand All @@ -44,7 +44,7 @@ def getmd5(string):
md5_hex = md5_hash.hexdigest()
return md5_hex

from sitemap import SitemapLoader
from utils.sitemap import SitemapLoader
async def get_doc_from_sitemap(url):
# https://www.langchain.asia/modules/indexes/document_loaders/examples/sitemap#%E8%BF%87%E6%BB%A4%E7%AB%99%E7%82%B9%E5%9C%B0%E5%9B%BE-url-
sitemap_loader = SitemapLoader(web_path=url)
Expand Down
File renamed without changes.
64 changes: 64 additions & 0 deletions utils/function_call.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
function_call_list = {
"current_weather": {
"functions": [
{
"name": "get_current_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
}
}
],
"function_call": "auto"
},
"web_search": {
"functions": [
{
"name": "get_web_search_results",
"description": "Search Google to enhance knowledge.",
"parameters": {
"type": "object",
"properties": {
"prompt": {
"type": "string",
"description": "The prompt to search."
}
},
"required": ["prompt"]
}
}
],
"function_call": "auto"
},
# "web_search": {
# "functions": [
# {
# "name": "get_web_search_results",
# "description": "Get the web page search results in a given keywords",
# "parameters": {
# "type": "object",
# "properties": {
# "keywords": {
# "type": "string",
# "description": "keywords that can yield better search results, keywords are connected with spaces, e.g. 1. The keywords of the sentence (How much does the zeabur software service cost per month?) is (zeabur price). 2. The keywords of the sentence (今天的微博热搜有哪些?) is (微博 热搜)"
# }
# },
# "required": ["keywords"]
# }
# }
# ],
# "function_call": "auto"
# },
}

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit a6168de

Please sign in to comment.