mistpe commited on
Commit
1823fe5
1 Parent(s): f4971d9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +148 -86
app.py CHANGED
@@ -1,12 +1,18 @@
1
- import os
2
- import json
 
3
  import hashlib
4
  import time
5
  import xml.etree.ElementTree as ET
6
- from flask import Flask, request, make_response
 
7
  from openai import OpenAI
8
  from dotenv import load_dotenv
9
- from functions import FUNCTIONS, FUNCTIONS_GROUP_1, FUNCTIONS_GROUP_2, process_function_call
 
 
 
 
10
 
11
  # 加载环境变量
12
  load_dotenv()
@@ -17,18 +23,66 @@ app = Flask(__name__)
17
  TOKEN = os.getenv('TOKEN')
18
  API_KEY = os.getenv("API_KEY")
19
  BASE_URL = os.getenv("OPENAI_BASE_URL")
 
20
  client = OpenAI(api_key=API_KEY, base_url=BASE_URL)
21
 
22
- # 定义可用的模型列表
23
- AVAILABLE_MODELS = {
24
- 'gpt-4o-mini': 'gpt-4o-mini',
25
- 'gpt-4o-mini': 'gpt-4o-mini',
26
- 'gpt-4o-mini': 'gpt-4o-mini',
27
- }
28
-
29
  # 存储用户会话信息
30
  user_sessions = {}
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  def verify_wechat(request):
33
  data = request.args
34
  signature = data.get('signature')
@@ -66,13 +120,14 @@ def generate_response_xml(from_user_name, to_user_name, output_content):
66
  response.content_type = 'application/xml'
67
  return response
68
 
69
- def get_openai_response(messages, model="gpt-4o-mini", functions=None, function_call=None):
70
  try:
 
71
  response = client.chat.completions.create(
72
- model=model,
73
  messages=messages,
74
- functions=functions,
75
- function_call=function_call
76
  )
77
  return response.choices[0].message
78
  except Exception as e:
