-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
386 lines (318 loc) · 15.1 KB
/
main.py
File metadata and controls
386 lines (318 loc) · 15.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
from flask import Flask, request, jsonify
from flask_cors import CORS
import json
import time
from datetime import datetime
from server import MessageServer
import uuid
import threading
import secrets
import string
import os
import logging
from logging.handlers import RotatingFileHandler
app = Flask(__name__)
CORS(app)
# 配置日志系统
def setup_logging():
"""设置应用日志配置"""
# 创建日志目录
if not os.path.exists('logs'):
os.makedirs('logs')
# 配置日志格式
log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
# 配置根日志记录器
logging.basicConfig(
level=logging.INFO,
format=log_format,
handlers=[
# 控制台输出
logging.StreamHandler(),
# 文件输出(带轮转)
RotatingFileHandler(
'logs/simhoshino_api.log',
maxBytes=10*1024*1024, # 10MB
backupCount=5,
encoding='utf-8'
)
]
)
# 设置Flask应用的日志级别
app.logger.setLevel(logging.INFO)
return logging.getLogger('SimHoshino')
# 初始化日志系统
logger = setup_logging()
# 初始化消息服务器实例
message_server = MessageServer()
def generate_api_key():
"""生成安全的API密钥"""
# 生成32位的随机字符串,包含字母和数字
alphabet = string.ascii_letters + string.digits
api_key = 'sk-' + ''.join(secrets.choice(alphabet) for _ in range(48))
return api_key
def load_or_create_api_key():
"""加载已保存的API密钥,如果不存在则生成新的并保存"""
key_file = "api_key.txt"
# 检查密钥文件是否存在
if os.path.exists(key_file):
try:
with open(key_file, 'r', encoding='utf-8') as f:
api_key = f.read().strip()
# 验证密钥格式是否正确
if api_key.startswith('sk-') and len(api_key) == 51:
print(f"🔑 使用已保存的API密钥")
return api_key
else:
print("⚠️ 已保存的API密钥格式不正确,将生成新密钥")
except Exception as e:
print(f"⚠️ 读取API密钥文件失败: {e},将生成新密钥")
# 生成新的API密钥
print("🆕 生成新的API密钥...")
api_key = generate_api_key()
# 保存到文件
try:
with open(key_file, 'w', encoding='utf-8') as f:
f.write(api_key)
print(f"💾 API密钥已保存到 {key_file}")
except Exception as e:
print(f"⚠️ 保存API密钥失败: {e}")
return api_key
class OpenAIAPIServer:
def __init__(self):
self.model_name = "SimHoshino-agent"
self.conversations = {}
def format_openai_response(self, content, model="SimHoshino-agent"):
"""格式化为OpenAI API响应格式"""
return {
"id": f"chatcmpl-{uuid.uuid4().hex[:8]}",
"object": "chat.completion",
"created": int(time.time()),
"model": model,
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": content
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 0,
"completion_tokens": len(content.split()),
"total_tokens": len(content.split())
}
}
def format_stream_response(self, content, model="SimHoshino-agent"):
"""格式化为流式响应"""
chat_id = f"chatcmpl-{uuid.uuid4().hex[:8]}"
timestamp = int(time.time())
# 开始响应
yield f"data: {json.dumps({'id': chat_id, 'object': 'chat.completion.chunk', 'created': timestamp, 'model': model, 'choices': [{'index': 0, 'delta': {'role': 'assistant'}, 'finish_reason': None}]})}\n\n"
# 内容响应
yield f"data: {json.dumps({'id': chat_id, 'object': 'chat.completion.chunk', 'created': timestamp, 'model': model, 'choices': [{'index': 0, 'delta': {'content': content}, 'finish_reason': None}]})}\n\n"
# 结束响应
yield f"data: {json.dumps({'id': chat_id, 'object': 'chat.completion.chunk', 'created': timestamp, 'model': model, 'choices': [{'index': 0, 'delta': {}, 'finish_reason': 'stop'}]})}\n\n"
yield "data: [DONE]\n\n"
# 创建API服务器实例
api_server = OpenAIAPIServer()
@app.route('/v1/chat/completions', methods=['POST'])
def chat_completions():
"""OpenAI兼容的聊天完成API"""
request_id = uuid.uuid4().hex[:8]
client_ip = request.remote_addr
# 记录请求开始
logger.info(f"[{request_id}] 新的聊天请求 - 客户端IP: {client_ip}")
try:
data = request.get_json()
logger.debug(f"[{request_id}] 请求数据: {json.dumps(data, ensure_ascii=False)}")
# 验证必需字段
if not data or 'messages' not in data:
error_msg = "Missing required field: messages"
logger.warning(f"[{request_id}] 请求验证失败: {error_msg}")
return jsonify({"error": {"message": error_msg, "type": "invalid_request_error"}}), 400
messages = data['messages']
model = data.get('model', 'SimHoshino-agent')
stream = data.get('stream', False)
logger.info(f"[{request_id}] 请求参数 - 模型: {model}, 流式: {stream}, 消息数量: {len(messages)}")
# 获取最后一条用户消息
user_message = None
for msg in reversed(messages):
if msg.get('role') == 'user':
user_message = msg.get('content', '')
break
if not user_message:
error_msg = "No user message found"
logger.warning(f"[{request_id}] 未找到用户消息")
return jsonify({"error": {"message": error_msg, "type": "invalid_request_error"}}), 400
logger.info(f"[{request_id}] 收到用户消息: {user_message}")
print(f"📨 收到用户消息: {user_message}")
# 发送消息到智能体
logger.info(f"[{request_id}] 开始发送消息到智能体")
success = message_server.send_message_to_chat(user_message)
if not success:
error_msg = "Failed to send message to agent"
logger.error(f"[{request_id}] 消息发送失败: {error_msg}")
logger.debug(f"[{request_id}] 发送失败详细信息 - 用户消息: {repr(user_message)}")
# 详细调试信息已记录到日志
# 尝试获取更多调试信息
try:
if hasattr(message_server, 'get_connection_status'):
status = message_server.get_connection_status()
logger.debug(f"[{request_id}] 连接状态: {status}")
print(f" - 连接状态: {status}")
if hasattr(message_server, 'last_error'):
logger.debug(f"[{request_id}] 最后错误: {message_server.last_error}")
print(f" - 最后错误: {message_server.last_error}")
except Exception as debug_e:
logger.debug(f"[{request_id}] 获取调试信息时出错: {debug_e}")
return jsonify({"error": {"message": error_msg, "type": "internal_server_error"}}), 500
logger.info(f"[{request_id}] 消息发送成功,等待智能体回复...")
# 等待一段时间让智能体处理
time.sleep(3)
# 获取智能体回复
logger.info(f"[{request_id}] 开始获取智能体回复")
previous_msg, at_msg = message_server.extract_at_messages()
if at_msg and previous_msg:
agent_name = previous_msg.strip()
logger.info(f"[{request_id}] 检测到智能体: {agent_name}")
print(f"🔍 检测到智能体: {agent_name}")
agent_response = message_server.get_agent_previous_message(agent_name)
if agent_response:
logger.info(f"[{request_id}] 获取到智能体回复 - 长度: {len(agent_response)}字符")
logger.debug(f"[{request_id}] 智能体回复内容: {agent_response}")
if stream:
logger.info(f"[{request_id}] 返回流式响应")
return app.response_class(
api_server.format_stream_response(agent_response, model),
mimetype='text/plain'
)
else:
logger.info(f"[{request_id}] 返回标准响应")
return jsonify(api_server.format_openai_response(agent_response, model))
else:
error_msg = f"智能体 {agent_name} 暂未回复,请稍后重试"
logger.warning(f"[{request_id}] 智能体未回复: {error_msg}")
logger.debug(f"[{request_id}] 智能体详细信息 - 名称: {agent_name}, previous_msg: {repr(previous_msg)}, at_msg: {repr(at_msg)}")
# 详细调试信息已记录到日志
else:
error_msg = "未检测到智能体回复"
logger.warning(f"[{request_id}] 未检测到智能体回复")
logger.debug(f"[{request_id}] extract_at_messages返回值 - previous_msg: {repr(previous_msg)}, at_msg: {repr(at_msg)}")
# 详细调试信息已记录到日志
# 尝试获取更多调试信息
try:
# 检查消息服务器的状态
print(f" - 消息服务器实例: {message_server}")
print(f" - 消息服务器类型: {type(message_server)}")
# 如果有其他调试方法,也可以调用
if hasattr(message_server, 'get_last_messages'):
last_messages = message_server.get_last_messages()
logger.debug(f"[{request_id}] 最近消息: {last_messages}")
print(f" - 最近消息: {last_messages}")
if hasattr(message_server, 'get_debug_info'):
debug_info = message_server.get_debug_info()
logger.debug(f"[{request_id}] 调试信息: {debug_info}")
except Exception as debug_e:
logger.debug(f"[{request_id}] 获取调试信息时出错: {debug_e}")
logger.error(f"[{request_id}] 最终错误: {error_msg}")
if stream:
logger.info(f"[{request_id}] 返回错误流式响应")
return app.response_class(
api_server.format_stream_response(error_msg, model),
mimetype='text/plain'
)
else:
logger.info(f"[{request_id}] 返回错误标准响应")
return jsonify(api_server.format_openai_response(error_msg, model))
except Exception as e:
import traceback
error_trace = traceback.format_exc()
logger.error(f"[{request_id}] API异常: {str(e)}")
logger.error(f"[{request_id}] 异常堆栈:\n{error_trace}")
logger.debug(f"[{request_id}] 请求数据: {repr(request.get_json())}")
# 详细异常信息已记录到日志
# 导入traceback来获取完整的错误堆栈
print("📋 完整错误堆栈:")
traceback.print_exc()
error_response = {
"error": {
"message": f"Internal server error: {str(e)}",
"type": "internal_server_error"
}
}
logger.info(f"[{request_id}] 返回异常响应")
return jsonify(error_response), 500
@app.route('/v1/models', methods=['GET'])
def list_models():
"""列出可用模型"""
client_ip = request.remote_addr
logger.info(f"模型列表请求 - 客户端IP: {client_ip}")
response = {
"object": "list",
"data": [{
"id": "SimHoshino-agent",
"object": "model",
"created": int(time.time()),
"owned_by": "SimHoshino"
}]
}
logger.debug(f"返回模型列表: {response}")
return jsonify(response)
@app.route('/health', methods=['GET'])
def health_check():
"""健康检查"""
client_ip = request.remote_addr
logger.info(f"健康检查请求 - 客户端IP: {client_ip}")
response = {
"status": "ok",
"timestamp": datetime.now().isoformat(),
"server": "SimHoshino OpenAI API Server"
}
logger.debug(f"健康检查响应: {response}")
return jsonify(response)
@app.route('/', methods=['GET'])
def index():
"""根路径信息"""
client_ip = request.remote_addr
logger.info(f"根路径访问 - 客户端IP: {client_ip}")
response = {
"message": "SimHoshino OpenAI API Server",
"version": "1.0.0",
"endpoints": {
"chat_completions": "/v1/chat/completions",
"models": "/v1/models",
"health": "/health"
},
"documentation": "Compatible with OpenAI API format"
}
logger.debug(f"根路径响应: {response}")
return jsonify(response)
if __name__ == '__main__':
# 只在主进程中显示启动信息,避免调试模式重载时重复显示
if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
# ASCII艺术字
print(r"""
$$$$$$\ $$\ $$\ $$\ $$\ $$\
$$ __$$\ \__| $$ | $$ | $$ | \__|
$$ / \__|$$\ $$$$$$\$$$$\ $$ | $$ | $$$$$$\ $$$$$$$\ $$$$$$$\ $$\ $$$$$$$\ $$$$$$\
\$$$$$$\ $$ |$$ _$$ _$$\ $$$$$$$$ |$$ __$$\ $$ _____|$$ __$$\ $$ |$$ __$$\ $$ __$$\
\____$$\ $$ |$$ / $$ / $$ |$$ __$$ |$$ / $$ |\$$$$$$\ $$ | $$ |$$ |$$ | $$ |$$ / $$ |
$$\ $$ |$$ |$$ | $$ | $$ |$$ | $$ |$$ | $$ | \____$$\ $$ | $$ |$$ |$$ | $$ |$$ | $$ |
\$$$$$$ |$$ |$$ | $$ | $$ |$$ | $$ |\$$$$$$ |$$$$$$$ |$$ | $$ |$$ |$$ | $$ |\$$$$$$ |
\______/ \__|\__| \__| \__|\__| \__| \______/ \_______/ \__| \__|\__|\__| \__| \______/
""")
# 加载或生成API密钥
logger.info("应用程序启动开始")
api_key = load_or_create_api_key()
logger.info(f"API密钥已准备就绪: {api_key[:12]}...")
logger.info("SimHoshino OpenAI API服务器启动中...")
print("🚀 启动SimHoshino OpenAI API服务器...")
print("📡 服务器地址: http://localhost:5000")
print("🔗 API端点: http://localhost:5000/v1/chat/completions")
print("📋 模型列表: http://localhost:5000/v1/models")
print("❤️ 健康检查: http://localhost:5000/health")
print("="*60)
print(f"🔑 API密钥: {api_key}")
print("="*60)
logger.info("服务器即将在端口5000上启动")
app.run(host='0.0.0.0', port=5000, debug=True)