Skip to content

Commit cbd98fd

Browse files
authored
Add files via upload
1 parent 231be3b commit cbd98fd

6 files changed

Lines changed: 1158 additions & 0 deletions

File tree

main.py

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

0 commit comments

Comments
 (0)