@@ -82,8 +137,47 @@ def get_openai_response(messages, model="gpt-4o-mini", functions=None, function_
82
  def split_message(message, max_length=500):
83
  return [message[i:i+max_length] for i in range(0, len(message), max_length)]
84
 
85
- def list_available_models():
86
- return "\n".join([f"{key}: {value}" for key, value in AVAILABLE_MODELS.items()])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
88
  @app.route('/api/wx', methods=['GET', 'POST'])
89
  def wechatai():
@@ -91,28 +185,14 @@ def wechatai():
91
  return verify_wechat(request)
92
  else:
93
  xml_str = request.data
94
- if not xml_str:
95
- return ""
96
-
97
  user_message_content, from_user_name, to_user_name = getUserMessageContentFromXML(xml_str)
98
-
99
  if from_user_name not in user_sessions:
100
- user_sessions[from_user_name] = {'model': 'gpt-4o-mini', 'messages': [], 'pending_response': []}
101
 
102
  session = user_sessions[from_user_name]
103
-
104
- if user_message_content.lower() == '/models':
105
- response_content = f"可用的模型列表:\n{list_available_models()}\n\n使用 /model 模型名称 来切换模型"
106
- return generate_response_xml(from_user_name, to_user_name, response_content)
107
- elif user_message_content.lower().startswith('/model'):
108
- model = user_message_content.split(' ')[1]
109
- if model in AVAILABLE_MODELS:
110
- session['model'] = model
111
- response_content = f'模型已切换为 {AVAILABLE_MODELS[model]}'
112
- else:
113
- response_content = f'无效的模型名称。可用的模型有:\n{list_available_models()}'
114
- return generate_response_xml(from_user_name, to_user_name, response_content)
115
- elif user_message_content.lower() == '继续':
116
  if session['pending_response']:
117
  response_content = session['pending_response'].pop(0)
118
  if session['pending_response']:
@@ -121,58 +201,40 @@ def wechatai():
121
  response_content += '\n\n回复结束。'
122
  else:
123
  response_content = "没有待发送的消息。"
124
- return generate_response_xml(from_user_name, to_user_name, response_content)
125
-
126
- session['messages'].append({"role": "user", "content": user_message_content})
127
- messages = session['messages']
128
-
129
- # 次级模型1: 处理搜索相关函数
130
- sub_model_1_response = get_openai_response(messages, model=session['model'], functions=FUNCTIONS_GROUP_1, function_call="auto")
131
-
132
- # 次级模型2: 处理邮件发送相关函数
133
- sub_model_2_response = get_openai_response(messages, model=session['model'], functions=FUNCTIONS_GROUP_2, function_call="auto")
134
-
135
- function_call_1 = sub_model_1_response.function_call if sub_model_1_response and sub_model_1_response.function_call else None
136
- function_call_2 = sub_model_2_response.function_call if sub_model_2_response and sub_model_2_response.function_call else None
137
-
138
- final_function_call = None
139
-
140
- if function_call_1 and function_call_2:
141
- # 裁决模型: 决定使用哪个函数调用
142
- arbitration_messages = messages + [
143
- {"role": "system", "content": "两个次级模型都建议使用函数。请决定使用哪个函数更合适。"},
144
- {"role": "assistant", "content": f"次级模型1建议使用函数:{function_call_1.name}"},
145
- {"role": "assistant", "content": f"次级模型2建议使用函数:{function_call_2.name}"}
146
- ]
147
- arbitration_response = get_openai_response(arbitration_messages, model=session['model'])
148
- if arbitration_response and ("模型1" in arbitration_response.content or function_call_1.name in arbitration_response.content):
149
- final_function_call = function_call_1
150
- else:
151
- final_function_call = function_call_2
152
- elif function_call_1:
153
- final_function_call = function_call_1
154
- elif function_call_2:
155
- final_function_call = function_call_2
156
-
157
- if final_function_call:
158
- function_name = final_function_call.name
159
- function_args = json.loads(final_function_call.arguments)
160
- function_result = process_function_call(function_name, function_args)
161
 
162
- messages.append({"role": "function", "name": function_name, "content": json.dumps(function_result, ensure_ascii=False)})
163
-
164
- # 主模型: 生成最终回复
165
- final_response = get_openai_response(messages, model=session['model'])
166
- response_content = final_response.content if final_response else "Error occurred"
167
-
168
- session['messages'].append({"role": "assistant", "content": response_content})
169
-
170
- # 处理长消息
171
- response_parts = split_message(response_content)
172
- if len(response_parts) > 1:
173
- session['pending_response'] = response_parts[1:]
174
- response_content = response_parts[0] + '\n\n回复"继续"获取下一部分。'
175
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  return generate_response_xml(from_user_name, to_user_name, response_content)
177
 
178
  if __name__ == '__main__':
 
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ from flask import Flask, request, make_response
4
  import hashlib
5
  import time
6
  import xml.etree.ElementTree as ET
7
+ import os
8
+ import json
9
  from openai import OpenAI
10
  from dotenv import load_dotenv
11
+ from duckduckgo_search import DDGS
12
+ import requests
13
+ import smtplib
14
+ from email.mime.text import MIMEText
15
+ from email.mime.multipart import MIMEMultipart
16
 
17
  # 加载环境变量
18
  load_dotenv()
 
23
  TOKEN = os.getenv('TOKEN')
24
  API_KEY = os.getenv("API_KEY")
25
  BASE_URL = os.getenv("OPENAI_BASE_URL")
26
+ emailkey = os.getenv("EMAIL_KEY")
27
  client = OpenAI(api_key=API_KEY, base_url=BASE_URL)
28
 
 
 
 
 
 
 
 
29
  # 存储用户会话信息
30
  user_sessions = {}
31
 
32
+ FUNCTIONS = [
33
+ {
34
+ "name": "search_duckduckgo",
35
+ "description": "使用DuckDuckGo搜索引擎查询信息。可以搜索最新新闻、文章、博客等内容。",
36
+ "parameters": {
37
+ "type": "object",
38
+ "properties": {
39
+ "keywords": {
40
+ "type": "array",
41
+ "items": {"type": "string"},
42
+ "description": "搜索的关键词列表。例如:['Python', '机器学习', '最新进展']。"
43
+ }
44
+ },
45
+ "required": ["keywords"]
46
+ }
47
+ },
48
+ {
49
+ "name": "search_papers",
50
+ "description": "使用Crossref API搜索学术论文。",
51
+ "parameters": {
52
+ "type": "object",
53
+ "properties": {
54
+ "query": {
55
+ "type": "string",
56
+ "description": "搜索查询字符串。例如:'climate change'。"
57
+ }
58
+ },
59
+ "required": ["query"]
60
+ }
61
+ },
62
+ {
63
+ "name": "send_email",
64
+ "description": "发送电子邮件。",
65
+ "parameters": {
66
+ "type": "object",
67
+ "properties": {
68
+ "to": {
69
+ "type": "string",
70
+ "description": "收件人邮箱地址"
71
+ },
72
+ "subject": {
73
+ "type": "string",
74
+ "description": "邮件主题"
75
+ },
76
+ "content": {
77
+ "type": "string",
78
+ "description": "邮件内容"
79
+ }
80
+ },
81
+ "required": ["to", "subject", "content"]
82
+ }
83
+ }
84
+ ]
85
+
86
  def verify_wechat(request):
87
  data = request.args
88
  signature = data.get('signature')
 
120
  response.content_type = 'application/xml'
121
  return response
122
 
123
+ def get_openai_response(messages):
124
  try:
125
+ tools = [{"type": "function", "function": func} for func in FUNCTIONS]
126
  response = client.chat.completions.create(
127
+ model="gpt-4o-mini",
128
  messages=messages,
129
+ tools=tools,
130
+ tool_choice="auto"
131
  )
132
  return response.choices[0].message
133
  except Exception as e:
 
137
  def split_message(message, max_length=500):
138
  return [message[i:i+max_length] for i in range(0, len(message), max_length)]
139
 
140
+ def search_duckduckgo(keywords):
141
+ search_term = " ".join(keywords)
142
+ with DDGS() as ddgs:
143
+ return list(ddgs.text(keywords=search_term, region="cn-zh", safesearch="on", max_results=5))
144
+
145
+ def search_papers(query):
146
+ url = f"https://api.crossref.org/works?query={query}"
147
+ response = requests.get(url)
148
+ if response.status_code == 200:
149
+ data = response.json()
150
+ papers = data['message']['items'][:5] # 限制结果数量
151
+ return [{"title": paper.get('title', [''])[0], "DOI": paper.get('DOI', '')} for paper in papers]
152
+ else:
153
+ return []
154
+
155
+ def send_email(to, subject, content):
156
+ try:
157
+ with smtplib.SMTP('106.15.184.28', 8025) as smtp:
158
+ smtp.login("jwt", emailkey)
159
+ message = MIMEMultipart()
160
+ message['From'] = "Me <[email protected]>"
161
+ message['To'] = to
162
+ message['Subject'] = subject
163
+ message.attach(MIMEText(content, 'html'))
164
+ smtp.sendmail("[email protected]", to, message.as_string())
165
+ return True
166
+ except Exception as e:
167
+ print(f"发送邮件时出错: {str(e)}")
168
+ return False
169
+
170
+ def process_function_call(tool_call):
171
+ function_name = tool_call.function.name
172
+ function_args = json.loads(tool_call.function.arguments)
173
+ if function_name == "search_duckduckgo":
174
+ return search_duckduckgo(function_args.get('keywords', []))
175
+ elif function_name == "search_papers":
176
+ return search_papers(function_args.get('query', ''))
177
+ elif function_name == "send_email":
178
+ return send_email(function_args.get('to', ''), function_args.get('subject', ''), function_args.get('content', ''))
179
+ else:
180
+ return None
181
 
182
  @app.route('/api/wx', methods=['GET', 'POST'])
183
  def wechatai():
 
185
  return verify_wechat(request)
186
  else:
187
  xml_str = request.data
 
 
 
188
  user_message_content, from_user_name, to_user_name = getUserMessageContentFromXML(xml_str)
189
+
190
  if from_user_name not in user_sessions:
191
+ user_sessions[from_user_name] = {'messages': [], 'pending_response': []}
192
 
193
  session = user_sessions[from_user_name]
194
+
195
+ if user_message_content.lower() == '继续':
 
 
 
 
 
 
 
 
 
 
 
196
  if session['pending_response']:
197
  response_content = session['pending_response'].pop(0)
198
  if session['pending_response']:
 
201
  response_content += '\n\n回复结束。'
202
  else:
203
  response_content = "没有待发送的消息。"
204
+ else:
205
+ session['messages'].append({"role": "user", "content": user_message_content})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
 
207
+ gpt_response = get_openai_response(session['messages'])
208
+
209
+ if gpt_response.tool_calls:
210
+ for tool_call in gpt_response.tool_calls:
211
+ function_result = process_function_call(tool_call)
212
+ session['messages'].append({
213
+ "role": "assistant",
214
+ "content": None,
215
+ "tool_call_id": tool_call.id,
216
+ "tool_calls": [tool_call]
217
+ })
218
+ session['messages'].append({
219
+ "role": "tool",
220
+ "content": json.dumps(function_result),
221
+ "tool_call_id": tool_call.id
222
+ })
223
+
224
+ final_response = get_openai_response(session['messages'])
225
+ response_content = final_response.content
226
+ else:
227
+ response_content = gpt_response.content
228
+
229
+ session['messages'].append({"role": "assistant", "content": response_content})
230
+
231
+ response_parts = split_message(response_content)
232
+ if len(response_parts) > 1:
233
+ response_content = response_parts[0] + '\n\n回复"继续"获取下一部分。'
234
+ session['pending_response'] = response_parts[1:]
235
+ else:
236
+ response_content = response_parts[0]
237
+
238
  return generate_response_xml(from_user_name, to_user_name, response_content)
239
 
240
  if __name__ == '__main__':