mistpe commited on
Commit
d5e38b1
·
verified ·
1 Parent(s): ce16bfe

Upload app仅生图,没有识图.py

Browse files
Files changed (1) hide show
  1. app仅生图,没有识图.py +629 -0
app仅生图,没有识图.py ADDED
@@ -0,0 +1,629 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, make_response
2
+ import hashlib
3
+ import time
4
+ import xml.etree.ElementTree as ET
5
+ import os
6
+ import json
7
+ from openai import OpenAI
8
+ from dotenv import load_dotenv
9
+ from markdown import markdown
10
+ import re
11
+ import threading
12
+ import logging
13
+ from datetime import datetime
14
+ import asyncio
15
+ from concurrent.futures import ThreadPoolExecutor
16
+ import queue
17
+ import uuid
18
+ import base64
19
+ from Crypto.Cipher import AES
20
+ import struct
21
+ import random
22
+ import string
23
+ import requests
24
+
25
+ logging.basicConfig(
26
+ level=logging.INFO,
27
+ format='%(asctime)s - %(levelname)s - %(message)s',
28
+ handlers=[
29
+ logging.FileHandler('wechat_service.log'),
30
+ logging.StreamHandler()
31
+ ]
32
+ )
33
+
34
+ load_dotenv()
35
+
36
+ app = Flask(__name__)
37
+
38
+ # Configuration
39
+ TOKEN = os.getenv('TOKEN')
40
+ ENCODING_AES_KEY = os.getenv('ENCODING_AES_KEY')
41
+ APPID = os.getenv('APPID')
42
+ APPSECRET = os.getenv('APPSECRET') # 新增 AppSecret
43
+ API_KEY = os.getenv("API_KEY")
44
+ BASE_URL = os.getenv("OPENAI_BASE_URL")
45
+ IMAGE_MODEL_URL = os.getenv("IMAGE_MODEL_URL")
46
+ IMAGE_MODEL_KEY = os.getenv("IMAGE_MODEL_KEY")
47
+
48
+ client = OpenAI(api_key=API_KEY, base_url=BASE_URL)
49
+ executor = ThreadPoolExecutor(max_workers=10)
50
+
51
+
52
+ # Define tools for image generation
53
+ TOOLS = [
54
+ {
55
+ "type": "function",
56
+ "function": {
57
+ "name": "generate_image",
58
+ "description": "Generate an image based on text description",
59
+ "parameters": {
60
+ "type": "object",
61
+ "properties": {
62
+ "prompt": {
63
+ "type": "string",
64
+ "description": "The description of the image to generate"
65
+ }
66
+ },
67
+ "required": ["prompt"]
68
+ }
69
+ }
70
+ }
71
+ ]
72
+ class AccessTokenManager:
73
+ def __init__(self):
74
+ self._access_token = None
75
+ self._expires_at = 0
76
+ self._lock = threading.Lock()
77
+
78
+ def get_token(self):
79
+ with self._lock:
80
+ now = time.time()
81
+ # 提前5分钟刷新token,确保在调用时token都是有效的
82
+ if self._access_token and now < (self._expires_at - 300):
83
+ return self._access_token
84
+
85
+ try:
86
+ url = "https://api.weixin.qq.com/cgi-bin/token"
87
+ params = {
88
+ "grant_type": "client_credential",
89
+ "appid": APPID,
90
+ "secret": APPSECRET
91
+ }
92
+
93
+ logging.info("开始获取新的access_token")
94
+ response = requests.get(url, params=params)
95
+ response.raise_for_status()
96
+ result = response.json()
97
+
98
+ if "access_token" not in result:
99
+ error_msg = f"获取access_token失败: {result}"
100
+ logging.error(error_msg)
101
+ raise ValueError(error_msg)
102
+
103
+ self._access_token = result["access_token"]
104
+ self._expires_at = now + result["expires_in"]
105
+ logging.info("成功获取新的access_token")
106
+
107
+ return self._access_token
108
+
109
+ except Exception as e:
110
+ error_msg = f"获取access_token时发生错误: {str(e)}"
111
+ logging.error(error_msg)
112
+ raise
113
+
114
+ def refresh_token(self):
115
+ with self._lock:
116
+ self._access_token = None
117
+ return self.get_token()
118
+
119
+ class WeChatCrypto:
120
+ def __init__(self, key, app_id):
121
+ self.key = base64.b64decode(key + '=')
122
+ self.app_id = app_id
123
+
124
+ def encrypt(self, text):
125
+ random_str = ''.join(random.choices(string.ascii_letters + string.digits, k=16))
126
+ text_bytes = text.encode('utf-8')
127
+ msg_len = struct.pack('>I', len(text_bytes))
128
+ message = random_str.encode('utf-8') + msg_len + text_bytes + self.app_id.encode('utf-8')
129
+ pad_len = 32 - (len(message) % 32)
130
+ message += chr(pad_len).encode('utf-8') * pad_len
131
+ cipher = AES.new(self.key, AES.MODE_CBC, self.key[:16])
132
+ encrypted = cipher.encrypt(message)
133
+ return base64.b64encode(encrypted).decode('utf-8')
134
+
135
+ def decrypt(self, encrypted_text):
136
+ encrypted_data = base64.b64decode(encrypted_text)
137
+ cipher = AES.new(self.key, AES.MODE_CBC, self.key[:16])
138
+ decrypted = cipher.decrypt(encrypted_data)
139
+ pad_len = decrypted[-1]
140
+ if not isinstance(pad_len, int):
141
+ pad_len = ord(pad_len)
142
+ content = decrypted[16:-pad_len]
143
+ msg_len = struct.unpack('>I', content[:4])[0]
144
+ xml_content = content[4:msg_len + 4].decode('utf-8')
145
+ app_id = content[msg_len + 4:].decode('utf-8')
146
+ if app_id != self.app_id:
147
+ raise ValueError('Invalid AppID')
148
+ return xml_content
149
+
150
+ class AsyncResponse:
151
+ def __init__(self):
152
+ self.status = "processing"
153
+ self.result = None
154
+ self.error = None
155
+ self.create_time = time.time()
156
+ self.timeout = 3600
157
+ self.response_type = "text" # Can be "text" or "image"
158
+ self.media_id = None # For image responses
159
+
160
+ def is_expired(self):
161
+ return time.time() - self.create_time > self.timeout
162
+
163
+ class UserSession:
164
+ def __init__(self):
165
+ self.messages = [{"role": "system", "content": "你是HXIAO公众号的智能助手,这一个用来分享与学习人工智能的公众号,我们的目标是专注AI应用的简单研究与实践。致力于分享切实可行的技术方案,希望让复杂的技术变得简单易懂。也喜欢用通俗的语言来解释专业概念,让技术真正服务于每个学习者"}]
166
+ self.pending_parts = []
167
+ self.last_active = time.time()
168
+ self.current_task = None
169
+ self.response_queue = {}
170
+ self.session_timeout = 3600
171
+
172
+ def is_expired(self):
173
+ return time.time() - self.last_active > self.session_timeout
174
+
175
+ def cleanup_expired_tasks(self):
176
+ expired_tasks = [
177
+ task_id for task_id, response in self.response_queue.items()
178
+ if response.is_expired()
179
+ ]
180
+ for task_id in expired_tasks:
181
+ del self.response_queue[task_id]
182
+ if self.current_task == task_id:
183
+ self.current_task = None
184
+
185
+ class SessionManager:
186
+ def __init__(self):
187
+ self.sessions = {}
188
+ self._lock = threading.Lock()
189
+ self.crypto = WeChatCrypto(ENCODING_AES_KEY, APPID)
190
+
191
+ def get_session(self, user_id):
192
+ with self._lock:
193
+ current_time = time.time()
194
+ if user_id in self.sessions:
195
+ session = self.sessions[user_id]
196
+ if session.is_expired():
197
+ session = UserSession()
198
+ else:
199
+ session.cleanup_expired_tasks()
200
+ else:
201
+ session = UserSession()
202
+ session.last_active = current_time
203
+ self.sessions[user_id] = session
204
+ return session
205
+
206
+ def clear_session(self, user_id):
207
+ with self._lock:
208
+ if user_id in self.sessions:
209
+ self.sessions[user_id] = UserSession()
210
+
211
+ def cleanup_expired_sessions(self):
212
+ with self._lock:
213
+ current_time = time.time()
214
+ expired_users = [
215
+ user_id for user_id, session in self.sessions.items()
216
+ if session.is_expired()
217
+ ]
218
+ for user_id in expired_users:
219
+ del self.sessions[user_id]
220
+ logging.info(f"已清理过期会话: {user_id}")
221
+
222
+ def convert_markdown_to_wechat(md_text):
223
+ if not md_text:
224
+ return md_text
225
+
226
+ md_text = re.sub(r'^# (.*?)$', r'【标题】\1', md_text, flags=re.MULTILINE)
227
+ md_text = re.sub(r'^## (.*?)$', r'【子标题】\1', md_text, flags=re.MULTILINE)
228
+ md_text = re.sub(r'^### (.*?)$', r'【小标题】\1', md_text, flags=re.MULTILINE)
229
+ md_text = re.sub(r'\*\*(.*?)\*\*', r'『\1』', md_text)
230
+ md_text = re.sub(r'\*(.*?)\*', r'「\1」', md_text)
231
+ md_text = re.sub(r'`(.*?)`', r'「\1」', md_text)
232
+ md_text = re.sub(r'^\- ', '• ', md_text, flags=re.MULTILINE)
233
+ md_text = re.sub(r'^\d\. ', '○ ', md_text, flags=re.MULTILINE)
234
+ md_text = re.sub(r'```[\w]*\n(.*?)```', r'【代码开始】\n\1\n【代码结束】', md_text, flags=re.DOTALL)
235
+ md_text = re.sub(r'^> (.*?)$', r'▎\1', md_text, flags=re.MULTILINE)
236
+ md_text = re.sub(r'^-{3,}$', r'—————————', md_text, flags=re.MULTILINE)
237
+ md_text = re.sub(r'\[(.*?)\]\((.*?)\)', r'\1(\2)', md_text)
238
+ md_text = re.sub(r'\n{3,}', '\n\n', md_text)
239
+
240
+ return md_text
241
+
242
+ def verify_signature(signature, timestamp, nonce, token):
243
+ items = [token, timestamp, nonce]
244
+ items.sort()
245
+ temp_str = ''.join(items)
246
+ hash_sha1 = hashlib.sha1(temp_str.encode('utf-8')).hexdigest()
247
+ return hash_sha1 == signature
248
+
249
+ def verify_msg_signature(msg_signature, timestamp, nonce, token, encrypt_msg):
250
+ items = [token, timestamp, nonce, encrypt_msg]
251
+ items.sort()
252
+ temp_str = ''.join(items)
253
+ hash_sha1 = hashlib.sha1(temp_str.encode('utf-8')).hexdigest()
254
+ return hash_sha1 == msg_signature
255
+
256
+ def parse_xml_message(xml_content):
257
+ root = ET.fromstring(xml_content)
258
+ return {
259
+ 'content': root.find('Content').text if root.find('Content') is not None else '',
260
+ 'from_user': root.find('FromUserName').text,
261
+ 'to_user': root.find('ToUserName').text,
262
+ 'msg_id': root.find('MsgId').text if root.find('MsgId') is not None else '',
263
+ 'create_time': root.find('CreateTime').text,
264
+ 'msg_type': root.find('MsgType').text
265
+ }
266
+
267
+ def generate_response_xml(to_user, from_user, content, response_type='text', media_id=None, encrypt_type='aes'):
268
+ timestamp = str(int(time.time()))
269
+ nonce = ''.join(random.choices(string.ascii_letters + string.digits, k=10))
270
+
271
+ if response_type == 'image' and media_id:
272
+ xml_content = f'''
273
+ <xml>
274
+ <ToUserName><![CDATA[{to_user}]]></ToUserName>
275
+ <FromUserName><![CDATA[{from_user}]]></FromUserName>
276
+ <CreateTime>{timestamp}</CreateTime>
277
+ <MsgType><![CDATA[image]]></MsgType>
278
+ <Image>
279
+ <MediaId><![CDATA[{media_id}]]></MediaId>
280
+ </Image>
281
+ </xml>
282
+ '''
283
+ else:
284
+ formatted_content = convert_markdown_to_wechat(content)
285
+ xml_content = f'''
286
+ <xml>
287
+ <ToUserName><![CDATA[{to_user}]]></ToUserName>
288
+ <FromUserName><![CDATA[{from_user}]]></FromUserName>
289
+ <CreateTime>{timestamp}</CreateTime>
290
+ <MsgType><![CDATA[text]]></MsgType>
291
+ <Content><![CDATA[{formatted_content}]]></Content>
292
+ </xml>
293
+ '''
294
+
295
+ if encrypt_type == 'aes':
296
+ encrypted = session_manager.crypto.encrypt(xml_content)
297
+ signature_list = [TOKEN, timestamp, nonce, encrypted]
298
+ signature_list.sort()
299
+ msg_signature = hashlib.sha1(''.join(signature_list).encode('utf-8')).hexdigest()
300
+
301
+ response_xml = f'''
302
+ <xml>
303
+ <Encrypt><![CDATA[{encrypted}]]></Encrypt>
304
+ <MsgSignature><![CDATA[{msg_signature}]]></MsgSignature>
305
+ <TimeStamp>{timestamp}</TimeStamp>
306
+ <Nonce><![CDATA[{nonce}]]></Nonce>
307
+ </xml>
308
+ '''
309
+ else:
310
+ response_xml = xml_content
311
+
312
+ response = make_response(response_xml)
313
+ response.content_type = 'application/xml'
314
+ return response
315
+
316
+ # 创建全局的token管理器实例
317
+ token_manager = AccessTokenManager()
318
+ def upload_image_to_wechat(image_data):
319
+ """上传图片到微信服务器并获取media_id"""
320
+ try:
321
+ access_token = token_manager.get_token()
322
+ upload_url = f'https://api.weixin.qq.com/cgi-bin/media/upload?access_token={access_token}&type=image'
323
+ files = {'media': ('image.jpg', image_data, 'image/jpeg')}
324
+
325
+ logging.info("开始上传图片到微信服务器")
326
+ response = requests.post(upload_url, files=files)
327
+ response.raise_for_status()
328
+ result = response.json()
329
+
330
+ if 'media_id' not in result:
331
+ if 'errcode' in result and result['errcode'] == 40001:
332
+ # access_token 可能过期,尝试刷新并重试
333
+ logging.info("access_token已过期,正在刷新并重试")
334
+ access_token = token_manager.refresh_token()
335
+ upload_url = f'https://api.weixin.qq.com/cgi-bin/media/upload?access_token={access_token}&type=image'
336
+ response = requests.post(upload_url, files=files)
337
+ response.raise_for_status()
338
+ result = response.json()
339
+
340
+ if 'media_id' not in result:
341
+ error_msg = f"上传图片失败: {result}"
342
+ logging.error(error_msg)
343
+ raise ValueError(error_msg)
344
+
345
+ logging.info(f"图片上传成功,获取到media_id")
346
+ return result['media_id']
347
+
348
+ except Exception as e:
349
+ error_msg = f"上传图片过程中发生错误: {str(e)}"
350
+ logging.error(error_msg)
351
+ raise
352
+
353
+ def process_long_running_task(messages):
354
+ try:
355
+ logging.info("开始调用AI服务")
356
+ response = client.chat.completions.create(
357
+ model="o3-mini",
358
+ messages=messages,
359
+ tools=TOOLS,
360
+ tool_choice="auto",
361
+ timeout=60
362
+ )
363
+ logging.info("AI服务响应成功")
364
+
365
+ if response.choices[0].message.tool_calls:
366
+ logging.info("检测到tool调用")
367
+ tool_call = response.choices[0].message.tool_calls[0]
368
+ if tool_call.function.name == "generate_image":
369
+ logging.info("开始处理图片生成请求")
370
+ args = json.loads(tool_call.function.arguments)
371
+ image_response = requests.post(
372
+ IMAGE_MODEL_URL,
373
+ headers={
374
+ 'Content-Type': 'application/json',
375
+ 'Authorization': f'Bearer {IMAGE_MODEL_KEY}'
376
+ },
377
+ json={
378
+ "model": "grok-latest-image",
379
+ "messages": [{
380
+ "role": "user",
381
+ "content": args['prompt']
382
+ }]
383
+ }
384
+ )
385
+ image_response.raise_for_status()
386
+ result = image_response.json()
387
+ logging.info("图片生成成功,准备下载图片")
388
+
389
+ # 从markdown格式中提取URL
390
+ markdown_content = result['choices'][0]['message']['content']
391
+ image_url = re.search(r'\!\[image\]\((.*?)\)', markdown_content).group(1)
392
+ logging.info(f"提取到图片URL: {image_url}")
393
+
394
+ img_response = requests.get(image_url)
395
+ img_response.raise_for_status()
396
+ media_id = upload_image_to_wechat(img_response.content)
397
+
398
+ return {
399
+ "type": "image",
400
+ "media_id": media_id
401
+ }
402
+
403
+ logging.info("返回文本响应")
404
+ return {
405
+ "type": "text",
406
+ "content": response.choices[0].message.content
407
+ }
408
+ except Exception as e:
409
+ logging.error(f"API调用错误: {str(e)}")
410
+ raise
411
+
412
+ def handle_async_task(session, task_id, messages):
413
+ try:
414
+ logging.info(f"开始处理异步任务: {task_id}")
415
+ if task_id not in session.response_queue:
416
+ return
417
+
418
+ result = process_long_running_task(messages)
419
+
420
+ if task_id in session.response_queue and not session.response_queue[task_id].is_expired():
421
+ session.response_queue[task_id].status = "completed"
422
+ session.response_queue[task_id].response_type = result.get("type", "text")
423
+ if result["type"] == "image":
424
+ session.response_queue[task_id].media_id = result["media_id"]
425
+ session.response_queue[task_id].result = None
426
+ messages.append({"role": "assistant", "content": "图片已生成"})
427
+ else:
428
+ session.response_queue[task_id].result = result["content"]
429
+ messages.append({"role": "assistant", "content": result["content"]})
430
+ except Exception as e:
431
+ logging.error(f"异步任务处理失败: {str(e)}")
432
+ if task_id in session.response_queue:
433
+ session.response_queue[task_id].status = "failed"
434
+ session.response_queue[task_id].error = str(e)
435
+
436
+ def generate_initial_response():
437
+ return "您的请求正在处理中,请回复'查询'获取结果(生图需要时间)"
438
+
439
+ def split_message(message, max_length=500):
440
+ return [message[i:i+max_length] for i in range(0, len(message), max_length)]
441
+
442
+ def append_status_message(content, has_pending_parts=False, is_processing=False):
443
+ if "您的请求正在处理中" in content:
444
+ return content + "\n\n-------------------\n发送'新对话'开始新的对话"
445
+
446
+ status_message = "\n\n-------------------"
447
+ if is_processing:
448
+ status_message += "\n请回复'查询'获取结果"
449
+ elif has_pending_parts:
450
+ status_message += "\n当前消息已截断,发送'继续'查看后续内容"
451
+ status_message += "\n发送'新对话'开始新的对话"
452
+ return content + status_message
453
+
454
+ session_manager = SessionManager()
455
+
456
+ @app.route('/api/wx', methods=['GET', 'POST'])
457
+ def wechatai():
458
+ if request.method == 'GET':
459
+ signature = request.args.get('signature')
460
+ timestamp = request.args.get('timestamp')
461
+ nonce = request.args.get('nonce')
462
+ echostr = request.args.get('echostr')
463
+
464
+ if verify_signature(signature, timestamp, nonce, TOKEN):
465
+ return echostr
466
+ return 'error', 403
467
+
468
+ try:
469
+ encrypt_type = request.args.get('encrypt_type', '')
470
+
471
+ if encrypt_type == 'aes':
472
+ msg_signature = request.args.get('msg_signature')
473
+ timestamp = request.args.get('timestamp')
474
+ nonce = request.args.get('nonce')
475
+
476
+ xml_tree = ET.fromstring(request.data)
477
+ encrypted_text = xml_tree.find('Encrypt').text
478
+
479
+ if not verify_msg_signature(msg_signature, timestamp, nonce, TOKEN, encrypted_text):
480
+ return 'Invalid signature', 403
481
+
482
+ decrypted_xml = session_manager.crypto.decrypt(encrypted_text)
483
+ message_data = parse_xml_message(decrypted_xml)
484
+ else:
485
+ message_data = parse_xml_message(request.data)
486
+
487
+ user_content = message_data['content'].strip()
488
+ from_user = message_data['from_user']
489
+ to_user = message_data['to_user']
490
+
491
+ logging.info(f"收到用户({from_user})消息: {user_content}")
492
+ session = session_manager.get_session(from_user)
493
+
494
+ if user_content == '新对话':
495
+ session_manager.clear_session(from_user)
496
+ return generate_response_xml(
497
+ from_user,
498
+ to_user,
499
+ append_status_message('已开始新的对话。请描述您的问题。'),
500
+ encrypt_type=encrypt_type
501
+ )
502
+
503
+ if user_content == '继续':
504
+ if session.pending_parts:
505
+ next_part = session.pending_parts.pop(0)
506
+ has_more = bool(session.pending_parts)
507
+ return generate_response_xml(
508
+ from_user,
509
+ to_user,
510
+ append_status_message(next_part, has_more),
511
+ encrypt_type=encrypt_type
512
+ )
513
+ return generate_response_xml(
514
+ from_user,
515
+ to_user,
516
+ append_status_message('没有更多内容了。请继续您的问题。'),
517
+ encrypt_type=encrypt_type
518
+ )
519
+
520
+ if user_content == '查询':
521
+ if session.current_task:
522
+ task_response = session.response_queue.get(session.current_task)
523
+ if task_response:
524
+ if task_response.is_expired():
525
+ del session.response_queue[session.current_task]
526
+ session.current_task = None
527
+ return generate_response_xml(
528
+ from_user,
529
+ to_user,
530
+ append_status_message('请求已过期,请重新提问。'),
531
+ encrypt_type=encrypt_type
532
+ )
533
+
534
+ if task_response.status == "completed":
535
+ if task_response.response_type == "image":
536
+ logging.info("返回图片响应")
537
+ del session.response_queue[session.current_task]
538
+ session.current_task = None
539
+ return generate_response_xml(
540
+ from_user,
541
+ to_user,
542
+ "",
543
+ response_type="image",
544
+ media_id=task_response.media_id,
545
+ encrypt_type=encrypt_type
546
+ )
547
+ else:
548
+ response = task_response.result
549
+ del session.response_queue[session.current_task]
550
+ session.current_task = None
551
+
552
+ if len(response) > 500:
553
+ parts = split_message(response)
554
+ first_part = parts.pop(0)
555
+ session.pending_parts = parts
556
+ return generate_response_xml(
557
+ from_user,
558
+ to_user,
559
+ append_status_message(first_part, True),
560
+ encrypt_type=encrypt_type
561
+ )
562
+ return generate_response_xml(
563
+ from_user,
564
+ to_user,
565
+ append_status_message(response),
566
+ encrypt_type=encrypt_type
567
+ )
568
+ elif task_response.status == "failed":
569
+ error_message = '处理过程中出现错误,请重新提问。'
570
+ del session.response_queue[session.current_task]
571
+ session.current_task = None
572
+ return generate_response_xml(
573
+ from_user,
574
+ to_user,
575
+ append_status_message(error_message),
576
+ encrypt_type=encrypt_type
577
+ )
578
+ else:
579
+ return generate_response_xml(
580
+ from_user,
581
+ to_user,
582
+ append_status_message('正在处理中,请稍后再次查询。(生图需要时间)', is_processing=True),
583
+ encrypt_type=encrypt_type
584
+ )
585
+ return generate_response_xml(
586
+ from_user,
587
+ to_user,
588
+ append_status_message('没有正在处理的请求。'),
589
+ encrypt_type=encrypt_type
590
+ )
591
+
592
+ session.messages.append({"role": "user", "content": user_content})
593
+
594
+ task_id = str(uuid.uuid4())
595
+ session.current_task = task_id
596
+ session.response_queue[task_id] = AsyncResponse()
597
+
598
+ executor.submit(handle_async_task, session, task_id, session.messages.copy())
599
+
600
+ logging.info("返回初始响应")
601
+ return generate_response_xml(
602
+ from_user,
603
+ to_user,
604
+ append_status_message(generate_initial_response(), is_processing=True),
605
+ encrypt_type=encrypt_type
606
+ )
607
+
608
+ except Exception as e:
609
+ logging.error(f"处理请求时出错: {str(e)}")
610
+ return generate_response_xml(
611
+ message_data['from_user'],
612
+ message_data['to_user'],
613
+ append_status_message('抱歉,系统暂时出现问题,请稍后重试。'),
614
+ encrypt_type if 'encrypt_type' in locals() else ''
615
+ )
616
+
617
+ def cleanup_sessions():
618
+ while True:
619
+ time.sleep(3600) # 每小时清理一次
620
+ try:
621
+ session_manager.cleanup_expired_sessions()
622
+ except Exception as e:
623
+ logging.error(f"清理会话时出错: {str(e)}")
624
+
625
+ if __name__ == '__main__':
626
+ cleanup_thread = threading.Thread(target=cleanup_sessions, daemon=True)
627
+ cleanup_thread.start()
628
+
629
+ app.run(host='0.0.0.0', port=7860, debug=True)