api_server.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. #! /usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # vim:fenc=utf-8
  4. import json
  5. import logging
  6. from argparse import ArgumentParser
  7. import werkzeug.exceptions
  8. from flask import Flask, request, jsonify
  9. from sqlalchemy.orm import sessionmaker
  10. import pqai_agent_server.utils
  11. from pqai_agent import chat_service, prompt_templates
  12. from pqai_agent import configs
  13. from pqai_agent.data_models.agent_configuration import AgentConfiguration
  14. from pqai_agent.data_models.service_module import ServiceModule
  15. from pqai_agent.history_dialogue_service import HistoryDialogueService
  16. from pqai_agent.logging import logger, setup_root_logger
  17. from pqai_agent.toolkit import global_tool_map
  18. from pqai_agent.user_manager import MySQLUserManager, MySQLUserRelationManager
  19. from pqai_agent.utils.db_utils import create_ai_agent_db_engine
  20. from pqai_agent.utils.prompt_utils import format_agent_profile, format_user_profile
  21. from pqai_agent_server.const import AgentApiConst
  22. from pqai_agent_server.const.status_enum import TestTaskStatus
  23. from pqai_agent_server.const.type_enum import EvaluateType
  24. from pqai_agent_server.dataset_service import DatasetService
  25. from pqai_agent_server.models import MySQLSessionManager
  26. from pqai_agent_server.task_server import TaskManager
  27. from pqai_agent_server.utils import (
  28. run_extractor_prompt,
  29. run_chat_prompt,
  30. run_response_type_prompt,
  31. )
  32. from pqai_agent_server.utils import wrap_response
  33. app = Flask('agent_api_server')
  34. const = AgentApiConst()
  35. @app.route('/api/listStaffs', methods=['GET'])
  36. def list_staffs():
  37. staff_data = app.user_relation_manager.list_staffs()
  38. return wrap_response(200, data=staff_data)
  39. @app.route('/api/getStaffProfile', methods=['GET'])
  40. def get_staff_profile():
  41. staff_id = request.args['staff_id']
  42. profile = app.user_manager.get_staff_profile(staff_id)
  43. if not profile:
  44. return wrap_response(404, msg='staff not found')
  45. else:
  46. return wrap_response(200, data=profile)
  47. @app.route('/api/getUserProfile', methods=['GET'])
  48. def get_user_profile():
  49. user_id = request.args['user_id']
  50. profile = app.user_manager.get_user_profile(user_id)
  51. if not profile:
  52. resp = {
  53. 'code': 404,
  54. 'msg': 'user not found'
  55. }
  56. else:
  57. resp = {
  58. 'code': 200,
  59. 'msg': 'success',
  60. 'data': profile
  61. }
  62. return jsonify(resp)
  63. @app.route('/api/listUsers', methods=['GET'])
  64. def list_users():
  65. user_name = request.args.get('user_name', None)
  66. user_union_id = request.args.get('user_union_id', None)
  67. if not user_name and not user_union_id:
  68. resp = {
  69. 'code': 400,
  70. 'msg': 'user_name or user_union_id is required'
  71. }
  72. return jsonify(resp)
  73. data = app.user_manager.list_users(user_name=user_name, user_union_id=user_union_id)
  74. return jsonify({'code': 200, 'data': data})
  75. @app.route('/api/getDialogueHistory', methods=['GET'])
  76. def get_dialogue_history():
  77. staff_id = request.args['staff_id']
  78. user_id = request.args['user_id']
  79. recent_minutes = int(request.args.get('recent_minutes', 1440))
  80. dialogue_history = app.history_dialogue_service.get_dialogue_history(staff_id, user_id, recent_minutes)
  81. return jsonify({'code': 200, 'data': dialogue_history})
  82. @app.route('/api/listModels', methods=['GET'])
  83. def list_models():
  84. models = {
  85. "deepseek-chat": chat_service.VOLCENGINE_MODEL_DEEPSEEK_V3,
  86. "gpt-4o": chat_service.OPENAI_MODEL_GPT_4o,
  87. "gpt-4o-mini": chat_service.OPENAI_MODEL_GPT_4o_mini,
  88. "doubao-pro-32k": chat_service.VOLCENGINE_MODEL_DOUBAO_PRO_32K,
  89. "doubao-pro-1.5": chat_service.VOLCENGINE_MODEL_DOUBAO_PRO_1_5_32K,
  90. "doubao-1.5-vision-pro": chat_service.VOLCENGINE_MODEL_DOUBAO_1_5_VISION_PRO,
  91. }
  92. ret_data = [
  93. {
  94. 'model_type': 'openai_compatible',
  95. 'model_name': model_name,
  96. 'display_name': model_display_name
  97. }
  98. for model_display_name, model_name in models.items()
  99. ]
  100. return wrap_response(200, data=ret_data)
  101. @app.route('/api/listScenes', methods=['GET'])
  102. def list_scenes():
  103. scenes = [
  104. {'scene': 'greeting', 'display_name': '问候'},
  105. {'scene': 'chitchat', 'display_name': '闲聊'},
  106. {'scene': 'profile_extractor', 'display_name': '画像提取'},
  107. {'scene': 'response_type_detector', 'display_name': '回复模态判断'},
  108. {'scene': 'custom_debugging', 'display_name': '自定义调试场景'}
  109. ]
  110. return wrap_response(200, data=scenes)
  111. @app.route('/api/getBasePrompt', methods=['GET'])
  112. def get_base_prompt():
  113. scene = request.args['scene']
  114. prompt_map = {
  115. 'greeting': prompt_templates.GENERAL_GREETING_PROMPT,
  116. 'chitchat': prompt_templates.CHITCHAT_PROMPT_COZE,
  117. 'profile_extractor': prompt_templates.USER_PROFILE_EXTRACT_PROMPT_V2,
  118. 'response_type_detector': prompt_templates.RESPONSE_TYPE_DETECT_PROMPT,
  119. 'custom_debugging': '',
  120. }
  121. model_map = {
  122. 'greeting': chat_service.VOLCENGINE_MODEL_DOUBAO_PRO_32K,
  123. 'chitchat': chat_service.VOLCENGINE_MODEL_DOUBAO_PRO_32K,
  124. 'profile_extractor': chat_service.VOLCENGINE_MODEL_DOUBAO_PRO_1_5_32K,
  125. 'response_type_detector': chat_service.VOLCENGINE_MODEL_DOUBAO_PRO_1_5_32K,
  126. 'custom_debugging': chat_service.VOLCENGINE_BOT_DEEPSEEK_V3_SEARCH
  127. }
  128. if scene not in prompt_map:
  129. return wrap_response(404, msg='scene not found')
  130. data = {
  131. 'model_name': model_map[scene],
  132. 'content': prompt_map[scene]
  133. }
  134. return wrap_response(200, data=data)
  135. @app.route('/api/runPrompt', methods=['POST'])
  136. def run_prompt():
  137. try:
  138. req_data = request.json
  139. logger.debug(req_data)
  140. scene = req_data['scene']
  141. if scene == 'profile_extractor':
  142. response = run_extractor_prompt(req_data)
  143. return wrap_response(200, data=response)
  144. elif scene == 'response_type_detector':
  145. response = run_response_type_prompt(req_data)
  146. return wrap_response(200, data=response.choices[0].message.content)
  147. else:
  148. response = run_chat_prompt(req_data)
  149. return wrap_response(200, data=response.choices[0].message.content)
  150. except Exception as e:
  151. logger.error(e)
  152. return wrap_response(500, msg='Error: {}'.format(e))
  153. @app.route('/api/formatForPrompt', methods=['POST'])
  154. def format_data_for_prompt():
  155. try:
  156. req_data = request.json
  157. content = req_data['content']
  158. format_type = req_data['format_type']
  159. if format_type == 'staff_profile':
  160. if not isinstance(content, dict):
  161. return wrap_response(400, msg='staff_profile should be a dict')
  162. response = format_agent_profile(content)
  163. elif format_type == 'user_profile':
  164. if not isinstance(content, dict):
  165. return wrap_response(400, msg='user_profile should be a dict')
  166. response = format_user_profile(content)
  167. elif format_type == 'dialogue':
  168. if not isinstance(content, list):
  169. return wrap_response(400, msg='dialogue should be a list')
  170. from pqai_agent_server.utils.prompt_util import format_dialogue_history
  171. response = format_dialogue_history(content)
  172. else:
  173. return wrap_response(400, msg='Invalid format_type')
  174. return wrap_response(200, data=response)
  175. except Exception as e:
  176. logger.error(e)
  177. return wrap_response(500, msg='Error: {}'.format(e))
  178. @app.route("/api/healthCheck", methods=["GET"])
  179. def health_check():
  180. return wrap_response(200, msg="OK")
  181. @app.route("/api/getStaffSessionSummary", methods=["GET"])
  182. def get_staff_session_summary():
  183. staff_id = request.args.get("staff_id")
  184. status = request.args.get("status", const.DEFAULT_STAFF_STATUS)
  185. page_id = request.args.get("page_id", const.DEFAULT_PAGE_ID)
  186. page_size = request.args.get("page_size", const.DEFAULT_PAGE_SIZE)
  187. # check params
  188. try:
  189. page_id = int(page_id)
  190. page_size = int(page_size)
  191. status = int(status)
  192. except Exception as e:
  193. return wrap_response(404, msg="Invalid parameter: {}".format(e))
  194. staff_session_summary = app.session_manager.get_staff_sessions_summary(
  195. staff_id, page_id, page_size, status
  196. )
  197. if not staff_session_summary:
  198. return wrap_response(404, msg="staff not found")
  199. else:
  200. return wrap_response(200, data=staff_session_summary)
  201. @app.route("/api/getStaffSessionList", methods=["GET"])
  202. def get_staff_session_list():
  203. staff_id = request.args.get("staff_id")
  204. if not staff_id:
  205. return wrap_response(404, msg="staff_id is required")
  206. page_size = request.args.get("page_size", const.DEFAULT_PAGE_SIZE)
  207. page_id = request.args.get("page_id", const.DEFAULT_PAGE_ID)
  208. # check params
  209. try:
  210. page_id = int(page_id)
  211. page_size = int(page_size)
  212. except Exception as e:
  213. return wrap_response(404, msg="Invalid parameter: {}".format(e))
  214. staff_session_list = app.session_manager.get_staff_session_list(staff_id, page_id, page_size)
  215. if not staff_session_list:
  216. return wrap_response(404, msg="staff not found")
  217. return wrap_response(200, data=staff_session_list)
  218. @app.route("/api/getStaffList", methods=["GET"])
  219. def get_staff_list():
  220. page_size = request.args.get("page_size", const.DEFAULT_PAGE_SIZE)
  221. page_id = request.args.get("page_id", const.DEFAULT_PAGE_ID)
  222. # check params
  223. try:
  224. page_id = int(page_id)
  225. page_size = int(page_size)
  226. except Exception as e:
  227. return wrap_response(404, msg="Invalid parameter: {}".format(e))
  228. staff_list = app.user_manager.get_staff_list(page_id, page_size)
  229. if not staff_list:
  230. return wrap_response(404, msg="staff not found")
  231. return wrap_response(200, data=staff_list)
  232. @app.route("/api/getConversationList", methods=["GET"])
  233. def get_conversation_list():
  234. """
  235. 获取staff && user 私聊对话列表
  236. :return:
  237. """
  238. staff_id = request.args.get("staff_id")
  239. user_id = request.args.get("user_id")
  240. if not staff_id or not user_id:
  241. return wrap_response(404, msg="staff_id and user_id are required")
  242. page = request.args.get("page")
  243. response = app.session_manager.get_conversation_list(staff_id, user_id, page, const.DEFAULT_CONVERSATION_SIZE)
  244. return wrap_response(200, data=response)
  245. @app.route("/api/sendMessage", methods=["POST"])
  246. def send_message():
  247. return wrap_response(200, msg="暂不实现功能")
  248. @app.route("/api/quitHumanIntervention", methods=["POST"])
  249. def quit_human_intervention():
  250. """
  251. 退出人工介入状态
  252. :return:
  253. """
  254. req_data = request.json
  255. staff_id = req_data["staff_id"]
  256. user_id = req_data["user_id"]
  257. if not user_id or not staff_id:
  258. return wrap_response(404, msg="user_id and staff_id are required")
  259. if pqai_agent_server.utils.common.quit_human_intervention(user_id, staff_id):
  260. return wrap_response(200, msg="success")
  261. else:
  262. return wrap_response(500, msg="error")
  263. @app.route("/api/enterHumanIntervention", methods=["POST"])
  264. def enter_human_intervention():
  265. """
  266. 进入人工介入状态
  267. :return:
  268. """
  269. req_data = request.json
  270. staff_id = req_data["staff_id"]
  271. user_id = req_data["user_id"]
  272. if not user_id or not staff_id:
  273. return wrap_response(404, msg="user_id and staff_id are required")
  274. if pqai_agent_server.utils.common.enter_human_intervention(user_id, staff_id):
  275. return wrap_response(200, msg="success")
  276. else:
  277. return wrap_response(500, msg="error")
  278. ## Agent管理接口
  279. @app.route("/api/getNativeAgentList", methods=["GET"])
  280. def get_native_agent_list():
  281. """
  282. 获取所有的Agent列表
  283. :return:
  284. """
  285. page = request.args.get('page', 1)
  286. page_size = request.args.get('page_size', 50)
  287. create_user = request.args.get('create_user', None)
  288. update_user = request.args.get('update_user', None)
  289. offset = (int(page) - 1) * int(page_size)
  290. with app.session_maker() as session:
  291. query = session.query(AgentConfiguration) \
  292. .filter(AgentConfiguration.is_delete == 0)
  293. if create_user:
  294. query = query.filter(AgentConfiguration.create_user == create_user)
  295. if update_user:
  296. query = query.filter(AgentConfiguration.update_user == update_user)
  297. total = query.count()
  298. query = query.offset(offset).limit(int(page_size))
  299. data = query.all()
  300. ret_data = {
  301. 'total': total,
  302. 'agent_list': [
  303. {
  304. 'id': agent.id,
  305. 'name': agent.name,
  306. 'display_name': agent.display_name,
  307. 'type': agent.type,
  308. 'execution_model': agent.execution_model,
  309. 'create_user': agent.create_user,
  310. 'update_user': agent.update_user,
  311. 'create_time': agent.create_time.strftime('%Y-%m-%d %H:%M:%S'),
  312. 'update_time': agent.update_time.strftime('%Y-%m-%d %H:%M:%S')
  313. }
  314. for agent in data
  315. ]
  316. }
  317. return wrap_response(200, data=ret_data)
  318. @app.route("/api/getNativeAgentConfiguration", methods=["GET"])
  319. def get_native_agent_configuration():
  320. """
  321. 获取指定Agent的配置
  322. :return:
  323. """
  324. agent_id = request.args.get('agent_id')
  325. if not agent_id:
  326. return wrap_response(404, msg='agent_id is required')
  327. with app.session_maker() as session:
  328. agent = session.query(AgentConfiguration).filter(AgentConfiguration.id == agent_id).first()
  329. if not agent:
  330. return wrap_response(404, msg='Agent not found')
  331. data = {
  332. 'id': agent.id,
  333. 'name': agent.name,
  334. 'display_name': agent.display_name,
  335. 'type': agent.type,
  336. 'execution_model': agent.execution_model,
  337. 'system_prompt': agent.system_prompt,
  338. 'task_prompt': agent.task_prompt,
  339. 'tools': json.loads(agent.tools),
  340. 'sub_agents': json.loads(agent.sub_agents),
  341. 'extra_params': json.loads(agent.extra_params),
  342. 'create_time': agent.create_time.strftime('%Y-%m-%d %H:%M:%S'),
  343. 'update_time': agent.update_time.strftime('%Y-%m-%d %H:%M:%S')
  344. }
  345. return wrap_response(200, data=data)
  346. @app.route("/api/saveNativeAgentConfiguration", methods=["POST"])
  347. def save_native_agent_configuration():
  348. """
  349. 保存Agent配置
  350. :return:
  351. """
  352. req_data = request.json
  353. agent_id = req_data.get('agent_id', None)
  354. name = req_data.get('name')
  355. display_name = req_data.get('display_name', None)
  356. type_ = req_data.get('type', 0)
  357. execution_model = req_data.get('execution_model', None)
  358. system_prompt = req_data.get('system_prompt', None)
  359. task_prompt = req_data.get('task_prompt', None)
  360. tools = json.dumps(req_data.get('tools', []))
  361. sub_agents = json.dumps(req_data.get('sub_agents', []))
  362. extra_params = req_data.get('extra_params', {})
  363. operate_user = req_data.get('user', None)
  364. if isinstance(extra_params, dict):
  365. extra_params = json.dumps(extra_params)
  366. elif isinstance(extra_params, str):
  367. try:
  368. json.loads(extra_params)
  369. except json.JSONDecodeError:
  370. return wrap_response(400, msg='extra_params should be a valid JSON object or string')
  371. if not extra_params:
  372. extra_params = '{}'
  373. if not name:
  374. return wrap_response(400, msg='name is required')
  375. with app.session_maker() as session:
  376. if agent_id:
  377. agent_id = int(agent_id)
  378. agent = session.query(AgentConfiguration).filter(AgentConfiguration.id == agent_id).first()
  379. if not agent:
  380. return wrap_response(404, msg='Agent not found')
  381. agent.name = name
  382. agent.display_name = display_name
  383. agent.type = type_
  384. agent.execution_model = execution_model
  385. agent.system_prompt = system_prompt
  386. agent.task_prompt = task_prompt
  387. agent.tools = tools
  388. agent.sub_agents = sub_agents
  389. agent.extra_params = extra_params
  390. agent.update_user = operate_user
  391. else:
  392. agent = AgentConfiguration(
  393. name=name,
  394. display_name=display_name,
  395. type=type_,
  396. execution_model=execution_model,
  397. system_prompt=system_prompt,
  398. task_prompt=task_prompt,
  399. tools=tools,
  400. sub_agents=sub_agents,
  401. extra_params=extra_params,
  402. create_user=operate_user,
  403. update_user=operate_user
  404. )
  405. session.add(agent)
  406. session.commit()
  407. return wrap_response(200, msg='Agent configuration saved successfully', data={'id': agent.id})
  408. @app.route("/api/deleteNativeAgentConfiguration", methods=["POST"])
  409. def delete_native_agent_configuration():
  410. """
  411. 删除指定Agent配置(软删除,设置is_delete=1)
  412. :return:
  413. """
  414. req_data = request.json
  415. agent_id = req_data.get('agent_id', None)
  416. if not agent_id:
  417. return wrap_response(400, msg='agent_id is required')
  418. try:
  419. agent_id = int(agent_id)
  420. except ValueError:
  421. return wrap_response(400, msg='agent_id must be an integer')
  422. with app.session_maker() as session:
  423. agent = session.query(AgentConfiguration).filter(
  424. AgentConfiguration.id == agent_id,
  425. AgentConfiguration.is_delete == 0
  426. ).first()
  427. if not agent:
  428. return wrap_response(404, msg='Agent not found')
  429. agent.is_delete = 1
  430. session.commit()
  431. return wrap_response(200, msg='Agent configuration deleted successfully')
  432. @app.route("/api/getModuleList", methods=["GET"])
  433. def get_module_list():
  434. """
  435. 获取所有的模块列表
  436. :return:
  437. """
  438. with app.session_maker() as session:
  439. query = session.query(ServiceModule) \
  440. .filter(ServiceModule.is_delete == 0)
  441. data = query.all()
  442. ret_data = [
  443. {
  444. 'id': module.id,
  445. 'name': module.name,
  446. 'display_name': module.display_name,
  447. 'default_agent_type': module.default_agent_type,
  448. 'default_agent_id': module.default_agent_id,
  449. 'create_time': module.create_time.strftime('%Y-%m-%d %H:%M:%S'),
  450. 'update_time': module.update_time.strftime('%Y-%m-%d %H:%M:%S')
  451. }
  452. for module in data
  453. ]
  454. return wrap_response(200, data=ret_data)
  455. @app.route("/api/getModuleConfiguration", methods=["GET"])
  456. def get_module_configuration():
  457. """
  458. 获取指定模块的配置
  459. :return:
  460. """
  461. module_id = request.args.get('module_id')
  462. if not module_id:
  463. return wrap_response(404, msg='module_id is required')
  464. with app.session_maker() as session:
  465. module = session.query(ServiceModule).filter(ServiceModule.id == module_id).first()
  466. if not module:
  467. return wrap_response(404, msg='Module not found')
  468. data = {
  469. 'id': module.id,
  470. 'name': module.name,
  471. 'display_name': module.display_name,
  472. 'default_agent_type': module.default_agent_type,
  473. 'default_agent_id': module.default_agent_id,
  474. 'create_time': module.create_time.strftime('%Y-%m-%d %H:%M:%S'),
  475. 'updated_time': module.updated_time.strftime('%Y-%m-%d %H:%M:%S')
  476. }
  477. return wrap_response(200, data=data)
  478. @app.route("/api/saveModuleConfiguration", methods=["POST"])
  479. def save_module_configuration():
  480. """
  481. 保存模块配置
  482. :return:
  483. """
  484. req_data = request.json
  485. module_id = req_data.get('module_id', None)
  486. name = req_data.get('name')
  487. display_name = req_data.get('display_name', None)
  488. default_agent_type = req_data.get('default_agent_type', 0)
  489. default_agent_id = req_data.get('default_agent_id', None)
  490. if not name:
  491. return wrap_response(400, msg='name is required')
  492. with app.session_maker() as session:
  493. if module_id:
  494. module_id = int(module_id)
  495. module = session.query(ServiceModule).filter(ServiceModule.id == module_id).first()
  496. if not module:
  497. return wrap_response(404, msg='Module not found')
  498. module.name = name
  499. module.display_name = display_name
  500. module.default_agent_type = default_agent_type
  501. module.default_agent_id = default_agent_id
  502. else:
  503. module = ServiceModule(
  504. name=name,
  505. display_name=display_name,
  506. default_agent_type=default_agent_type,
  507. default_agent_id=default_agent_id
  508. )
  509. session.add(module)
  510. session.commit()
  511. return wrap_response(200, msg='Module configuration saved successfully', data={'id': module.id})
  512. @app.route("/api/getTestTaskList", methods=["GET"])
  513. def get_test_task_list():
  514. """
  515. 获取单元测试任务列表
  516. :return:
  517. """
  518. page_num = request.args.get("pageNum", const.DEFAULT_PAGE_ID)
  519. page_size = request.args.get("pageSize", const.DEFAULT_PAGE_SIZE)
  520. try:
  521. page_num = int(page_num)
  522. page_size = int(page_size)
  523. except Exception as e:
  524. return wrap_response(404, msg="Invalid parameter: {}".format(e))
  525. response = app.task_manager.get_test_task_list(page_num, page_size)
  526. return wrap_response(200, data=response)
  527. @app.route("/api/getTestTaskConversations", methods=["GET"])
  528. def get_test_task_conversations():
  529. """
  530. 获取单元测试对话任务列表
  531. :return:
  532. """
  533. task_id = request.args.get("taskId", None)
  534. if not task_id:
  535. return wrap_response(404, msg='task_id is required')
  536. page_num = request.args.get("pageNum", const.DEFAULT_PAGE_ID)
  537. page_size = request.args.get("pageSize", const.DEFAULT_PAGE_SIZE)
  538. try:
  539. page_num = int(page_num)
  540. page_size = int(page_size)
  541. except Exception as e:
  542. return wrap_response(404, msg="Invalid parameter: {}".format(e))
  543. response = app.task_manager.get_test_task_conversations(int(task_id), page_num, page_size)
  544. return wrap_response(200, data=response)
  545. @app.route("/api/createTestTask", methods=["POST"])
  546. def create_test_task():
  547. """
  548. 创建单元测试任务
  549. :return:
  550. """
  551. req_data = request.json
  552. agent_id = req_data.get('agentId', None)
  553. module_id = req_data.get('moduleId', None)
  554. evaluate_type = req_data.get('evaluateType', None)
  555. if not agent_id:
  556. return wrap_response(404, msg='agent id is required')
  557. if not module_id:
  558. return wrap_response(404, msg='module id is required')
  559. if not evaluate_type:
  560. return wrap_response(404, msg='evaluate_type id is required')
  561. app.task_manager.create_task(agent_id, module_id, evaluate_type)
  562. return wrap_response(200)
  563. @app.route("/api/stopTestTask", methods=["POST"])
  564. def stop_test_task():
  565. """
  566. 停止单元测试任务
  567. :return:
  568. """
  569. req_data = request.json
  570. task_id = req_data.get('taskId', None)
  571. if not task_id:
  572. return wrap_response(400, msg='task id is required')
  573. task = app.task_manager.get_task(task_id)
  574. if task.status not in (TestTaskStatus.NOT_STARTED.value, TestTaskStatus.IN_PROGRESS.value):
  575. return wrap_response(400, msg='task status is invalid')
  576. app.task_manager.cancel_task(task_id)
  577. return wrap_response(200)
  578. @app.route("/api/resumeTestTask", methods=["POST"])
  579. def resume_test_task():
  580. """
  581. 恢复停止的单元测试任务
  582. :return:
  583. """
  584. req_data = request.json
  585. task_id = req_data.get('taskId', None)
  586. if not task_id:
  587. return wrap_response(400, msg='task id is required')
  588. task = app.task_manager.get_task(task_id)
  589. if task.status != TestTaskStatus.CANCELLED.value:
  590. return wrap_response(400, msg='task status is invalid')
  591. app.task_manager.resume_task(task_id)
  592. return wrap_response(200)
  593. @app.route("/api/getEvaluateType", methods=["GET"])
  594. def get_evaluate_type():
  595. """
  596. 获取评估类型
  597. :return:
  598. """
  599. name_desc_list = [
  600. {
  601. "type": item.value,
  602. "desc": item.description
  603. }
  604. for item in EvaluateType]
  605. return wrap_response(code=200, data=name_desc_list)
  606. @app.route("/api/getDatasetList", methods=["GET"])
  607. def get_dataset_list():
  608. """
  609. 获取数据集列表
  610. :return:
  611. """
  612. page_num = request.args.get("pageNum", const.DEFAULT_PAGE_ID)
  613. page_size = request.args.get("pageSize", const.DEFAULT_PAGE_SIZE)
  614. try:
  615. page_num = int(page_num)
  616. page_size = int(page_size)
  617. except Exception as e:
  618. return wrap_response(404, msg="Invalid parameter: {}".format(e))
  619. response = app.dataset_service.get_dataset_list(page_num, page_size)
  620. return wrap_response(200, data=response)
  621. @app.route("/api/getConversationDataList", methods=["GET"])
  622. def get_conversation_data_list():
  623. """
  624. 获取对话列表
  625. :return:
  626. """
  627. dataset_id = request.args.get("datasetId", None)
  628. if not dataset_id:
  629. return wrap_response(404, msg='dataset_id is required')
  630. page_num = request.args.get("pageNum", const.DEFAULT_PAGE_ID)
  631. page_size = request.args.get("pageSize", const.DEFAULT_PAGE_SIZE)
  632. try:
  633. page_num = int(page_num)
  634. page_size = int(page_size)
  635. except Exception as e:
  636. return wrap_response(404, msg="Invalid parameter: {}".format(e))
  637. response = app.dataset_service.get_conversation_data_list(int(dataset_id), page_num, page_size)
  638. return wrap_response(200, data=response)
  639. @app.route("/api/getToolList", methods=["GET"])
  640. def get_tool_list():
  641. """
  642. 获取所有的工具列表
  643. :return:
  644. """
  645. tools = []
  646. for tool_name, tool in global_tool_map.items():
  647. tools.append({
  648. 'name': tool_name,
  649. 'description': tool.get_function_description(),
  650. 'parameters': tool.parameters if hasattr(tool, 'parameters') else {}
  651. })
  652. return wrap_response(200, data=tools)
  653. @app.route("/api/getModuleAgentTypes", methods=["GET"])
  654. def get_agent_types():
  655. """
  656. 获取所有的Agent类型
  657. :return:
  658. """
  659. agent_types = [
  660. {'type': 0, 'display_name': '原生'},
  661. {'type': 1, 'display_name': 'Coze'}
  662. ]
  663. return wrap_response(200, data=agent_types)
  664. @app.errorhandler(werkzeug.exceptions.BadRequest)
  665. def handle_bad_request(e):
  666. logger.error(e)
  667. return wrap_response(400, msg='Bad Request: {}'.format(e.description))
  668. if __name__ == '__main__':
  669. parser = ArgumentParser()
  670. parser.add_argument('--prod', action='store_true')
  671. parser.add_argument('--host', default='127.0.0.1')
  672. parser.add_argument('--port', type=int, default=8083)
  673. parser.add_argument('--log-level', default='INFO')
  674. args = parser.parse_args()
  675. config = configs.get()
  676. logging_level = logging.getLevelName(args.log_level)
  677. setup_root_logger(level=logging_level, logfile_name='agent_api_server.log')
  678. # set db config
  679. agent_db_config = config['database']['ai_agent']
  680. growth_db_config = config['database']['growth']
  681. user_db_config = config['storage']['user']
  682. staff_db_config = config['storage']['staff']
  683. agent_state_db_config = config['storage']['agent_state']
  684. chat_history_db_config = config['storage']['chat_history']
  685. # init user manager
  686. user_manager = MySQLUserManager(agent_db_config, user_db_config['table'], staff_db_config['table'])
  687. app.user_manager = user_manager
  688. # init session manager
  689. session_manager = MySQLSessionManager(
  690. db_config=agent_db_config,
  691. staff_table=staff_db_config['table'],
  692. user_table=user_db_config['table'],
  693. agent_state_table=agent_state_db_config['table'],
  694. chat_history_table=chat_history_db_config['table']
  695. )
  696. app.session_manager = session_manager
  697. agent_db_engine = create_ai_agent_db_engine()
  698. app.session_maker = sessionmaker(bind=agent_db_engine)
  699. dataset_service = DatasetService(session_maker=sessionmaker(bind=agent_db_engine))
  700. app.dataset_service = dataset_service
  701. task_manager = TaskManager(session_maker=sessionmaker(bind=agent_db_engine), dataset_service=dataset_service)
  702. app.task_manager = task_manager
  703. task_manager.recover_tasks()
  704. wecom_db_config = config['storage']['user_relation']
  705. user_relation_manager = MySQLUserRelationManager(
  706. agent_db_config, growth_db_config,
  707. config['storage']['staff']['table'],
  708. user_db_config['table'],
  709. wecom_db_config['table']['staff'],
  710. wecom_db_config['table']['relation'],
  711. wecom_db_config['table']['user']
  712. )
  713. app.user_relation_manager = user_relation_manager
  714. app.history_dialogue_service = HistoryDialogueService(
  715. config['storage']['history_dialogue']['api_base_url']
  716. )
  717. app.run(debug=not args.prod, host=args.host, port=args.port)