hzruoo commited on
Commit
1903ce7
·
verified ·
1 Parent(s): f0364b6

Delete fairies_proxy.py

Browse files
Files changed (1) hide show
  1. fairies_proxy.py +0 -713
fairies_proxy.py DELETED
@@ -1,713 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Fairies AI to OpenAI API Proxy v2.0
4
- 支持本地API密钥验证、模型列表、WebSocket连接池和流式响应
5
- """
6
-
7
- import asyncio
8
- import json
9
- import os
10
- import signal
11
- import time
12
- import uuid
13
- import websockets
14
- import requests
15
- from flask import Flask, request, jsonify, Response
16
- from waitress import serve
17
- from datetime import datetime
18
- from typing import Dict, List, Optional, Any
19
- import threading
20
-
21
- # 配置信息
22
- FAIRIES_COOKIE_STRING = os.getenv('FAIRIES_COOKIE_STRING', '')
23
- LOCAL_API_KEY = os.getenv('FAIRIES_API_KEY', 'sk-fairies-proxy-2025-v2')
24
-
25
- # 支持的模型列表
26
- AVAILABLE_MODELS = [
27
- {"id": "claude-sonnet-4-20250514", "object": "model", "created": int(time.time()), "owned_by": "anthropic"},
28
- {"id": "gpt-4.1", "object": "model", "created": int(time.time()), "owned_by": "openai"},
29
- {"id": "gemini-2.5-flash", "object": "model", "created": int(time.time()), "owned_by": "google"},
30
- {"id": "gemini-2.5-pro", "object": "model", "created": int(time.time()), "owned_by": "google"},
31
- {"id": "gpt-4.1-mini", "object": "model", "created": int(time.time()), "owned_by": "openai"},
32
- {"id": "x-ai/grok-3-mini", "object": "model", "created": int(time.time()), "owned_by": "x-ai"}
33
- ]
34
-
35
- PORT = int(os.getenv('PORT', 7001))
36
- app = Flask(__name__)
37
-
38
- # 验证必要的环境变量
39
- if not FAIRIES_COOKIE_STRING:
40
- print("⚠️ 警告: FAIRIES_COOKIE_STRING 环境变量未设置!")
41
- print(" 请设置: export FAIRIES_COOKIE_STRING='your_cookie_string'")
42
-
43
- print(f"🔧 配置信息:")
44
- print(f" API密钥: {LOCAL_API_KEY}")
45
- print(f" 端口: {PORT}")
46
- print(f" Cookie状态: {'✅ 已设置' if FAIRIES_COOKIE_STRING else '❌ 未设置'}")
47
- print()
48
-
49
- class FairiesAIClient:
50
- def __init__(self, cookie_string):
51
- self.cookie_string = cookie_string
52
- self.api_base = "https://fairies.ai/api"
53
- self.websocket_url = "wss://fairies.ai/api/agent/ws"
54
-
55
- # 预配置的Agent ID(固定不变)
56
- self.PREDEFINED_AGENTS = {
57
- "MAIN": "4f18843d-8823-4151-99e2-00fa87eec890",
58
- "DEEPRESEARCH": "5b84e999-08e5-431f-a188-af22c1a8db2c",
59
- "PERCEPTOR": "69611193-859e-4b5c-b1ee-2d2ea6ab25b2",
60
- "AUTOMATOR": "fc59b81a-5e0a-48e9-b78f-be2b66949d37"
61
- }
62
-
63
- self.headers = {
64
- "Content-Type": "application/json",
65
- "Cookie": cookie_string,
66
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
67
- }
68
-
69
- print(f"🔧 初始化FairiesAI客户端")
70
- print(f" 预配置Agent数量: {len(self.PREDEFINED_AGENTS)}")
71
-
72
- def get_agent_id(self, agent_type="MAIN"):
73
- """获取预配置的Agent ID"""
74
- agent_id = self.PREDEFINED_AGENTS.get(agent_type.upper())
75
- if agent_id:
76
- print(f"✅ 使用预配置Agent: {agent_type} -> {agent_id}")
77
- return agent_id
78
- else:
79
- print(f"⚠️ 未知的Agent类型: {agent_type},使用默认MAIN")
80
- return self.PREDEFINED_AGENTS["MAIN"]
81
-
82
- async def create_websocket_connection(self):
83
- """为每个请求创建独立的WebSocket连接"""
84
- session_token = None
85
- for cookie_part in self.cookie_string.split(';'):
86
- if 'session=' in cookie_part:
87
- session_token = cookie_part.split('session=')[1].strip()
88
- break
89
-
90
- if not session_token:
91
- raise Exception("No session token found in cookies")
92
-
93
- print(f"🔗 创建独立WebSocket连接")
94
-
95
- ws_headers = {
96
- "session-token": session_token,
97
- "Cookie": f"session={session_token}",
98
- "User-Agent": "Altera-Agent/1.0"
99
- }
100
-
101
- try:
102
- # 兼容不同版本的websockets库
103
- try:
104
- # 新版本使用extra_headers
105
- websocket = await asyncio.wait_for(
106
- websockets.connect(
107
- self.websocket_url,
108
- extra_headers=ws_headers,
109
- ping_interval=30, # 30秒ping间隔
110
- ping_timeout=10, # 10秒ping超时
111
- close_timeout=10 # 10秒关闭超时
112
- ),
113
- timeout=30.0 # 连接超时30秒
114
- )
115
- except TypeError:
116
- # 旧版本使用additional_headers
117
- websocket = await asyncio.wait_for(
118
- websockets.connect(
119
- self.websocket_url,
120
- additional_headers=ws_headers,
121
- ping_interval=30, # 30秒ping间隔
122
- ping_timeout=10, # 10秒ping超时
123
- close_timeout=10 # 10秒关闭超时
124
- ),
125
- timeout=30.0 # 连接超时30秒
126
- )
127
-
128
- print("✅ WebSocket连接成功")
129
- return websocket
130
- except asyncio.TimeoutError:
131
- print(f"❌ WebSocket连接超时")
132
- raise Exception("WebSocket connection timeout")
133
- except websockets.exceptions.InvalidStatusCode as e:
134
- if e.status_code in [401, 403]:
135
- print(f"❌ WebSocket认证失败: {e.status_code}")
136
- print("💡 提示: 请检查FAIRIES_COOKIE_STRING是否有效")
137
- raise Exception(f"Authentication failed: {e.status_code}")
138
- elif e.status_code == 429:
139
- print(f"❌ WebSocket连接频率限制: {e.status_code}")
140
- raise Exception(f"Rate limit exceeded: {e.status_code}")
141
- elif e.status_code >= 500:
142
- print(f"❌ WebSocket服务器错误: {e.status_code}")
143
- raise Exception(f"Server error: {e.status_code}")
144
- else:
145
- print(f"❌ WebSocket连接失败,状态码: {e.status_code}")
146
- raise Exception(f"WebSocket connection failed: {e.status_code}")
147
- except Exception as e:
148
- print(f"❌ WebSocket连接失败: {str(e)}")
149
- raise
150
-
151
- async def chat_stream(self, agent_id, query, model="claude-sonnet-4-20250514"):
152
- """异步流式聊天方法 - 每个请求使用独立连接"""
153
- websocket = None
154
- try:
155
- websocket = await self.create_websocket_connection()
156
-
157
- # 使用昨天能工作的消息格式
158
- user_message = {
159
- "type": "USER_MESSAGE",
160
- "agent_id": agent_id,
161
- "observation": {
162
- "message": {
163
- "user_query": query,
164
- "mode": "main",
165
- "files": [],
166
- "memories": [],
167
- "model": model,
168
- "task_id": None,
169
- "workspace_path": "/"
170
- }
171
- }
172
- }
173
-
174
- await websocket.send(json.dumps(user_message))
175
- print("📤 消息已发送")
176
-
177
- is_assistant_turn = False
178
- delta_count = 0
179
- response_completed = False
180
-
181
- try:
182
- # 使用昨天能工作的消息处理逻辑
183
- async for message in websocket:
184
- try:
185
- try:
186
- inner_json_string = json.loads(message)
187
- parsed_message = json.loads(inner_json_string)
188
- except (json.JSONDecodeError, TypeError):
189
- parsed_message = json.loads(message)
190
-
191
- msg_type = parsed_message.get("type")
192
-
193
- if msg_type == "CHAT_MESSAGE":
194
- inner_msg = parsed_message.get("message", {}).get("message", {})
195
- inner_type = inner_msg.get("type")
196
- role = inner_msg.get("item", {}).get("role")
197
-
198
- if inner_type == "response.output_item.added" and role == "assistant":
199
- is_assistant_turn = True
200
- print("🤖 AI开始回复...")
201
-
202
- elif is_assistant_turn and inner_type == "response.output_text.delta":
203
- delta_text = inner_msg.get("delta", "")
204
- if delta_text:
205
- delta_count += 1
206
- yield {"delta": delta_text}
207
-
208
- elif inner_type == "response.output_item.done" and role == "assistant":
209
- is_assistant_turn = False
210
- response_completed = True
211
- print(f"✅ AI回复完成,共收到 {delta_count} 个delta")
212
- yield {"done": True}
213
- break
214
-
215
- # 处理任务相关消息
216
- elif msg_type == "TASK_UPDATE":
217
- task_data = parsed_message.get("task", {})
218
- print(f"📋 任务更新: {task_data.get('status', 'unknown')}")
219
-
220
- except Exception as e:
221
- print(f"⚠️ 处理消息时出错: {e}")
222
- continue
223
-
224
- except asyncio.TimeoutError:
225
- print("⚠️ WebSocket消息接收超时")
226
- yield {"error": "Message receive timeout"}
227
- except websockets.exceptions.ConnectionClosed:
228
- if not response_completed:
229
- print("⚠️ WebSocket连接意外关闭")
230
- yield {"error": "Connection closed unexpectedly"}
231
- else:
232
- print("✅ WebSocket连接正常关闭")
233
-
234
- except Exception as e:
235
- print(f"❌ WebSocket通信失败: {str(e)}")
236
- yield {"error": str(e)}
237
- finally:
238
- # 确保WebSocket连接正确关闭
239
- if websocket:
240
- try:
241
- await websocket.close()
242
- print("🔒 WebSocket连接已关闭")
243
- except Exception as e:
244
- print(f"⚠️ 关闭WebSocket连接时出错: {e}")
245
-
246
- async def close_websocket_pool(self):
247
- """关闭所有WebSocket连接"""
248
- # 移除WebSocket连接池的清理逻辑
249
- print("🔒 移除WebSocket连接池的清理逻辑")
250
-
251
- def list_tasks(self):
252
- """获取任务列表"""
253
- try:
254
- response = requests.get(f"{self.api_base}/workflow/tasks", headers=self.headers, timeout=10)
255
- response.raise_for_status()
256
- tasks = response.json()
257
- print(f"📋 获取到 {len(tasks)} 个任务")
258
- return tasks
259
- except Exception as e:
260
- print(f"❌ 获取任务列表失败: {e}")
261
- return []
262
-
263
- def update_task_status(self, task_id, status):
264
- """更新任务状态"""
265
- try:
266
- payload = {"status": status}
267
- response = requests.put(f"{self.api_base}/workflow/tasks/{task_id}",
268
- headers=self.headers, json=payload)
269
- response.raise_for_status()
270
- print(f"✅ 任务 {task_id} 状态更新为: {status}")
271
- return True
272
- except Exception as e:
273
- print(f"❌ 更新任务状态失败: {e}")
274
- return False
275
-
276
- def delete_task(self, task_id):
277
- """删除任务"""
278
- try:
279
- response = requests.delete(f"{self.api_base}/workflow/tasks/{task_id}", headers=self.headers)
280
- response.raise_for_status()
281
- print(f"🗑️ 任务 {task_id} 已删除")
282
- return True
283
- except Exception as e:
284
- print(f"❌ 删除任务失败: {e}")
285
- return False
286
-
287
- def validate_connection(self):
288
- """验证连接配置"""
289
- issues = []
290
-
291
- # 检查Cookie
292
- if not self.cookie_string:
293
- issues.append("FAIRIES_COOKIE_STRING未设置")
294
- else:
295
- # 检查session token
296
- session_token = None
297
- for cookie_part in self.cookie_string.split(';'):
298
- if 'session=' in cookie_part:
299
- session_token = cookie_part.split('session=')[1].strip()
300
- break
301
- if not session_token:
302
- issues.append("Cookie中未找到有效的session token")
303
-
304
- if issues:
305
- print("⚠️ 连接配置问题:")
306
- for issue in issues:
307
- print(f" - {issue}")
308
- return False
309
- else:
310
- print("✅ 连接配置验证通过")
311
- return True
312
-
313
- async def health_check(self):
314
- """健康检查"""
315
- try:
316
- # 测试WebSocket连接
317
- websocket = await self.create_websocket_connection()
318
- await websocket.ping()
319
- await websocket.close() # 立即关闭测试连接
320
- print("✅ WebSocket连接健康")
321
- return {"websocket": "healthy", "status": "ok"}
322
- except Exception as e:
323
- print(f"❌ WebSocket连接不健康: {e}")
324
- return {"websocket": "unhealthy", "status": "error", "error": str(e)}
325
-
326
- # 全局客户端实例
327
- fairies_client = FairiesAIClient(FAIRIES_COOKIE_STRING)
328
-
329
- def verify_api_key():
330
- """验证API密钥"""
331
- auth_header = request.headers.get('Authorization')
332
- if not auth_header:
333
- return False, jsonify({"error": {"message": "Authorization header is required", "type": "invalid_request_error"}}), 401
334
-
335
- if not auth_header.startswith('Bearer '):
336
- return False, jsonify({"error": {"message": "Invalid authorization header format", "type": "invalid_request_error"}}), 401
337
-
338
- api_key = auth_header.split(' ')[1]
339
- if api_key != LOCAL_API_KEY:
340
- return False, jsonify({"error": {"message": "Invalid API key", "type": "invalid_request_error"}}), 401
341
-
342
- return True, None, None
343
-
344
- @app.route('/v1/models', methods=['GET'])
345
- def list_models():
346
- """返回可用模型列表"""
347
- is_valid, error_response, status_code = verify_api_key()
348
- if not is_valid:
349
- return error_response, status_code
350
-
351
- return jsonify({
352
- "object": "list",
353
- "data": AVAILABLE_MODELS
354
- })
355
-
356
- @app.route('/v1/chat/completions', methods=['POST'])
357
- def chat_completions():
358
- """OpenAI兼容的聊天完成接口"""
359
- is_valid, error_response, status_code = verify_api_key()
360
- if not is_valid:
361
- return error_response, status_code
362
-
363
- data = request.get_json()
364
- if not data:
365
- return jsonify({"error": {"message": "Request body is required", "type": "invalid_request_error"}}), 400
366
-
367
- messages = data.get('messages', [])
368
- stream = data.get('stream', False)
369
- model = data.get('model', 'claude-sonnet-4-20250514')
370
-
371
- if not messages:
372
- return jsonify({"error": {"message": "messages field is required", "type": "invalid_request_error"}}), 400
373
-
374
- # 提取最后一条用户消息
375
- user_message = None
376
- for msg in reversed(messages):
377
- if msg.get('role') == 'user':
378
- user_message = msg.get('content')
379
- break
380
-
381
- if not user_message:
382
- return jsonify({"error": {"message": "No user message found", "type": "invalid_request_error"}}), 400
383
-
384
- print(f"👤 用户消息: {user_message}")
385
- print(f"📡 流式模式: {stream}")
386
- print(f"🎯 模型: {model}")
387
-
388
- try:
389
- agent_id = fairies_client.get_agent_id()
390
-
391
- if not agent_id:
392
- return jsonify({"error": {"message": "Failed to get agent ID", "type": "internal_server_error"}}), 500
393
-
394
- if stream:
395
- def generate_stream():
396
- loop = asyncio.new_event_loop()
397
- asyncio.set_event_loop(loop)
398
-
399
- try:
400
- async def stream_generator():
401
- async_gen = fairies_client.chat_stream(agent_id, user_message, model)
402
- try:
403
- async for chunk in async_gen:
404
- if "error" in chunk:
405
- yield f"data: {json.dumps({'error': chunk['error']})}\n\n"
406
- return
407
- elif "delta" in chunk:
408
- delta_response = {
409
- "id": f"chatcmpl-{str(uuid.uuid4())}",
410
- "object": "chat.completion.chunk",
411
- "created": int(time.time()),
412
- "model": model,
413
- "choices": [{
414
- "index": 0,
415
- "delta": {"content": chunk["delta"]},
416
- "finish_reason": None
417
- }]
418
- }
419
- yield f"data: {json.dumps(delta_response)}\n\n"
420
- elif chunk.get("done"):
421
- final_response = {
422
- "id": f"chatcmpl-{str(uuid.uuid4())}",
423
- "object": "chat.completion.chunk",
424
- "created": int(time.time()),
425
- "model": model,
426
- "choices": [{
427
- "index": 0,
428
- "delta": {},
429
- "finish_reason": "stop"
430
- }]
431
- }
432
- yield f"data: {json.dumps(final_response)}\n\n"
433
- yield "data: [DONE]\n\n"
434
- return
435
- except asyncio.CancelledError:
436
- print("⚠️ 流式传输被取消")
437
- raise
438
- except Exception as stream_error:
439
- print(f"⚠️ 流式传输出错: {stream_error}")
440
- yield f"data: {json.dumps({'error': str(stream_error)})}\n\n"
441
- finally:
442
- # 确保异步生成器正确关闭
443
- if hasattr(async_gen, 'aclose'):
444
- await async_gen.aclose()
445
-
446
- # 改进的流式处理
447
- async_gen = stream_generator()
448
- try:
449
- while True:
450
- try:
451
- chunk = loop.run_until_complete(async_gen.__anext__())
452
- yield chunk
453
- except StopAsyncIteration:
454
- break
455
- finally:
456
- # 确保生成器正确关闭
457
- try:
458
- loop.run_until_complete(async_gen.aclose())
459
- except Exception as gen_close_error:
460
- print(f"⚠️ 关闭流生成器时出错: {gen_close_error}")
461
-
462
- finally:
463
- loop.close()
464
-
465
- return Response(generate_stream(), mimetype='text/plain; charset=utf-8')
466
- else:
467
- # 非流式响应
468
- loop = asyncio.new_event_loop()
469
- asyncio.set_event_loop(loop)
470
-
471
- response_content = ""
472
- async def collect_response():
473
- nonlocal response_content
474
- async_gen = fairies_client.chat_stream(agent_id, user_message, model)
475
- try:
476
- async for chunk in async_gen:
477
- if "error" in chunk:
478
- return {"error": chunk["error"]}
479
- elif "delta" in chunk:
480
- response_content += chunk["delta"]
481
- elif chunk.get("done"):
482
- break
483
- finally:
484
- # 确保异步生成器正确关闭
485
- if hasattr(async_gen, 'aclose'):
486
- await async_gen.aclose()
487
- return {"response": response_content}
488
-
489
- result = loop.run_until_complete(collect_response())
490
- loop.close()
491
-
492
- if "error" in result:
493
- return jsonify({"error": {"message": result["error"], "type": "internal_server_error"}}), 500
494
-
495
- response = {
496
- "id": f"chatcmpl-{str(uuid.uuid4())}",
497
- "object": "chat.completion",
498
- "created": int(time.time()),
499
- "model": model,
500
- "choices": [{
501
- "index": 0,
502
- "message": {
503
- "role": "assistant",
504
- "content": response_content
505
- },
506
- "finish_reason": "stop"
507
- }],
508
- "usage": {
509
- "prompt_tokens": len(user_message.split()),
510
- "completion_tokens": len(response_content.split()),
511
- "total_tokens": len(user_message.split()) + len(response_content.split())
512
- }
513
- }
514
-
515
- return jsonify(response)
516
-
517
- except Exception as e:
518
- print(f"❌ 处理请求时出错: {e}")
519
- return jsonify({"error": {"message": str(e), "type": "internal_server_error"}}), 500
520
-
521
- @app.route('/v1/tasks', methods=['GET'])
522
- def get_tasks():
523
- """获取任务列表"""
524
- is_valid, error_response, status_code = verify_api_key()
525
- if not is_valid:
526
- return error_response, status_code
527
-
528
- try:
529
- tasks = fairies_client.list_tasks()
530
- return jsonify({"tasks": tasks})
531
- except Exception as e:
532
- return jsonify({"error": {"message": str(e), "type": "internal_server_error"}}), 500
533
-
534
- @app.route('/v1/tasks/<task_id>/status', methods=['PUT'])
535
- def set_task_status(task_id):
536
- """更新任务状态"""
537
- is_valid, error_response, status_code = verify_api_key()
538
- if not is_valid:
539
- return error_response, status_code
540
-
541
- data = request.get_json()
542
- if not data or 'status' not in data:
543
- return jsonify({"error": {"message": "status field is required", "type": "invalid_request_error"}}), 400
544
-
545
- try:
546
- success = fairies_client.update_task_status(task_id, data['status'])
547
- if success:
548
- return jsonify({"success": True, "message": f"Task {task_id} status updated"})
549
- else:
550
- return jsonify({"error": {"message": "Failed to update task status", "type": "internal_server_error"}}), 500
551
- except Exception as e:
552
- return jsonify({"error": {"message": str(e), "type": "internal_server_error"}}), 500
553
-
554
- @app.route('/v1/tasks/<task_id>', methods=['DELETE'])
555
- def remove_task(task_id):
556
- """删除任务"""
557
- is_valid, error_response, status_code = verify_api_key()
558
- if not is_valid:
559
- return error_response, status_code
560
-
561
- try:
562
- success = fairies_client.delete_task(task_id)
563
- if success:
564
- return jsonify({"success": True, "message": f"Task {task_id} deleted"})
565
- else:
566
- return jsonify({"error": {"message": "Failed to delete task", "type": "internal_server_error"}}), 500
567
- except Exception as e:
568
- return jsonify({"error": {"message": str(e), "type": "internal_server_error"}}), 500
569
-
570
- @app.route('/v1/pool/status', methods=['GET'])
571
- def pool_status():
572
- """获取连接池状态"""
573
- is_valid, error_response, status_code = verify_api_key()
574
- if not is_valid:
575
- return error_response, status_code
576
-
577
- # 移除WebSocket连接池的清理逻辑
578
- pool_info = {
579
- "active_connections": 0,
580
- "last_cleanup": 0,
581
- "cleanup_interval": 0,
582
- "next_cleanup_in": 0,
583
- "connections": []
584
- }
585
-
586
- return jsonify(pool_info)
587
-
588
- @app.route('/v1/pool/cleanup', methods=['POST'])
589
- def manual_cleanup():
590
- """手动清理连接池"""
591
- is_valid, error_response, status_code = verify_api_key()
592
- if not is_valid:
593
- return error_response, status_code
594
-
595
- try:
596
- loop = asyncio.new_event_loop()
597
- asyncio.set_event_loop(loop)
598
- # 移除WebSocket连接池的清理逻��
599
- loop.close()
600
- return jsonify({"success": True, "message": "Connection pool cleanup completed"})
601
- except Exception as e:
602
- return jsonify({"error": {"message": str(e), "type": "internal_server_error"}}), 500
603
-
604
- @app.route('/health', methods=['GET'])
605
- def health_check():
606
- """健康检查"""
607
- try:
608
- # 检查连接池状态
609
- # 移除WebSocket连接池的清理逻辑
610
- pool_size = 0
611
-
612
- # 检查Cookie配置
613
- cookie_status = "✅ 已设置" if FAIRIES_COOKIE_STRING else "❌ 未设置"
614
-
615
- health_info = {
616
- "status": "healthy",
617
- "service": "Fairies AI OpenAI-Compatible Proxy",
618
- "version": "2.0",
619
- "websocket_pool_size": pool_size,
620
- "cookie_status": cookie_status,
621
- "timestamp": datetime.now().isoformat()
622
- }
623
- return jsonify(health_info)
624
- except Exception as e:
625
- return jsonify({
626
- "status": "unhealthy",
627
- "error": str(e),
628
- "timestamp": datetime.now().isoformat()
629
- }), 500
630
-
631
- @app.route('/', methods=['GET'])
632
- def api_info():
633
- return jsonify({
634
- "service": "Fairies AI OpenAI-Compatible Proxy v2.0",
635
- "base_url": f"http://localhost:{PORT}",
636
- "endpoints": [
637
- "/v1/chat/completions",
638
- "/v1/models",
639
- "/v1/tasks",
640
- "/health"
641
- ],
642
- "features": [
643
- "OpenAI兼容的聊天接口",
644
- "智能WebSocket连接池管理",
645
- "流式响应支持",
646
- "预配置Agent ID",
647
- "完善的错误处理机制"
648
- ]
649
- })
650
-
651
- def cleanup_on_exit():
652
- """程序退出时的清理函数"""
653
- print("\n🔧 正在清理资源...")
654
- try:
655
- # 移除WebSocket连接池的清理逻辑
656
- print("✅ 资源清理完成 (无WebSocket连接池需要清理)")
657
- except Exception as e:
658
- print(f"⚠️ 清理资源时出错: {e}")
659
-
660
- def signal_handler(signum, frame):
661
- """信号处理函数"""
662
- print(f"\n📡 收到信号 {signum},正在关闭服务...")
663
- cleanup_on_exit()
664
- import sys
665
- sys.exit(0)
666
-
667
- if __name__ == '__main__':
668
- # 注册清理函数和信号处理
669
- import atexit
670
- atexit.register(cleanup_on_exit)
671
- signal.signal(signal.SIGINT, signal_handler)
672
- signal.signal(signal.SIGTERM, signal_handler)
673
-
674
- print("🚀 Fairies AI OpenAI-Compatible Proxy v2.0 启动中...")
675
- print(f"📋 服务地址: http://localhost:{PORT}")
676
- print(f"🔑 API密钥: {LOCAL_API_KEY}")
677
- print(f"📦 支持模型: {len(AVAILABLE_MODELS)} 个")
678
- print()
679
-
680
- # 验证连接配置
681
- print("🔍 验证连接配置...")
682
- if not fairies_client.validate_connection():
683
- print("⚠️ 配置验证失败,服务可能无法正常工作")
684
- print("💡 请设置正确的环境变量后重启服务")
685
- print()
686
-
687
- print("🔧 测试命令:")
688
- print(f"curl -X POST http://localhost:{PORT}/v1/chat/completions \\")
689
- print(f" -H 'Authorization: Bearer {LOCAL_API_KEY}' \\")
690
- print(" -H 'Content-Type: application/json' \\")
691
- print(" -d '{\"messages\": [{\"role\": \"user\", \"content\": \"你好\"}]}'")
692
- print()
693
- print("📋 任务管理:")
694
- print(f"curl -X GET http://localhost:{PORT}/v1/tasks \\")
695
- print(f" -H 'Authorization: Bearer {LOCAL_API_KEY}'")
696
- print()
697
- print("🏥 健康检查:")
698
- print(f"curl -X GET http://localhost:{PORT}/health")
699
- print()
700
-
701
- try:
702
- print("🚀 使用Waitress服务器启动...")
703
- serve(app, host='0.0.0.0', port=PORT)
704
- except ImportError:
705
- # 如果没有安装waitress,回退到Flask开发服务器
706
- print("⚠️ 未找到Waitress,使用Flask开发服务器启动...")
707
- app.run(host='0.0.0.0', port=PORT, debug=False)
708
- except KeyboardInterrupt:
709
- print("\n👋 服务被用户中断")
710
- except Exception as e:
711
- print(f"\n❌ 服务运行出错: {e}")
712
- finally:
713
- cleanup_on_exit()