api_server.py 31 KB

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