sensitive_check_server.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. 敏感词审查 — 前端测试服务器
  5. 提供独立 HTTP API,直接调用 check_sensitive(AC 自动机 + LLM 二审)
  6. """
  7. import sys
  8. import os
  9. import json
  10. import time
  11. import asyncio
  12. from http.server import HTTPServer, SimpleHTTPRequestHandler
  13. from urllib.parse import urlparse
  14. PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  15. os.chdir(PROJECT_ROOT)
  16. from foundation.observability.logger.loggering import review_logger as logger
  17. from core.construction_review.component.reviewers.utils import (
  18. check_sensitive_words_async,
  19. SensitiveWordChecker,
  20. )
  21. from core.construction_review.component.ai_review_engine import AIReviewEngine
  22. from core.base.task_models import TaskFileInfo
  23. def run_async(coro):
  24. """在同步上下文中运行异步协程"""
  25. try:
  26. loop = asyncio.get_running_loop()
  27. import concurrent.futures
  28. with concurrent.futures.ThreadPoolExecutor() as executor:
  29. future = executor.submit(asyncio.run, coro)
  30. return future.result()
  31. except RuntimeError:
  32. return asyncio.run(coro)
  33. # 初始化引擎和检测器
  34. _ai_engine = None
  35. def _get_ai_engine():
  36. global _ai_engine
  37. if _ai_engine is None:
  38. task_info = TaskFileInfo(
  39. file_info={
  40. "file_id": "test_sensitive",
  41. "callback_task_id": "test_task",
  42. "user_id": "test_user",
  43. "review_config": ["sensitive_check"],
  44. }
  45. )
  46. _ai_engine = AIReviewEngine(task_file_info=task_info)
  47. return _ai_engine
  48. async def do_sensitive_check(review_content: str) -> dict:
  49. """执行敏感词检查(AC 自动机 + LLM 二审)"""
  50. trace_id = f"sensitive_check_web_{int(time.time() * 1000)}"
  51. engine = _get_ai_engine()
  52. logger.info(f"[敏感词Web测试] trace_id={trace_id}, content_length={len(review_content)}")
  53. start = time.time()
  54. result = await engine.check_sensitive(
  55. trace_id_idx=f"_web_{int(start * 1000)}",
  56. review_content=review_content,
  57. state=None,
  58. stage_name=None,
  59. )
  60. wall_time = time.time() - start
  61. return {
  62. "trace_id": trace_id,
  63. "success": result.success,
  64. "details": result.details,
  65. "error_message": result.error_message,
  66. "model_execution_time": result.execution_time,
  67. "wall_time": round(wall_time, 3),
  68. "content_length": len(review_content),
  69. }
  70. class SensitiveCheckHandler(SimpleHTTPRequestHandler):
  71. """HTTP请求处理器"""
  72. def do_GET(self):
  73. parsed = urlparse(self.path)
  74. if parsed.path == '/api/health':
  75. self.send_json_response({"status": "ok"})
  76. elif parsed.path in ('', '/', '/index.html'):
  77. index_path = os.path.join(os.path.dirname(__file__), 'sensitive_check_test.html')
  78. self.serve_file(index_path, 'text/html')
  79. else:
  80. super().do_GET()
  81. def do_POST(self):
  82. parsed = urlparse(self.path)
  83. if parsed.path == '/api/sensitive_check':
  84. content_length = int(self.headers.get('Content-Length', 0))
  85. post_data = self.rfile.read(content_length)
  86. try:
  87. body = json.loads(post_data.decode('utf-8'))
  88. review_content = body.get('content', '')
  89. if not review_content:
  90. self.send_json_response({"error": "请提供 content 参数"}, 400)
  91. return
  92. print(f"\n[敏感词Web测试] 收到请求, content_length={len(review_content)}")
  93. result = run_async(do_sensitive_check(review_content))
  94. print(f"[敏感词Web测试] 完成, success={result['success']}, wall_time={result['wall_time']}s")
  95. self.send_json_response(result)
  96. except json.JSONDecodeError:
  97. self.send_json_response({"error": "JSON解析失败"}, 400)
  98. except Exception as e:
  99. logger.error(f"[敏感词Web测试] 处理失败: {e}", exc_info=True)
  100. self.send_json_response({"error": str(e)}, 500)
  101. else:
  102. self.send_json_response({"error": "Not Found"}, 404)
  103. def do_OPTIONS(self):
  104. self.send_response(200)
  105. self.send_header('Access-Control-Allow-Origin', '*')
  106. self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
  107. self.send_header('Access-Control-Allow-Headers', 'Content-Type')
  108. self.end_headers()
  109. def send_json_response(self, data, status=200):
  110. self.send_response(status)
  111. self.send_header('Content-Type', 'application/json; charset=utf-8')
  112. self.send_header('Access-Control-Allow-Origin', '*')
  113. self.end_headers()
  114. self.wfile.write(json.dumps(data, ensure_ascii=False, indent=2).encode('utf-8'))
  115. def serve_file(self, filepath: str, content_type: str):
  116. if os.path.exists(filepath):
  117. self.send_response(200)
  118. self.send_header('Content-Type', f'{content_type}; charset=utf-8')
  119. self.end_headers()
  120. with open(filepath, 'rb') as f:
  121. self.wfile.write(f.read())
  122. else:
  123. self.send_json_response({"error": f"文件不存在: {filepath}"}, 404)
  124. def end_headers(self):
  125. self.send_header('Access-Control-Allow-Origin', '*')
  126. super().end_headers()
  127. def run_server(port=8021):
  128. # 初始化敏感词检测器
  129. try:
  130. stats = SensitiveWordChecker.initialize()
  131. print(f"敏感词检测器初始化完成: {stats}")
  132. except Exception as e:
  133. print(f"敏感词检测器初始化失败: {e}")
  134. server = HTTPServer(('0.0.0.0', port), SensitiveCheckHandler)
  135. print(f"\n{'='*70}")
  136. print(f" 敏感词审查 — 前端测试服务器")
  137. print(f"{'='*70}")
  138. print(f" 访问地址: http://localhost:{port}")
  139. print(f" API端点: POST /api/sensitive_check")
  140. print(f"{'='*70}\n")
  141. server.serve_forever()
  142. if __name__ == "__main__":
  143. import argparse
  144. parser = argparse.ArgumentParser(description='敏感词审查前端测试服务器')
  145. parser.add_argument('--port', type=int, default=8021, help='服务端口 (默认: 8021)')
  146. args = parser.parse_args()
  147. run_server(args.port)