您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

workflow_statistic.py 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. from datetime import datetime
  2. from decimal import Decimal
  3. import pytz
  4. import sqlalchemy as sa
  5. from flask import jsonify
  6. from flask_login import current_user
  7. from flask_restx import Resource, reqparse
  8. from controllers.console import api, console_ns
  9. from controllers.console.app.wraps import get_app_model
  10. from controllers.console.wraps import account_initialization_required, setup_required
  11. from extensions.ext_database import db
  12. from libs.helper import DatetimeString
  13. from libs.login import login_required
  14. from models.enums import WorkflowRunTriggeredFrom
  15. from models.model import AppMode
  16. @console_ns.route("/apps/<uuid:app_id>/workflow/statistics/daily-conversations")
  17. class WorkflowDailyRunsStatistic(Resource):
  18. @api.doc("get_workflow_daily_runs_statistic")
  19. @api.doc(description="Get workflow daily runs statistics")
  20. @api.doc(params={"app_id": "Application ID"})
  21. @api.doc(params={"start": "Start date and time (YYYY-MM-DD HH:MM)", "end": "End date and time (YYYY-MM-DD HH:MM)"})
  22. @api.response(200, "Daily runs statistics retrieved successfully")
  23. @get_app_model
  24. @setup_required
  25. @login_required
  26. @account_initialization_required
  27. def get(self, app_model):
  28. account = current_user
  29. parser = reqparse.RequestParser()
  30. parser.add_argument("start", type=DatetimeString("%Y-%m-%d %H:%M"), location="args")
  31. parser.add_argument("end", type=DatetimeString("%Y-%m-%d %H:%M"), location="args")
  32. args = parser.parse_args()
  33. sql_query = """SELECT
  34. DATE(DATE_TRUNC('day', created_at AT TIME ZONE 'UTC' AT TIME ZONE :tz )) AS date,
  35. COUNT(id) AS runs
  36. FROM
  37. workflow_runs
  38. WHERE
  39. app_id = :app_id
  40. AND triggered_from = :triggered_from"""
  41. arg_dict = {
  42. "tz": account.timezone,
  43. "app_id": app_model.id,
  44. "triggered_from": WorkflowRunTriggeredFrom.APP_RUN.value,
  45. }
  46. timezone = pytz.timezone(account.timezone)
  47. utc_timezone = pytz.utc
  48. if args["start"]:
  49. start_datetime = datetime.strptime(args["start"], "%Y-%m-%d %H:%M")
  50. start_datetime = start_datetime.replace(second=0)
  51. start_datetime_timezone = timezone.localize(start_datetime)
  52. start_datetime_utc = start_datetime_timezone.astimezone(utc_timezone)
  53. sql_query += " AND created_at >= :start"
  54. arg_dict["start"] = start_datetime_utc
  55. if args["end"]:
  56. end_datetime = datetime.strptime(args["end"], "%Y-%m-%d %H:%M")
  57. end_datetime = end_datetime.replace(second=0)
  58. end_datetime_timezone = timezone.localize(end_datetime)
  59. end_datetime_utc = end_datetime_timezone.astimezone(utc_timezone)
  60. sql_query += " AND created_at < :end"
  61. arg_dict["end"] = end_datetime_utc
  62. sql_query += " GROUP BY date ORDER BY date"
  63. response_data = []
  64. with db.engine.begin() as conn:
  65. rs = conn.execute(sa.text(sql_query), arg_dict)
  66. for i in rs:
  67. response_data.append({"date": str(i.date), "runs": i.runs})
  68. return jsonify({"data": response_data})
  69. @console_ns.route("/apps/<uuid:app_id>/workflow/statistics/daily-terminals")
  70. class WorkflowDailyTerminalsStatistic(Resource):
  71. @api.doc("get_workflow_daily_terminals_statistic")
  72. @api.doc(description="Get workflow daily terminals statistics")
  73. @api.doc(params={"app_id": "Application ID"})
  74. @api.doc(params={"start": "Start date and time (YYYY-MM-DD HH:MM)", "end": "End date and time (YYYY-MM-DD HH:MM)"})
  75. @api.response(200, "Daily terminals statistics retrieved successfully")
  76. @get_app_model
  77. @setup_required
  78. @login_required
  79. @account_initialization_required
  80. def get(self, app_model):
  81. account = current_user
  82. parser = reqparse.RequestParser()
  83. parser.add_argument("start", type=DatetimeString("%Y-%m-%d %H:%M"), location="args")
  84. parser.add_argument("end", type=DatetimeString("%Y-%m-%d %H:%M"), location="args")
  85. args = parser.parse_args()
  86. sql_query = """SELECT
  87. DATE(DATE_TRUNC('day', created_at AT TIME ZONE 'UTC' AT TIME ZONE :tz )) AS date,
  88. COUNT(DISTINCT workflow_runs.created_by) AS terminal_count
  89. FROM
  90. workflow_runs
  91. WHERE
  92. app_id = :app_id
  93. AND triggered_from = :triggered_from"""
  94. arg_dict = {
  95. "tz": account.timezone,
  96. "app_id": app_model.id,
  97. "triggered_from": WorkflowRunTriggeredFrom.APP_RUN.value,
  98. }
  99. timezone = pytz.timezone(account.timezone)
  100. utc_timezone = pytz.utc
  101. if args["start"]:
  102. start_datetime = datetime.strptime(args["start"], "%Y-%m-%d %H:%M")
  103. start_datetime = start_datetime.replace(second=0)
  104. start_datetime_timezone = timezone.localize(start_datetime)
  105. start_datetime_utc = start_datetime_timezone.astimezone(utc_timezone)
  106. sql_query += " AND created_at >= :start"
  107. arg_dict["start"] = start_datetime_utc
  108. if args["end"]:
  109. end_datetime = datetime.strptime(args["end"], "%Y-%m-%d %H:%M")
  110. end_datetime = end_datetime.replace(second=0)
  111. end_datetime_timezone = timezone.localize(end_datetime)
  112. end_datetime_utc = end_datetime_timezone.astimezone(utc_timezone)
  113. sql_query += " AND created_at < :end"
  114. arg_dict["end"] = end_datetime_utc
  115. sql_query += " GROUP BY date ORDER BY date"
  116. response_data = []
  117. with db.engine.begin() as conn:
  118. rs = conn.execute(sa.text(sql_query), arg_dict)
  119. for i in rs:
  120. response_data.append({"date": str(i.date), "terminal_count": i.terminal_count})
  121. return jsonify({"data": response_data})
  122. @console_ns.route("/apps/<uuid:app_id>/workflow/statistics/token-costs")
  123. class WorkflowDailyTokenCostStatistic(Resource):
  124. @api.doc("get_workflow_daily_token_cost_statistic")
  125. @api.doc(description="Get workflow daily token cost statistics")
  126. @api.doc(params={"app_id": "Application ID"})
  127. @api.doc(params={"start": "Start date and time (YYYY-MM-DD HH:MM)", "end": "End date and time (YYYY-MM-DD HH:MM)"})
  128. @api.response(200, "Daily token cost statistics retrieved successfully")
  129. @get_app_model
  130. @setup_required
  131. @login_required
  132. @account_initialization_required
  133. def get(self, app_model):
  134. account = current_user
  135. parser = reqparse.RequestParser()
  136. parser.add_argument("start", type=DatetimeString("%Y-%m-%d %H:%M"), location="args")
  137. parser.add_argument("end", type=DatetimeString("%Y-%m-%d %H:%M"), location="args")
  138. args = parser.parse_args()
  139. sql_query = """SELECT
  140. DATE(DATE_TRUNC('day', created_at AT TIME ZONE 'UTC' AT TIME ZONE :tz )) AS date,
  141. SUM(workflow_runs.total_tokens) AS token_count
  142. FROM
  143. workflow_runs
  144. WHERE
  145. app_id = :app_id
  146. AND triggered_from = :triggered_from"""
  147. arg_dict = {
  148. "tz": account.timezone,
  149. "app_id": app_model.id,
  150. "triggered_from": WorkflowRunTriggeredFrom.APP_RUN.value,
  151. }
  152. timezone = pytz.timezone(account.timezone)
  153. utc_timezone = pytz.utc
  154. if args["start"]:
  155. start_datetime = datetime.strptime(args["start"], "%Y-%m-%d %H:%M")
  156. start_datetime = start_datetime.replace(second=0)
  157. start_datetime_timezone = timezone.localize(start_datetime)
  158. start_datetime_utc = start_datetime_timezone.astimezone(utc_timezone)
  159. sql_query += " AND created_at >= :start"
  160. arg_dict["start"] = start_datetime_utc
  161. if args["end"]:
  162. end_datetime = datetime.strptime(args["end"], "%Y-%m-%d %H:%M")
  163. end_datetime = end_datetime.replace(second=0)
  164. end_datetime_timezone = timezone.localize(end_datetime)
  165. end_datetime_utc = end_datetime_timezone.astimezone(utc_timezone)
  166. sql_query += " AND created_at < :end"
  167. arg_dict["end"] = end_datetime_utc
  168. sql_query += " GROUP BY date ORDER BY date"
  169. response_data = []
  170. with db.engine.begin() as conn:
  171. rs = conn.execute(sa.text(sql_query), arg_dict)
  172. for i in rs:
  173. response_data.append(
  174. {
  175. "date": str(i.date),
  176. "token_count": i.token_count,
  177. }
  178. )
  179. return jsonify({"data": response_data})
  180. @console_ns.route("/apps/<uuid:app_id>/workflow/statistics/average-app-interactions")
  181. class WorkflowAverageAppInteractionStatistic(Resource):
  182. @api.doc("get_workflow_average_app_interaction_statistic")
  183. @api.doc(description="Get workflow average app interaction statistics")
  184. @api.doc(params={"app_id": "Application ID"})
  185. @api.doc(params={"start": "Start date and time (YYYY-MM-DD HH:MM)", "end": "End date and time (YYYY-MM-DD HH:MM)"})
  186. @api.response(200, "Average app interaction statistics retrieved successfully")
  187. @setup_required
  188. @login_required
  189. @account_initialization_required
  190. @get_app_model(mode=[AppMode.WORKFLOW])
  191. def get(self, app_model):
  192. account = current_user
  193. parser = reqparse.RequestParser()
  194. parser.add_argument("start", type=DatetimeString("%Y-%m-%d %H:%M"), location="args")
  195. parser.add_argument("end", type=DatetimeString("%Y-%m-%d %H:%M"), location="args")
  196. args = parser.parse_args()
  197. sql_query = """SELECT
  198. AVG(sub.interactions) AS interactions,
  199. sub.date
  200. FROM
  201. (
  202. SELECT
  203. DATE(DATE_TRUNC('day', c.created_at AT TIME ZONE 'UTC' AT TIME ZONE :tz )) AS date,
  204. c.created_by,
  205. COUNT(c.id) AS interactions
  206. FROM
  207. workflow_runs c
  208. WHERE
  209. c.app_id = :app_id
  210. AND c.triggered_from = :triggered_from
  211. {{start}}
  212. {{end}}
  213. GROUP BY
  214. date, c.created_by
  215. ) sub
  216. GROUP BY
  217. sub.date"""
  218. arg_dict = {
  219. "tz": account.timezone,
  220. "app_id": app_model.id,
  221. "triggered_from": WorkflowRunTriggeredFrom.APP_RUN.value,
  222. }
  223. timezone = pytz.timezone(account.timezone)
  224. utc_timezone = pytz.utc
  225. if args["start"]:
  226. start_datetime = datetime.strptime(args["start"], "%Y-%m-%d %H:%M")
  227. start_datetime = start_datetime.replace(second=0)
  228. start_datetime_timezone = timezone.localize(start_datetime)
  229. start_datetime_utc = start_datetime_timezone.astimezone(utc_timezone)
  230. sql_query = sql_query.replace("{{start}}", " AND c.created_at >= :start")
  231. arg_dict["start"] = start_datetime_utc
  232. else:
  233. sql_query = sql_query.replace("{{start}}", "")
  234. if args["end"]:
  235. end_datetime = datetime.strptime(args["end"], "%Y-%m-%d %H:%M")
  236. end_datetime = end_datetime.replace(second=0)
  237. end_datetime_timezone = timezone.localize(end_datetime)
  238. end_datetime_utc = end_datetime_timezone.astimezone(utc_timezone)
  239. sql_query = sql_query.replace("{{end}}", " AND c.created_at < :end")
  240. arg_dict["end"] = end_datetime_utc
  241. else:
  242. sql_query = sql_query.replace("{{end}}", "")
  243. response_data = []
  244. with db.engine.begin() as conn:
  245. rs = conn.execute(sa.text(sql_query), arg_dict)
  246. for i in rs:
  247. response_data.append(
  248. {"date": str(i.date), "interactions": float(i.interactions.quantize(Decimal("0.01")))}
  249. )
  250. return jsonify({"data": response_data})