| @@ -1,12 +1,26 @@ | |||
| from flask_restx import Resource, reqparse | |||
| from flask_restx import Resource, fields, reqparse | |||
| from controllers.console import api | |||
| from controllers.console import api, console_ns | |||
| from controllers.console.wraps import account_initialization_required, setup_required | |||
| from libs.login import login_required | |||
| from services.advanced_prompt_template_service import AdvancedPromptTemplateService | |||
| @console_ns.route("/app/prompt-templates") | |||
| class AdvancedPromptTemplateList(Resource): | |||
| @api.doc("get_advanced_prompt_templates") | |||
| @api.doc(description="Get advanced prompt templates based on app mode and model configuration") | |||
| @api.expect( | |||
| api.parser() | |||
| .add_argument("app_mode", type=str, required=True, location="args", help="Application mode") | |||
| .add_argument("model_mode", type=str, required=True, location="args", help="Model mode") | |||
| .add_argument("has_context", type=str, default="true", location="args", help="Whether has context") | |||
| .add_argument("model_name", type=str, required=True, location="args", help="Model name") | |||
| ) | |||
| @api.response( | |||
| 200, "Prompt templates retrieved successfully", fields.List(fields.Raw(description="Prompt template data")) | |||
| ) | |||
| @api.response(400, "Invalid request parameters") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -19,6 +33,3 @@ class AdvancedPromptTemplateList(Resource): | |||
| args = parser.parse_args() | |||
| return AdvancedPromptTemplateService.get_prompt(args) | |||
| api.add_resource(AdvancedPromptTemplateList, "/app/prompt-templates") | |||
| @@ -1,6 +1,6 @@ | |||
| from flask_restx import Resource, reqparse | |||
| from flask_restx import Resource, fields, reqparse | |||
| from controllers.console import api | |||
| from controllers.console import api, console_ns | |||
| from controllers.console.app.wraps import get_app_model | |||
| from controllers.console.wraps import account_initialization_required, setup_required | |||
| from libs.helper import uuid_value | |||
| @@ -9,7 +9,18 @@ from models.model import AppMode | |||
| from services.agent_service import AgentService | |||
| @console_ns.route("/apps/<uuid:app_id>/agent/logs") | |||
| class AgentLogApi(Resource): | |||
| @api.doc("get_agent_logs") | |||
| @api.doc(description="Get agent execution logs for an application") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.parser() | |||
| .add_argument("message_id", type=str, required=True, location="args", help="Message UUID") | |||
| .add_argument("conversation_id", type=str, required=True, location="args", help="Conversation UUID") | |||
| ) | |||
| @api.response(200, "Agent logs retrieved successfully", fields.List(fields.Raw(description="Agent log entries"))) | |||
| @api.response(400, "Invalid request parameters") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -23,6 +34,3 @@ class AgentLogApi(Resource): | |||
| args = parser.parse_args() | |||
| return AgentService.get_agent_logs(app_model, args["conversation_id"], args["message_id"]) | |||
| api.add_resource(AgentLogApi, "/apps/<uuid:app_id>/agent/logs") | |||
| @@ -2,11 +2,11 @@ from typing import Literal | |||
| from flask import request | |||
| from flask_login import current_user | |||
| from flask_restx import Resource, marshal, marshal_with, reqparse | |||
| from flask_restx import Resource, fields, marshal, marshal_with, reqparse | |||
| from werkzeug.exceptions import Forbidden | |||
| from controllers.common.errors import NoFileUploadedError, TooManyFilesError | |||
| from controllers.console import api | |||
| from controllers.console import api, console_ns | |||
| from controllers.console.wraps import ( | |||
| account_initialization_required, | |||
| cloud_edition_billing_resource_check, | |||
| @@ -21,7 +21,23 @@ from libs.login import login_required | |||
| from services.annotation_service import AppAnnotationService | |||
| @console_ns.route("/apps/<uuid:app_id>/annotation-reply/<string:action>") | |||
| class AnnotationReplyActionApi(Resource): | |||
| @api.doc("annotation_reply_action") | |||
| @api.doc(description="Enable or disable annotation reply for an app") | |||
| @api.doc(params={"app_id": "Application ID", "action": "Action to perform (enable/disable)"}) | |||
| @api.expect( | |||
| api.model( | |||
| "AnnotationReplyActionRequest", | |||
| { | |||
| "score_threshold": fields.Float(required=True, description="Score threshold for annotation matching"), | |||
| "embedding_provider_name": fields.String(required=True, description="Embedding provider name"), | |||
| "embedding_model_name": fields.String(required=True, description="Embedding model name"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "Action completed successfully") | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -43,7 +59,13 @@ class AnnotationReplyActionApi(Resource): | |||
| return result, 200 | |||
| @console_ns.route("/apps/<uuid:app_id>/annotation-setting") | |||
| class AppAnnotationSettingDetailApi(Resource): | |||
| @api.doc("get_annotation_setting") | |||
| @api.doc(description="Get annotation settings for an app") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.response(200, "Annotation settings retrieved successfully") | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -56,7 +78,23 @@ class AppAnnotationSettingDetailApi(Resource): | |||
| return result, 200 | |||
| @console_ns.route("/apps/<uuid:app_id>/annotation-settings/<uuid:annotation_setting_id>") | |||
| class AppAnnotationSettingUpdateApi(Resource): | |||
| @api.doc("update_annotation_setting") | |||
| @api.doc(description="Update annotation settings for an app") | |||
| @api.doc(params={"app_id": "Application ID", "annotation_setting_id": "Annotation setting ID"}) | |||
| @api.expect( | |||
| api.model( | |||
| "AnnotationSettingUpdateRequest", | |||
| { | |||
| "score_threshold": fields.Float(required=True, description="Score threshold"), | |||
| "embedding_provider_name": fields.String(required=True, description="Embedding provider"), | |||
| "embedding_model_name": fields.String(required=True, description="Embedding model"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "Settings updated successfully") | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -75,7 +113,13 @@ class AppAnnotationSettingUpdateApi(Resource): | |||
| return result, 200 | |||
| @console_ns.route("/apps/<uuid:app_id>/annotation-reply/<string:action>/status/<uuid:job_id>") | |||
| class AnnotationReplyActionStatusApi(Resource): | |||
| @api.doc("get_annotation_reply_action_status") | |||
| @api.doc(description="Get status of annotation reply action job") | |||
| @api.doc(params={"app_id": "Application ID", "job_id": "Job ID", "action": "Action type"}) | |||
| @api.response(200, "Job status retrieved successfully") | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -99,7 +143,19 @@ class AnnotationReplyActionStatusApi(Resource): | |||
| return {"job_id": job_id, "job_status": job_status, "error_msg": error_msg}, 200 | |||
| @console_ns.route("/apps/<uuid:app_id>/annotations") | |||
| class AnnotationApi(Resource): | |||
| @api.doc("list_annotations") | |||
| @api.doc(description="Get annotations for an app with pagination") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.parser() | |||
| .add_argument("page", type=int, location="args", default=1, help="Page number") | |||
| .add_argument("limit", type=int, location="args", default=20, help="Page size") | |||
| .add_argument("keyword", type=str, location="args", default="", help="Search keyword") | |||
| ) | |||
| @api.response(200, "Annotations retrieved successfully") | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -122,6 +178,21 @@ class AnnotationApi(Resource): | |||
| } | |||
| return response, 200 | |||
| @api.doc("create_annotation") | |||
| @api.doc(description="Create a new annotation for an app") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.model( | |||
| "CreateAnnotationRequest", | |||
| { | |||
| "question": fields.String(required=True, description="Question text"), | |||
| "answer": fields.String(required=True, description="Answer text"), | |||
| "annotation_reply": fields.Raw(description="Annotation reply data"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(201, "Annotation created successfully", annotation_fields) | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -168,7 +239,13 @@ class AnnotationApi(Resource): | |||
| return {"result": "success"}, 204 | |||
| @console_ns.route("/apps/<uuid:app_id>/annotations/export") | |||
| class AnnotationExportApi(Resource): | |||
| @api.doc("export_annotations") | |||
| @api.doc(description="Export all annotations for an app") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.response(200, "Annotations exported successfully", fields.List(fields.Nested(annotation_fields))) | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -182,7 +259,14 @@ class AnnotationExportApi(Resource): | |||
| return response, 200 | |||
| @console_ns.route("/apps/<uuid:app_id>/annotations/<uuid:annotation_id>") | |||
| class AnnotationUpdateDeleteApi(Resource): | |||
| @api.doc("update_delete_annotation") | |||
| @api.doc(description="Update or delete an annotation") | |||
| @api.doc(params={"app_id": "Application ID", "annotation_id": "Annotation ID"}) | |||
| @api.response(200, "Annotation updated successfully", annotation_fields) | |||
| @api.response(204, "Annotation deleted successfully") | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -214,7 +298,14 @@ class AnnotationUpdateDeleteApi(Resource): | |||
| return {"result": "success"}, 204 | |||
| @console_ns.route("/apps/<uuid:app_id>/annotations/batch-import") | |||
| class AnnotationBatchImportApi(Resource): | |||
| @api.doc("batch_import_annotations") | |||
| @api.doc(description="Batch import annotations from CSV file") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.response(200, "Batch import started successfully") | |||
| @api.response(403, "Insufficient permissions") | |||
| @api.response(400, "No file uploaded or too many files") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -239,7 +330,13 @@ class AnnotationBatchImportApi(Resource): | |||
| return AppAnnotationService.batch_import_app_annotations(app_id, file) | |||
| @console_ns.route("/apps/<uuid:app_id>/annotations/batch-import-status/<uuid:job_id>") | |||
| class AnnotationBatchImportStatusApi(Resource): | |||
| @api.doc("get_batch_import_status") | |||
| @api.doc(description="Get status of batch import job") | |||
| @api.doc(params={"app_id": "Application ID", "job_id": "Job ID"}) | |||
| @api.response(200, "Job status retrieved successfully") | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -262,7 +359,20 @@ class AnnotationBatchImportStatusApi(Resource): | |||
| return {"job_id": job_id, "job_status": job_status, "error_msg": error_msg}, 200 | |||
| @console_ns.route("/apps/<uuid:app_id>/annotations/<uuid:annotation_id>/hit-histories") | |||
| class AnnotationHitHistoryListApi(Resource): | |||
| @api.doc("list_annotation_hit_histories") | |||
| @api.doc(description="Get hit histories for an annotation") | |||
| @api.doc(params={"app_id": "Application ID", "annotation_id": "Annotation ID"}) | |||
| @api.expect( | |||
| api.parser() | |||
| .add_argument("page", type=int, location="args", default=1, help="Page number") | |||
| .add_argument("limit", type=int, location="args", default=20, help="Page size") | |||
| ) | |||
| @api.response( | |||
| 200, "Hit histories retrieved successfully", fields.List(fields.Nested(annotation_hit_history_fields)) | |||
| ) | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -285,17 +395,3 @@ class AnnotationHitHistoryListApi(Resource): | |||
| "page": page, | |||
| } | |||
| return response | |||
| api.add_resource(AnnotationReplyActionApi, "/apps/<uuid:app_id>/annotation-reply/<string:action>") | |||
| api.add_resource( | |||
| AnnotationReplyActionStatusApi, "/apps/<uuid:app_id>/annotation-reply/<string:action>/status/<uuid:job_id>" | |||
| ) | |||
| api.add_resource(AnnotationApi, "/apps/<uuid:app_id>/annotations") | |||
| api.add_resource(AnnotationExportApi, "/apps/<uuid:app_id>/annotations/export") | |||
| api.add_resource(AnnotationUpdateDeleteApi, "/apps/<uuid:app_id>/annotations/<uuid:annotation_id>") | |||
| api.add_resource(AnnotationBatchImportApi, "/apps/<uuid:app_id>/annotations/batch-import") | |||
| api.add_resource(AnnotationBatchImportStatusApi, "/apps/<uuid:app_id>/annotations/batch-import-status/<uuid:job_id>") | |||
| api.add_resource(AnnotationHitHistoryListApi, "/apps/<uuid:app_id>/annotations/<uuid:annotation_id>/hit-histories") | |||
| api.add_resource(AppAnnotationSettingDetailApi, "/apps/<uuid:app_id>/annotation-setting") | |||
| api.add_resource(AppAnnotationSettingUpdateApi, "/apps/<uuid:app_id>/annotation-settings/<uuid:annotation_setting_id>") | |||
| @@ -2,12 +2,12 @@ import uuid | |||
| from typing import cast | |||
| from flask_login import current_user | |||
| from flask_restx import Resource, inputs, marshal, marshal_with, reqparse | |||
| from flask_restx import Resource, fields, inputs, marshal, marshal_with, reqparse | |||
| from sqlalchemy import select | |||
| from sqlalchemy.orm import Session | |||
| from werkzeug.exceptions import BadRequest, Forbidden, abort | |||
| from controllers.console import api | |||
| from controllers.console import api, console_ns | |||
| from controllers.console.app.wraps import get_app_model | |||
| from controllers.console.wraps import ( | |||
| account_initialization_required, | |||
| @@ -34,7 +34,27 @@ def _validate_description_length(description): | |||
| return description | |||
| @console_ns.route("/apps") | |||
| class AppListApi(Resource): | |||
| @api.doc("list_apps") | |||
| @api.doc(description="Get list of applications with pagination and filtering") | |||
| @api.expect( | |||
| api.parser() | |||
| .add_argument("page", type=int, location="args", help="Page number (1-99999)", default=1) | |||
| .add_argument("limit", type=int, location="args", help="Page size (1-100)", default=20) | |||
| .add_argument( | |||
| "mode", | |||
| type=str, | |||
| location="args", | |||
| choices=["completion", "chat", "advanced-chat", "workflow", "agent-chat", "channel", "all"], | |||
| default="all", | |||
| help="App mode filter", | |||
| ) | |||
| .add_argument("name", type=str, location="args", help="Filter by app name") | |||
| .add_argument("tag_ids", type=str, location="args", help="Comma-separated tag IDs") | |||
| .add_argument("is_created_by_me", type=bool, location="args", help="Filter by creator") | |||
| ) | |||
| @api.response(200, "Success", app_pagination_fields) | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -91,6 +111,24 @@ class AppListApi(Resource): | |||
| return marshal(app_pagination, app_pagination_fields), 200 | |||
| @api.doc("create_app") | |||
| @api.doc(description="Create a new application") | |||
| @api.expect( | |||
| api.model( | |||
| "CreateAppRequest", | |||
| { | |||
| "name": fields.String(required=True, description="App name"), | |||
| "description": fields.String(description="App description (max 400 chars)"), | |||
| "mode": fields.String(required=True, enum=ALLOW_CREATE_APP_MODES, description="App mode"), | |||
| "icon_type": fields.String(description="Icon type"), | |||
| "icon": fields.String(description="Icon"), | |||
| "icon_background": fields.String(description="Icon background color"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(201, "App created successfully", app_detail_fields) | |||
| @api.response(403, "Insufficient permissions") | |||
| @api.response(400, "Invalid request parameters") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -124,7 +162,12 @@ class AppListApi(Resource): | |||
| return app, 201 | |||
| @console_ns.route("/apps/<uuid:app_id>") | |||
| class AppApi(Resource): | |||
| @api.doc("get_app_detail") | |||
| @api.doc(description="Get application details") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.response(200, "Success", app_detail_fields_with_site) | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -143,6 +186,26 @@ class AppApi(Resource): | |||
| return app_model | |||
| @api.doc("update_app") | |||
| @api.doc(description="Update application details") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.model( | |||
| "UpdateAppRequest", | |||
| { | |||
| "name": fields.String(required=True, description="App name"), | |||
| "description": fields.String(description="App description (max 400 chars)"), | |||
| "icon_type": fields.String(description="Icon type"), | |||
| "icon": fields.String(description="Icon"), | |||
| "icon_background": fields.String(description="Icon background color"), | |||
| "use_icon_as_answer_icon": fields.Boolean(description="Use icon as answer icon"), | |||
| "max_active_requests": fields.Integer(description="Maximum active requests"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "App updated successfully", app_detail_fields_with_site) | |||
| @api.response(403, "Insufficient permissions") | |||
| @api.response(400, "Invalid request parameters") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -181,6 +244,11 @@ class AppApi(Resource): | |||
| return app_model | |||
| @api.doc("delete_app") | |||
| @api.doc(description="Delete application") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.response(204, "App deleted successfully") | |||
| @api.response(403, "Insufficient permissions") | |||
| @get_app_model | |||
| @setup_required | |||
| @login_required | |||
| @@ -197,7 +265,25 @@ class AppApi(Resource): | |||
| return {"result": "success"}, 204 | |||
| @console_ns.route("/apps/<uuid:app_id>/copy") | |||
| class AppCopyApi(Resource): | |||
| @api.doc("copy_app") | |||
| @api.doc(description="Create a copy of an existing application") | |||
| @api.doc(params={"app_id": "Application ID to copy"}) | |||
| @api.expect( | |||
| api.model( | |||
| "CopyAppRequest", | |||
| { | |||
| "name": fields.String(description="Name for the copied app"), | |||
| "description": fields.String(description="Description for the copied app"), | |||
| "icon_type": fields.String(description="Icon type"), | |||
| "icon": fields.String(description="Icon"), | |||
| "icon_background": fields.String(description="Icon background color"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(201, "App copied successfully", app_detail_fields_with_site) | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -239,7 +325,22 @@ class AppCopyApi(Resource): | |||
| return app, 201 | |||
| @console_ns.route("/apps/<uuid:app_id>/export") | |||
| class AppExportApi(Resource): | |||
| @api.doc("export_app") | |||
| @api.doc(description="Export application configuration as DSL") | |||
| @api.doc(params={"app_id": "Application ID to export"}) | |||
| @api.expect( | |||
| api.parser() | |||
| .add_argument("include_secret", type=bool, location="args", default=False, help="Include secrets in export") | |||
| .add_argument("workflow_id", type=str, location="args", help="Specific workflow ID to export") | |||
| ) | |||
| @api.response( | |||
| 200, | |||
| "App exported successfully", | |||
| api.model("AppExportResponse", {"data": fields.String(description="DSL export data")}), | |||
| ) | |||
| @api.response(403, "Insufficient permissions") | |||
| @get_app_model | |||
| @setup_required | |||
| @login_required | |||
| @@ -263,7 +364,13 @@ class AppExportApi(Resource): | |||
| } | |||
| @console_ns.route("/apps/<uuid:app_id>/name") | |||
| class AppNameApi(Resource): | |||
| @api.doc("check_app_name") | |||
| @api.doc(description="Check if app name is available") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect(api.parser().add_argument("name", type=str, required=True, location="args", help="Name to check")) | |||
| @api.response(200, "Name availability checked") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -284,7 +391,23 @@ class AppNameApi(Resource): | |||
| return app_model | |||
| @console_ns.route("/apps/<uuid:app_id>/icon") | |||
| class AppIconApi(Resource): | |||
| @api.doc("update_app_icon") | |||
| @api.doc(description="Update application icon") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.model( | |||
| "AppIconRequest", | |||
| { | |||
| "icon": fields.String(required=True, description="Icon data"), | |||
| "icon_type": fields.String(description="Icon type"), | |||
| "icon_background": fields.String(description="Icon background color"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "Icon updated successfully") | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -306,7 +429,18 @@ class AppIconApi(Resource): | |||
| return app_model | |||
| @console_ns.route("/apps/<uuid:app_id>/site-enable") | |||
| class AppSiteStatus(Resource): | |||
| @api.doc("update_app_site_status") | |||
| @api.doc(description="Enable or disable app site") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.model( | |||
| "AppSiteStatusRequest", {"enable_site": fields.Boolean(required=True, description="Enable or disable site")} | |||
| ) | |||
| ) | |||
| @api.response(200, "Site status updated successfully", app_detail_fields) | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -327,7 +461,18 @@ class AppSiteStatus(Resource): | |||
| return app_model | |||
| @console_ns.route("/apps/<uuid:app_id>/api-enable") | |||
| class AppApiStatus(Resource): | |||
| @api.doc("update_app_api_status") | |||
| @api.doc(description="Enable or disable app API") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.model( | |||
| "AppApiStatusRequest", {"enable_api": fields.Boolean(required=True, description="Enable or disable API")} | |||
| ) | |||
| ) | |||
| @api.response(200, "API status updated successfully", app_detail_fields) | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -348,7 +493,12 @@ class AppApiStatus(Resource): | |||
| return app_model | |||
| @console_ns.route("/apps/<uuid:app_id>/trace") | |||
| class AppTraceApi(Resource): | |||
| @api.doc("get_app_trace") | |||
| @api.doc(description="Get app tracing configuration") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.response(200, "Trace configuration retrieved successfully") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -358,6 +508,20 @@ class AppTraceApi(Resource): | |||
| return app_trace_config | |||
| @api.doc("update_app_trace") | |||
| @api.doc(description="Update app tracing configuration") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.model( | |||
| "AppTraceRequest", | |||
| { | |||
| "enabled": fields.Boolean(required=True, description="Enable or disable tracing"), | |||
| "tracing_provider": fields.String(required=True, description="Tracing provider"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "Trace configuration updated successfully") | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -377,14 +541,3 @@ class AppTraceApi(Resource): | |||
| ) | |||
| return {"result": "success"} | |||
| api.add_resource(AppListApi, "/apps") | |||
| api.add_resource(AppApi, "/apps/<uuid:app_id>") | |||
| api.add_resource(AppCopyApi, "/apps/<uuid:app_id>/copy") | |||
| api.add_resource(AppExportApi, "/apps/<uuid:app_id>/export") | |||
| api.add_resource(AppNameApi, "/apps/<uuid:app_id>/name") | |||
| api.add_resource(AppIconApi, "/apps/<uuid:app_id>/icon") | |||
| api.add_resource(AppSiteStatus, "/apps/<uuid:app_id>/site-enable") | |||
| api.add_resource(AppApiStatus, "/apps/<uuid:app_id>/api-enable") | |||
| api.add_resource(AppTraceApi, "/apps/<uuid:app_id>/trace") | |||
| @@ -1,11 +1,11 @@ | |||
| import logging | |||
| from flask import request | |||
| from flask_restx import Resource, reqparse | |||
| from flask_restx import Resource, fields, reqparse | |||
| from werkzeug.exceptions import InternalServerError | |||
| import services | |||
| from controllers.console import api | |||
| from controllers.console import api, console_ns | |||
| from controllers.console.app.error import ( | |||
| AppUnavailableError, | |||
| AudioTooLargeError, | |||
| @@ -34,7 +34,18 @@ from services.errors.audio import ( | |||
| logger = logging.getLogger(__name__) | |||
| @console_ns.route("/apps/<uuid:app_id>/audio-to-text") | |||
| class ChatMessageAudioApi(Resource): | |||
| @api.doc("chat_message_audio_transcript") | |||
| @api.doc(description="Transcript audio to text for chat messages") | |||
| @api.doc(params={"app_id": "App ID"}) | |||
| @api.response( | |||
| 200, | |||
| "Audio transcription successful", | |||
| api.model("AudioTranscriptResponse", {"text": fields.String(description="Transcribed text from audio")}), | |||
| ) | |||
| @api.response(400, "Bad request - No audio uploaded or unsupported type") | |||
| @api.response(413, "Audio file too large") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -76,7 +87,24 @@ class ChatMessageAudioApi(Resource): | |||
| raise InternalServerError() | |||
| @console_ns.route("/apps/<uuid:app_id>/text-to-audio") | |||
| class ChatMessageTextApi(Resource): | |||
| @api.doc("chat_message_text_to_speech") | |||
| @api.doc(description="Convert text to speech for chat messages") | |||
| @api.doc(params={"app_id": "App ID"}) | |||
| @api.expect( | |||
| api.model( | |||
| "TextToSpeechRequest", | |||
| { | |||
| "message_id": fields.String(description="Message ID"), | |||
| "text": fields.String(required=True, description="Text to convert to speech"), | |||
| "voice": fields.String(description="Voice to use for TTS"), | |||
| "streaming": fields.Boolean(description="Whether to stream the audio"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "Text to speech conversion successful") | |||
| @api.response(400, "Bad request - Invalid parameters") | |||
| @get_app_model | |||
| @setup_required | |||
| @login_required | |||
| @@ -124,7 +152,14 @@ class ChatMessageTextApi(Resource): | |||
| raise InternalServerError() | |||
| @console_ns.route("/apps/<uuid:app_id>/text-to-audio/voices") | |||
| class TextModesApi(Resource): | |||
| @api.doc("get_text_to_speech_voices") | |||
| @api.doc(description="Get available TTS voices for a specific language") | |||
| @api.doc(params={"app_id": "App ID"}) | |||
| @api.expect(api.parser().add_argument("language", type=str, required=True, location="args", help="Language code")) | |||
| @api.response(200, "TTS voices retrieved successfully", fields.List(fields.Raw(description="Available voices"))) | |||
| @api.response(400, "Invalid language parameter") | |||
| @get_app_model | |||
| @setup_required | |||
| @login_required | |||
| @@ -164,8 +199,3 @@ class TextModesApi(Resource): | |||
| except Exception as e: | |||
| logger.exception("Failed to handle get request to TextModesApi") | |||
| raise InternalServerError() | |||
| api.add_resource(ChatMessageAudioApi, "/apps/<uuid:app_id>/audio-to-text") | |||
| api.add_resource(ChatMessageTextApi, "/apps/<uuid:app_id>/text-to-audio") | |||
| api.add_resource(TextModesApi, "/apps/<uuid:app_id>/text-to-audio/voices") | |||
| @@ -1,11 +1,11 @@ | |||
| import logging | |||
| from flask import request | |||
| from flask_restx import Resource, reqparse | |||
| from flask_restx import Resource, fields, reqparse | |||
| from werkzeug.exceptions import Forbidden, InternalServerError, NotFound | |||
| import services | |||
| from controllers.console import api | |||
| from controllers.console import api, console_ns | |||
| from controllers.console.app.error import ( | |||
| AppUnavailableError, | |||
| CompletionRequestError, | |||
| @@ -38,7 +38,27 @@ logger = logging.getLogger(__name__) | |||
| # define completion message api for user | |||
| @console_ns.route("/apps/<uuid:app_id>/completion-messages") | |||
| class CompletionMessageApi(Resource): | |||
| @api.doc("create_completion_message") | |||
| @api.doc(description="Generate completion message for debugging") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.model( | |||
| "CompletionMessageRequest", | |||
| { | |||
| "inputs": fields.Raw(required=True, description="Input variables"), | |||
| "query": fields.String(description="Query text", default=""), | |||
| "files": fields.List(fields.Raw(), description="Uploaded files"), | |||
| "model_config": fields.Raw(required=True, description="Model configuration"), | |||
| "response_mode": fields.String(enum=["blocking", "streaming"], description="Response mode"), | |||
| "retriever_from": fields.String(default="dev", description="Retriever source"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "Completion generated successfully") | |||
| @api.response(400, "Invalid request parameters") | |||
| @api.response(404, "App not found") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -86,7 +106,12 @@ class CompletionMessageApi(Resource): | |||
| raise InternalServerError() | |||
| @console_ns.route("/apps/<uuid:app_id>/completion-messages/<string:task_id>/stop") | |||
| class CompletionMessageStopApi(Resource): | |||
| @api.doc("stop_completion_message") | |||
| @api.doc(description="Stop a running completion message generation") | |||
| @api.doc(params={"app_id": "Application ID", "task_id": "Task ID to stop"}) | |||
| @api.response(200, "Task stopped successfully") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -99,7 +124,29 @@ class CompletionMessageStopApi(Resource): | |||
| return {"result": "success"}, 200 | |||
| @console_ns.route("/apps/<uuid:app_id>/chat-messages") | |||
| class ChatMessageApi(Resource): | |||
| @api.doc("create_chat_message") | |||
| @api.doc(description="Generate chat message for debugging") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.model( | |||
| "ChatMessageRequest", | |||
| { | |||
| "inputs": fields.Raw(required=True, description="Input variables"), | |||
| "query": fields.String(required=True, description="User query"), | |||
| "files": fields.List(fields.Raw(), description="Uploaded files"), | |||
| "model_config": fields.Raw(required=True, description="Model configuration"), | |||
| "conversation_id": fields.String(description="Conversation ID"), | |||
| "parent_message_id": fields.String(description="Parent message ID"), | |||
| "response_mode": fields.String(enum=["blocking", "streaming"], description="Response mode"), | |||
| "retriever_from": fields.String(default="dev", description="Retriever source"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "Chat message generated successfully") | |||
| @api.response(400, "Invalid request parameters") | |||
| @api.response(404, "App or conversation not found") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -161,7 +208,12 @@ class ChatMessageApi(Resource): | |||
| raise InternalServerError() | |||
| @console_ns.route("/apps/<uuid:app_id>/chat-messages/<string:task_id>/stop") | |||
| class ChatMessageStopApi(Resource): | |||
| @api.doc("stop_chat_message") | |||
| @api.doc(description="Stop a running chat message generation") | |||
| @api.doc(params={"app_id": "Application ID", "task_id": "Task ID to stop"}) | |||
| @api.response(200, "Task stopped successfully") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -172,9 +224,3 @@ class ChatMessageStopApi(Resource): | |||
| AppQueueManager.set_stop_flag(task_id, InvokeFrom.DEBUGGER, current_user.id) | |||
| return {"result": "success"}, 200 | |||
| api.add_resource(CompletionMessageApi, "/apps/<uuid:app_id>/completion-messages") | |||
| api.add_resource(CompletionMessageStopApi, "/apps/<uuid:app_id>/completion-messages/<string:task_id>/stop") | |||
| api.add_resource(ChatMessageApi, "/apps/<uuid:app_id>/chat-messages") | |||
| api.add_resource(ChatMessageStopApi, "/apps/<uuid:app_id>/chat-messages/<string:task_id>/stop") | |||
| @@ -8,7 +8,7 @@ from sqlalchemy import func, or_ | |||
| from sqlalchemy.orm import joinedload | |||
| from werkzeug.exceptions import Forbidden, NotFound | |||
| from controllers.console import api | |||
| from controllers.console import api, console_ns | |||
| from controllers.console.app.wraps import get_app_model | |||
| from controllers.console.wraps import account_initialization_required, setup_required | |||
| from core.app.entities.app_invoke_entities import InvokeFrom | |||
| @@ -28,7 +28,29 @@ from services.conversation_service import ConversationService | |||
| from services.errors.conversation import ConversationNotExistsError | |||
| @console_ns.route("/apps/<uuid:app_id>/completion-conversations") | |||
| class CompletionConversationApi(Resource): | |||
| @api.doc("list_completion_conversations") | |||
| @api.doc(description="Get completion conversations with pagination and filtering") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.parser() | |||
| .add_argument("keyword", type=str, location="args", help="Search keyword") | |||
| .add_argument("start", type=str, location="args", help="Start date (YYYY-MM-DD HH:MM)") | |||
| .add_argument("end", type=str, location="args", help="End date (YYYY-MM-DD HH:MM)") | |||
| .add_argument( | |||
| "annotation_status", | |||
| type=str, | |||
| location="args", | |||
| choices=["annotated", "not_annotated", "all"], | |||
| default="all", | |||
| help="Annotation status filter", | |||
| ) | |||
| .add_argument("page", type=int, location="args", default=1, help="Page number") | |||
| .add_argument("limit", type=int, location="args", default=20, help="Page size (1-100)") | |||
| ) | |||
| @api.response(200, "Success", conversation_pagination_fields) | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -101,7 +123,14 @@ class CompletionConversationApi(Resource): | |||
| return conversations | |||
| @console_ns.route("/apps/<uuid:app_id>/completion-conversations/<uuid:conversation_id>") | |||
| class CompletionConversationDetailApi(Resource): | |||
| @api.doc("get_completion_conversation") | |||
| @api.doc(description="Get completion conversation details with messages") | |||
| @api.doc(params={"app_id": "Application ID", "conversation_id": "Conversation ID"}) | |||
| @api.response(200, "Success", conversation_message_detail_fields) | |||
| @api.response(403, "Insufficient permissions") | |||
| @api.response(404, "Conversation not found") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -114,6 +143,12 @@ class CompletionConversationDetailApi(Resource): | |||
| return _get_conversation(app_model, conversation_id) | |||
| @api.doc("delete_completion_conversation") | |||
| @api.doc(description="Delete a completion conversation") | |||
| @api.doc(params={"app_id": "Application ID", "conversation_id": "Conversation ID"}) | |||
| @api.response(204, "Conversation deleted successfully") | |||
| @api.response(403, "Insufficient permissions") | |||
| @api.response(404, "Conversation not found") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -133,7 +168,38 @@ class CompletionConversationDetailApi(Resource): | |||
| return {"result": "success"}, 204 | |||
| @console_ns.route("/apps/<uuid:app_id>/chat-conversations") | |||
| class ChatConversationApi(Resource): | |||
| @api.doc("list_chat_conversations") | |||
| @api.doc(description="Get chat conversations with pagination, filtering and summary") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.parser() | |||
| .add_argument("keyword", type=str, location="args", help="Search keyword") | |||
| .add_argument("start", type=str, location="args", help="Start date (YYYY-MM-DD HH:MM)") | |||
| .add_argument("end", type=str, location="args", help="End date (YYYY-MM-DD HH:MM)") | |||
| .add_argument( | |||
| "annotation_status", | |||
| type=str, | |||
| location="args", | |||
| choices=["annotated", "not_annotated", "all"], | |||
| default="all", | |||
| help="Annotation status filter", | |||
| ) | |||
| .add_argument("message_count_gte", type=int, location="args", help="Minimum message count") | |||
| .add_argument("page", type=int, location="args", default=1, help="Page number") | |||
| .add_argument("limit", type=int, location="args", default=20, help="Page size (1-100)") | |||
| .add_argument( | |||
| "sort_by", | |||
| type=str, | |||
| location="args", | |||
| choices=["created_at", "-created_at", "updated_at", "-updated_at"], | |||
| default="-updated_at", | |||
| help="Sort field and direction", | |||
| ) | |||
| ) | |||
| @api.response(200, "Success", conversation_with_summary_pagination_fields) | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -261,7 +327,14 @@ class ChatConversationApi(Resource): | |||
| return conversations | |||
| @console_ns.route("/apps/<uuid:app_id>/chat-conversations/<uuid:conversation_id>") | |||
| class ChatConversationDetailApi(Resource): | |||
| @api.doc("get_chat_conversation") | |||
| @api.doc(description="Get chat conversation details") | |||
| @api.doc(params={"app_id": "Application ID", "conversation_id": "Conversation ID"}) | |||
| @api.response(200, "Success", conversation_detail_fields) | |||
| @api.response(403, "Insufficient permissions") | |||
| @api.response(404, "Conversation not found") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -274,6 +347,12 @@ class ChatConversationDetailApi(Resource): | |||
| return _get_conversation(app_model, conversation_id) | |||
| @api.doc("delete_chat_conversation") | |||
| @api.doc(description="Delete a chat conversation") | |||
| @api.doc(params={"app_id": "Application ID", "conversation_id": "Conversation ID"}) | |||
| @api.response(204, "Conversation deleted successfully") | |||
| @api.response(403, "Insufficient permissions") | |||
| @api.response(404, "Conversation not found") | |||
| @setup_required | |||
| @login_required | |||
| @get_app_model(mode=[AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT]) | |||
| @@ -293,12 +372,6 @@ class ChatConversationDetailApi(Resource): | |||
| return {"result": "success"}, 204 | |||
| api.add_resource(CompletionConversationApi, "/apps/<uuid:app_id>/completion-conversations") | |||
| api.add_resource(CompletionConversationDetailApi, "/apps/<uuid:app_id>/completion-conversations/<uuid:conversation_id>") | |||
| api.add_resource(ChatConversationApi, "/apps/<uuid:app_id>/chat-conversations") | |||
| api.add_resource(ChatConversationDetailApi, "/apps/<uuid:app_id>/chat-conversations/<uuid:conversation_id>") | |||
| def _get_conversation(app_model, conversation_id): | |||
| conversation = ( | |||
| db.session.query(Conversation) | |||
| @@ -2,7 +2,7 @@ from flask_restx import Resource, marshal_with, reqparse | |||
| from sqlalchemy import select | |||
| from sqlalchemy.orm import Session | |||
| from controllers.console import api | |||
| from controllers.console import api, console_ns | |||
| from controllers.console.app.wraps import get_app_model | |||
| from controllers.console.wraps import account_initialization_required, setup_required | |||
| from extensions.ext_database import db | |||
| @@ -12,7 +12,17 @@ from models import ConversationVariable | |||
| from models.model import AppMode | |||
| @console_ns.route("/apps/<uuid:app_id>/conversation-variables") | |||
| class ConversationVariablesApi(Resource): | |||
| @api.doc("get_conversation_variables") | |||
| @api.doc(description="Get conversation variables for an application") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.parser().add_argument( | |||
| "conversation_id", type=str, location="args", help="Conversation ID to filter variables" | |||
| ) | |||
| ) | |||
| @api.response(200, "Conversation variables retrieved successfully", paginated_conversation_variable_fields) | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -55,6 +65,3 @@ class ConversationVariablesApi(Resource): | |||
| for row in rows | |||
| ], | |||
| } | |||
| api.add_resource(ConversationVariablesApi, "/apps/<uuid:app_id>/conversation-variables") | |||
| @@ -1,9 +1,9 @@ | |||
| from collections.abc import Sequence | |||
| from flask_login import current_user | |||
| from flask_restx import Resource, reqparse | |||
| from flask_restx import Resource, fields, reqparse | |||
| from controllers.console import api | |||
| from controllers.console import api, console_ns | |||
| from controllers.console.app.error import ( | |||
| CompletionRequestError, | |||
| ProviderModelCurrentlyNotSupportError, | |||
| @@ -19,7 +19,23 @@ from core.model_runtime.errors.invoke import InvokeError | |||
| from libs.login import login_required | |||
| @console_ns.route("/rule-generate") | |||
| class RuleGenerateApi(Resource): | |||
| @api.doc("generate_rule_config") | |||
| @api.doc(description="Generate rule configuration using LLM") | |||
| @api.expect( | |||
| api.model( | |||
| "RuleGenerateRequest", | |||
| { | |||
| "instruction": fields.String(required=True, description="Rule generation instruction"), | |||
| "model_config": fields.Raw(required=True, description="Model configuration"), | |||
| "no_variable": fields.Boolean(required=True, default=False, description="Whether to exclude variables"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "Rule configuration generated successfully") | |||
| @api.response(400, "Invalid request parameters") | |||
| @api.response(402, "Provider quota exceeded") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -50,7 +66,26 @@ class RuleGenerateApi(Resource): | |||
| return rules | |||
| @console_ns.route("/rule-code-generate") | |||
| class RuleCodeGenerateApi(Resource): | |||
| @api.doc("generate_rule_code") | |||
| @api.doc(description="Generate code rules using LLM") | |||
| @api.expect( | |||
| api.model( | |||
| "RuleCodeGenerateRequest", | |||
| { | |||
| "instruction": fields.String(required=True, description="Code generation instruction"), | |||
| "model_config": fields.Raw(required=True, description="Model configuration"), | |||
| "no_variable": fields.Boolean(required=True, default=False, description="Whether to exclude variables"), | |||
| "code_language": fields.String( | |||
| default="javascript", description="Programming language for code generation" | |||
| ), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "Code rules generated successfully") | |||
| @api.response(400, "Invalid request parameters") | |||
| @api.response(402, "Provider quota exceeded") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -82,7 +117,22 @@ class RuleCodeGenerateApi(Resource): | |||
| return code_result | |||
| @console_ns.route("/rule-structured-output-generate") | |||
| class RuleStructuredOutputGenerateApi(Resource): | |||
| @api.doc("generate_structured_output") | |||
| @api.doc(description="Generate structured output rules using LLM") | |||
| @api.expect( | |||
| api.model( | |||
| "StructuredOutputGenerateRequest", | |||
| { | |||
| "instruction": fields.String(required=True, description="Structured output generation instruction"), | |||
| "model_config": fields.Raw(required=True, description="Model configuration"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "Structured output generated successfully") | |||
| @api.response(400, "Invalid request parameters") | |||
| @api.response(402, "Provider quota exceeded") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -111,7 +161,27 @@ class RuleStructuredOutputGenerateApi(Resource): | |||
| return structured_output | |||
| @console_ns.route("/instruction-generate") | |||
| class InstructionGenerateApi(Resource): | |||
| @api.doc("generate_instruction") | |||
| @api.doc(description="Generate instruction for workflow nodes or general use") | |||
| @api.expect( | |||
| api.model( | |||
| "InstructionGenerateRequest", | |||
| { | |||
| "flow_id": fields.String(required=True, description="Workflow/Flow ID"), | |||
| "node_id": fields.String(description="Node ID for workflow context"), | |||
| "current": fields.String(description="Current instruction text"), | |||
| "language": fields.String(default="javascript", description="Programming language (javascript/python)"), | |||
| "instruction": fields.String(required=True, description="Instruction for generation"), | |||
| "model_config": fields.Raw(required=True, description="Model configuration"), | |||
| "ideal_output": fields.String(description="Expected ideal output"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "Instruction generated successfully") | |||
| @api.response(400, "Invalid request parameters or flow/workflow not found") | |||
| @api.response(402, "Provider quota exceeded") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -203,7 +273,21 @@ class InstructionGenerateApi(Resource): | |||
| raise CompletionRequestError(e.description) | |||
| @console_ns.route("/instruction-generate/template") | |||
| class InstructionGenerationTemplateApi(Resource): | |||
| @api.doc("get_instruction_template") | |||
| @api.doc(description="Get instruction generation template") | |||
| @api.expect( | |||
| api.model( | |||
| "InstructionTemplateRequest", | |||
| { | |||
| "instruction": fields.String(required=True, description="Template instruction"), | |||
| "ideal_output": fields.String(description="Expected ideal output"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "Template retrieved successfully") | |||
| @api.response(400, "Invalid request parameters") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -222,10 +306,3 @@ class InstructionGenerationTemplateApi(Resource): | |||
| return {"data": INSTRUCTION_GENERATE_TEMPLATE_CODE} | |||
| case _: | |||
| raise ValueError(f"Invalid type: {args['type']}") | |||
| api.add_resource(RuleGenerateApi, "/rule-generate") | |||
| api.add_resource(RuleCodeGenerateApi, "/rule-code-generate") | |||
| api.add_resource(RuleStructuredOutputGenerateApi, "/rule-structured-output-generate") | |||
| api.add_resource(InstructionGenerateApi, "/instruction-generate") | |||
| api.add_resource(InstructionGenerationTemplateApi, "/instruction-generate/template") | |||
| @@ -2,10 +2,10 @@ import json | |||
| from enum import StrEnum | |||
| from flask_login import current_user | |||
| from flask_restx import Resource, marshal_with, reqparse | |||
| from flask_restx import Resource, fields, marshal_with, reqparse | |||
| from werkzeug.exceptions import NotFound | |||
| from controllers.console import api | |||
| from controllers.console import api, console_ns | |||
| from controllers.console.app.wraps import get_app_model | |||
| from controllers.console.wraps import account_initialization_required, setup_required | |||
| from extensions.ext_database import db | |||
| @@ -19,7 +19,12 @@ class AppMCPServerStatus(StrEnum): | |||
| INACTIVE = "inactive" | |||
| @console_ns.route("/apps/<uuid:app_id>/server") | |||
| class AppMCPServerController(Resource): | |||
| @api.doc("get_app_mcp_server") | |||
| @api.doc(description="Get MCP server configuration for an application") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.response(200, "MCP server configuration retrieved successfully", app_server_fields) | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -29,6 +34,20 @@ class AppMCPServerController(Resource): | |||
| server = db.session.query(AppMCPServer).where(AppMCPServer.app_id == app_model.id).first() | |||
| return server | |||
| @api.doc("create_app_mcp_server") | |||
| @api.doc(description="Create MCP server configuration for an application") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.model( | |||
| "MCPServerCreateRequest", | |||
| { | |||
| "description": fields.String(description="Server description"), | |||
| "parameters": fields.Raw(required=True, description="Server parameters configuration"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(201, "MCP server configuration created successfully", app_server_fields) | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -59,6 +78,23 @@ class AppMCPServerController(Resource): | |||
| db.session.commit() | |||
| return server | |||
| @api.doc("update_app_mcp_server") | |||
| @api.doc(description="Update MCP server configuration for an application") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.model( | |||
| "MCPServerUpdateRequest", | |||
| { | |||
| "id": fields.String(required=True, description="Server ID"), | |||
| "description": fields.String(description="Server description"), | |||
| "parameters": fields.Raw(required=True, description="Server parameters configuration"), | |||
| "status": fields.String(description="Server status"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "MCP server configuration updated successfully", app_server_fields) | |||
| @api.response(403, "Insufficient permissions") | |||
| @api.response(404, "Server not found") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -94,7 +130,14 @@ class AppMCPServerController(Resource): | |||
| return server | |||
| @console_ns.route("/apps/<uuid:server_id>/server/refresh") | |||
| class AppMCPServerRefreshController(Resource): | |||
| @api.doc("refresh_app_mcp_server") | |||
| @api.doc(description="Refresh MCP server configuration and regenerate server code") | |||
| @api.doc(params={"server_id": "Server ID"}) | |||
| @api.response(200, "MCP server refreshed successfully", app_server_fields) | |||
| @api.response(403, "Insufficient permissions") | |||
| @api.response(404, "Server not found") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -113,7 +156,3 @@ class AppMCPServerRefreshController(Resource): | |||
| server.server_code = AppMCPServer.generate_server_code(16) | |||
| db.session.commit() | |||
| return server | |||
| api.add_resource(AppMCPServerController, "/apps/<uuid:app_id>/server") | |||
| api.add_resource(AppMCPServerRefreshController, "/apps/<uuid:server_id>/server/refresh") | |||
| @@ -5,7 +5,7 @@ from flask_restx.inputs import int_range | |||
| from sqlalchemy import exists, select | |||
| from werkzeug.exceptions import Forbidden, InternalServerError, NotFound | |||
| from controllers.console import api | |||
| from controllers.console import api, console_ns | |||
| from controllers.console.app.error import ( | |||
| CompletionRequestError, | |||
| ProviderModelCurrentlyNotSupportError, | |||
| @@ -37,6 +37,7 @@ from services.message_service import MessageService | |||
| logger = logging.getLogger(__name__) | |||
| @console_ns.route("/apps/<uuid:app_id>/chat-messages") | |||
| class ChatMessageListApi(Resource): | |||
| message_infinite_scroll_pagination_fields = { | |||
| "limit": fields.Integer, | |||
| @@ -44,6 +45,17 @@ class ChatMessageListApi(Resource): | |||
| "data": fields.List(fields.Nested(message_detail_fields)), | |||
| } | |||
| @api.doc("list_chat_messages") | |||
| @api.doc(description="Get chat messages for a conversation with pagination") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.parser() | |||
| .add_argument("conversation_id", type=str, required=True, location="args", help="Conversation ID") | |||
| .add_argument("first_id", type=str, location="args", help="First message ID for pagination") | |||
| .add_argument("limit", type=int, location="args", default=20, help="Number of messages to return (1-100)") | |||
| ) | |||
| @api.response(200, "Success", message_infinite_scroll_pagination_fields) | |||
| @api.response(404, "Conversation not found") | |||
| @setup_required | |||
| @login_required | |||
| @get_app_model(mode=[AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT]) | |||
| @@ -117,7 +129,23 @@ class ChatMessageListApi(Resource): | |||
| return InfiniteScrollPagination(data=history_messages, limit=args["limit"], has_more=has_more) | |||
| @console_ns.route("/apps/<uuid:app_id>/feedbacks") | |||
| class MessageFeedbackApi(Resource): | |||
| @api.doc("create_message_feedback") | |||
| @api.doc(description="Create or update message feedback (like/dislike)") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.model( | |||
| "MessageFeedbackRequest", | |||
| { | |||
| "message_id": fields.String(required=True, description="Message ID"), | |||
| "rating": fields.String(enum=["like", "dislike"], description="Feedback rating"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "Feedback updated successfully") | |||
| @api.response(404, "Message not found") | |||
| @api.response(403, "Insufficient permissions") | |||
| @get_app_model | |||
| @setup_required | |||
| @login_required | |||
| @@ -162,7 +190,24 @@ class MessageFeedbackApi(Resource): | |||
| return {"result": "success"} | |||
| @console_ns.route("/apps/<uuid:app_id>/annotations") | |||
| class MessageAnnotationApi(Resource): | |||
| @api.doc("create_message_annotation") | |||
| @api.doc(description="Create message annotation") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.model( | |||
| "MessageAnnotationRequest", | |||
| { | |||
| "message_id": fields.String(description="Message ID"), | |||
| "question": fields.String(required=True, description="Question text"), | |||
| "answer": fields.String(required=True, description="Answer text"), | |||
| "annotation_reply": fields.Raw(description="Annotation reply"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "Annotation created successfully", annotation_fields) | |||
| @api.response(403, "Insufficient permissions") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -186,7 +231,16 @@ class MessageAnnotationApi(Resource): | |||
| return annotation | |||
| @console_ns.route("/apps/<uuid:app_id>/annotations/count") | |||
| class MessageAnnotationCountApi(Resource): | |||
| @api.doc("get_annotation_count") | |||
| @api.doc(description="Get count of message annotations for the app") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.response( | |||
| 200, | |||
| "Annotation count retrieved successfully", | |||
| api.model("AnnotationCountResponse", {"count": fields.Integer(description="Number of annotations")}), | |||
| ) | |||
| @get_app_model | |||
| @setup_required | |||
| @login_required | |||
| @@ -197,7 +251,17 @@ class MessageAnnotationCountApi(Resource): | |||
| return {"count": count} | |||
| @console_ns.route("/apps/<uuid:app_id>/chat-messages/<uuid:message_id>/suggested-questions") | |||
| class MessageSuggestedQuestionApi(Resource): | |||
| @api.doc("get_message_suggested_questions") | |||
| @api.doc(description="Get suggested questions for a message") | |||
| @api.doc(params={"app_id": "Application ID", "message_id": "Message ID"}) | |||
| @api.response( | |||
| 200, | |||
| "Suggested questions retrieved successfully", | |||
| api.model("SuggestedQuestionsResponse", {"data": fields.List(fields.String(description="Suggested question"))}), | |||
| ) | |||
| @api.response(404, "Message or conversation not found") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -230,7 +294,13 @@ class MessageSuggestedQuestionApi(Resource): | |||
| return {"data": questions} | |||
| @console_ns.route("/apps/<uuid:app_id>/messages/<uuid:message_id>") | |||
| class MessageApi(Resource): | |||
| @api.doc("get_message") | |||
| @api.doc(description="Get message details by ID") | |||
| @api.doc(params={"app_id": "Application ID", "message_id": "Message ID"}) | |||
| @api.response(200, "Message retrieved successfully", message_detail_fields) | |||
| @api.response(404, "Message not found") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -245,11 +315,3 @@ class MessageApi(Resource): | |||
| raise NotFound("Message Not Exists.") | |||
| return message | |||
| api.add_resource(MessageSuggestedQuestionApi, "/apps/<uuid:app_id>/chat-messages/<uuid:message_id>/suggested-questions") | |||
| api.add_resource(ChatMessageListApi, "/apps/<uuid:app_id>/chat-messages", endpoint="console_chat_messages") | |||
| api.add_resource(MessageFeedbackApi, "/apps/<uuid:app_id>/feedbacks") | |||
| api.add_resource(MessageAnnotationApi, "/apps/<uuid:app_id>/annotations") | |||
| api.add_resource(MessageAnnotationCountApi, "/apps/<uuid:app_id>/annotations/count") | |||
| api.add_resource(MessageApi, "/apps/<uuid:app_id>/messages/<uuid:message_id>", endpoint="console_message") | |||
| @@ -2,10 +2,11 @@ import json | |||
| from typing import cast | |||
| from flask import request | |||
| from flask_restx import Resource | |||
| from flask_login import current_user | |||
| from flask_restx import Resource, fields | |||
| from werkzeug.exceptions import Forbidden | |||
| from controllers.console import api | |||
| from controllers.console import api, console_ns | |||
| from controllers.console.app.wraps import get_app_model | |||
| from controllers.console.wraps import account_initialization_required, setup_required | |||
| from core.agent.entities import AgentToolEntity | |||
| @@ -13,13 +14,39 @@ from core.tools.tool_manager import ToolManager | |||
| from core.tools.utils.configuration import ToolParameterConfigurationManager | |||
| from events.app_event import app_model_config_was_updated | |||
| from extensions.ext_database import db | |||
| from libs.login import current_user, login_required | |||
| from libs.login import login_required | |||
| from models.account import Account | |||
| from models.model import AppMode, AppModelConfig | |||
| from services.app_model_config_service import AppModelConfigService | |||
| @console_ns.route("/apps/<uuid:app_id>/model-config") | |||
| class ModelConfigResource(Resource): | |||
| @api.doc("update_app_model_config") | |||
| @api.doc(description="Update application model configuration") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.model( | |||
| "ModelConfigRequest", | |||
| { | |||
| "provider": fields.String(description="Model provider"), | |||
| "model": fields.String(description="Model name"), | |||
| "configs": fields.Raw(description="Model configuration parameters"), | |||
| "opening_statement": fields.String(description="Opening statement"), | |||
| "suggested_questions": fields.List(fields.String(), description="Suggested questions"), | |||
| "more_like_this": fields.Raw(description="More like this configuration"), | |||
| "speech_to_text": fields.Raw(description="Speech to text configuration"), | |||
| "text_to_speech": fields.Raw(description="Text to speech configuration"), | |||
| "retrieval_model": fields.Raw(description="Retrieval model configuration"), | |||
| "tools": fields.List(fields.Raw(), description="Available tools"), | |||
| "dataset_configs": fields.Raw(description="Dataset configurations"), | |||
| "agent_mode": fields.Raw(description="Agent mode configuration"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "Model configuration updated successfully") | |||
| @api.response(400, "Invalid configuration") | |||
| @api.response(404, "App not found") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -150,6 +177,3 @@ class ModelConfigResource(Resource): | |||
| app_model_config_was_updated.send(app_model, app_model_config=new_app_model_config) | |||
| return {"result": "success"} | |||
| api.add_resource(ModelConfigResource, "/apps/<uuid:app_id>/model-config") | |||
| @@ -1,18 +1,31 @@ | |||
| from flask_restx import Resource, reqparse | |||
| from flask_restx import Resource, fields, reqparse | |||
| from werkzeug.exceptions import BadRequest | |||
| from controllers.console import api | |||
| from controllers.console import api, console_ns | |||
| from controllers.console.app.error import TracingConfigCheckError, TracingConfigIsExist, TracingConfigNotExist | |||
| from controllers.console.wraps import account_initialization_required, setup_required | |||
| from libs.login import login_required | |||
| from services.ops_service import OpsService | |||
| @console_ns.route("/apps/<uuid:app_id>/trace-config") | |||
| class TraceAppConfigApi(Resource): | |||
| """ | |||
| Manage trace app configurations | |||
| """ | |||
| @api.doc("get_trace_app_config") | |||
| @api.doc(description="Get tracing configuration for an application") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.parser().add_argument( | |||
| "tracing_provider", type=str, required=True, location="args", help="Tracing provider name" | |||
| ) | |||
| ) | |||
| @api.response( | |||
| 200, "Tracing configuration retrieved successfully", fields.Raw(description="Tracing configuration data") | |||
| ) | |||
| @api.response(400, "Invalid request parameters") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -29,6 +42,22 @@ class TraceAppConfigApi(Resource): | |||
| except Exception as e: | |||
| raise BadRequest(str(e)) | |||
| @api.doc("create_trace_app_config") | |||
| @api.doc(description="Create a new tracing configuration for an application") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.model( | |||
| "TraceConfigCreateRequest", | |||
| { | |||
| "tracing_provider": fields.String(required=True, description="Tracing provider name"), | |||
| "tracing_config": fields.Raw(required=True, description="Tracing configuration data"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response( | |||
| 201, "Tracing configuration created successfully", fields.Raw(description="Created configuration data") | |||
| ) | |||
| @api.response(400, "Invalid request parameters or configuration already exists") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -51,6 +80,20 @@ class TraceAppConfigApi(Resource): | |||
| except Exception as e: | |||
| raise BadRequest(str(e)) | |||
| @api.doc("update_trace_app_config") | |||
| @api.doc(description="Update an existing tracing configuration for an application") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.model( | |||
| "TraceConfigUpdateRequest", | |||
| { | |||
| "tracing_provider": fields.String(required=True, description="Tracing provider name"), | |||
| "tracing_config": fields.Raw(required=True, description="Updated tracing configuration data"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "Tracing configuration updated successfully", fields.Raw(description="Success response")) | |||
| @api.response(400, "Invalid request parameters or configuration not found") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -71,6 +114,16 @@ class TraceAppConfigApi(Resource): | |||
| except Exception as e: | |||
| raise BadRequest(str(e)) | |||
| @api.doc("delete_trace_app_config") | |||
| @api.doc(description="Delete an existing tracing configuration for an application") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.parser().add_argument( | |||
| "tracing_provider", type=str, required=True, location="args", help="Tracing provider name" | |||
| ) | |||
| ) | |||
| @api.response(204, "Tracing configuration deleted successfully") | |||
| @api.response(400, "Invalid request parameters or configuration not found") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -87,6 +140,3 @@ class TraceAppConfigApi(Resource): | |||
| return {"result": "success"}, 204 | |||
| except Exception as e: | |||
| raise BadRequest(str(e)) | |||
| api.add_resource(TraceAppConfigApi, "/apps/<uuid:app_id>/trace-config") | |||
| @@ -1,9 +1,9 @@ | |||
| from flask_login import current_user | |||
| from flask_restx import Resource, marshal_with, reqparse | |||
| from flask_restx import Resource, fields, marshal_with, reqparse | |||
| from werkzeug.exceptions import Forbidden, NotFound | |||
| from constants.languages import supported_language | |||
| from controllers.console import api | |||
| from controllers.console import api, console_ns | |||
| from controllers.console.app.wraps import get_app_model | |||
| from controllers.console.wraps import account_initialization_required, setup_required | |||
| from extensions.ext_database import db | |||
| @@ -36,7 +36,39 @@ def parse_app_site_args(): | |||
| return parser.parse_args() | |||
| @console_ns.route("/apps/<uuid:app_id>/site") | |||
| class AppSite(Resource): | |||
| @api.doc("update_app_site") | |||
| @api.doc(description="Update application site configuration") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.model( | |||
| "AppSiteRequest", | |||
| { | |||
| "title": fields.String(description="Site title"), | |||
| "icon_type": fields.String(description="Icon type"), | |||
| "icon": fields.String(description="Icon"), | |||
| "icon_background": fields.String(description="Icon background color"), | |||
| "description": fields.String(description="Site description"), | |||
| "default_language": fields.String(description="Default language"), | |||
| "chat_color_theme": fields.String(description="Chat color theme"), | |||
| "chat_color_theme_inverted": fields.Boolean(description="Inverted chat color theme"), | |||
| "customize_domain": fields.String(description="Custom domain"), | |||
| "copyright": fields.String(description="Copyright text"), | |||
| "privacy_policy": fields.String(description="Privacy policy"), | |||
| "custom_disclaimer": fields.String(description="Custom disclaimer"), | |||
| "customize_token_strategy": fields.String( | |||
| enum=["must", "allow", "not_allow"], description="Token strategy" | |||
| ), | |||
| "prompt_public": fields.Boolean(description="Make prompt public"), | |||
| "show_workflow_steps": fields.Boolean(description="Show workflow steps"), | |||
| "use_icon_as_answer_icon": fields.Boolean(description="Use icon as answer icon"), | |||
| }, | |||
| ) | |||
| ) | |||
| @api.response(200, "Site configuration updated successfully", app_site_fields) | |||
| @api.response(403, "Insufficient permissions") | |||
| @api.response(404, "App not found") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -84,7 +116,14 @@ class AppSite(Resource): | |||
| return site | |||
| @console_ns.route("/apps/<uuid:app_id>/site/access-token-reset") | |||
| class AppSiteAccessTokenReset(Resource): | |||
| @api.doc("reset_app_site_access_token") | |||
| @api.doc(description="Reset access token for application site") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.response(200, "Access token reset successfully", app_site_fields) | |||
| @api.response(403, "Insufficient permissions (admin/owner required)") | |||
| @api.response(404, "App or site not found") | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -108,7 +147,3 @@ class AppSiteAccessTokenReset(Resource): | |||
| db.session.commit() | |||
| return site | |||
| api.add_resource(AppSite, "/apps/<uuid:app_id>/site") | |||
| api.add_resource(AppSiteAccessTokenReset, "/apps/<uuid:app_id>/site/access-token-reset") | |||
| @@ -5,9 +5,9 @@ import pytz | |||
| import sqlalchemy as sa | |||
| from flask import jsonify | |||
| from flask_login import current_user | |||
| from flask_restx import Resource, reqparse | |||
| from flask_restx import Resource, fields, reqparse | |||
| from controllers.console import api | |||
| from controllers.console import api, console_ns | |||
| from controllers.console.app.wraps import get_app_model | |||
| from controllers.console.wraps import account_initialization_required, setup_required | |||
| from core.app.entities.app_invoke_entities import InvokeFrom | |||
| @@ -17,7 +17,21 @@ from libs.login import login_required | |||
| from models import AppMode, Message | |||
| @console_ns.route("/apps/<uuid:app_id>/statistics/daily-messages") | |||
| class DailyMessageStatistic(Resource): | |||
| @api.doc("get_daily_message_statistics") | |||
| @api.doc(description="Get daily message statistics for an application") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.parser() | |||
| .add_argument("start", type=str, location="args", help="Start date (YYYY-MM-DD HH:MM)") | |||
| .add_argument("end", type=str, location="args", help="End date (YYYY-MM-DD HH:MM)") | |||
| ) | |||
| @api.response( | |||
| 200, | |||
| "Daily message statistics retrieved successfully", | |||
| fields.List(fields.Raw(description="Daily message count data")), | |||
| ) | |||
| @get_app_model | |||
| @setup_required | |||
| @login_required | |||
| @@ -74,7 +88,21 @@ WHERE | |||
| return jsonify({"data": response_data}) | |||
| @console_ns.route("/apps/<uuid:app_id>/statistics/daily-conversations") | |||
| class DailyConversationStatistic(Resource): | |||
| @api.doc("get_daily_conversation_statistics") | |||
| @api.doc(description="Get daily conversation statistics for an application") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.parser() | |||
| .add_argument("start", type=str, location="args", help="Start date (YYYY-MM-DD HH:MM)") | |||
| .add_argument("end", type=str, location="args", help="End date (YYYY-MM-DD HH:MM)") | |||
| ) | |||
| @api.response( | |||
| 200, | |||
| "Daily conversation statistics retrieved successfully", | |||
| fields.List(fields.Raw(description="Daily conversation count data")), | |||
| ) | |||
| @get_app_model | |||
| @setup_required | |||
| @login_required | |||
| @@ -126,7 +154,21 @@ class DailyConversationStatistic(Resource): | |||
| return jsonify({"data": response_data}) | |||
| @console_ns.route("/apps/<uuid:app_id>/statistics/daily-end-users") | |||
| class DailyTerminalsStatistic(Resource): | |||
| @api.doc("get_daily_terminals_statistics") | |||
| @api.doc(description="Get daily terminal/end-user statistics for an application") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.parser() | |||
| .add_argument("start", type=str, location="args", help="Start date (YYYY-MM-DD HH:MM)") | |||
| .add_argument("end", type=str, location="args", help="End date (YYYY-MM-DD HH:MM)") | |||
| ) | |||
| @api.response( | |||
| 200, | |||
| "Daily terminal statistics retrieved successfully", | |||
| fields.List(fields.Raw(description="Daily terminal count data")), | |||
| ) | |||
| @get_app_model | |||
| @setup_required | |||
| @login_required | |||
| @@ -183,7 +225,21 @@ WHERE | |||
| return jsonify({"data": response_data}) | |||
| @console_ns.route("/apps/<uuid:app_id>/statistics/token-costs") | |||
| class DailyTokenCostStatistic(Resource): | |||
| @api.doc("get_daily_token_cost_statistics") | |||
| @api.doc(description="Get daily token cost statistics for an application") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.parser() | |||
| .add_argument("start", type=str, location="args", help="Start date (YYYY-MM-DD HH:MM)") | |||
| .add_argument("end", type=str, location="args", help="End date (YYYY-MM-DD HH:MM)") | |||
| ) | |||
| @api.response( | |||
| 200, | |||
| "Daily token cost statistics retrieved successfully", | |||
| fields.List(fields.Raw(description="Daily token cost data")), | |||
| ) | |||
| @get_app_model | |||
| @setup_required | |||
| @login_required | |||
| @@ -243,7 +299,21 @@ WHERE | |||
| return jsonify({"data": response_data}) | |||
| @console_ns.route("/apps/<uuid:app_id>/statistics/average-session-interactions") | |||
| class AverageSessionInteractionStatistic(Resource): | |||
| @api.doc("get_average_session_interaction_statistics") | |||
| @api.doc(description="Get average session interaction statistics for an application") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.parser() | |||
| .add_argument("start", type=str, location="args", help="Start date (YYYY-MM-DD HH:MM)") | |||
| .add_argument("end", type=str, location="args", help="End date (YYYY-MM-DD HH:MM)") | |||
| ) | |||
| @api.response( | |||
| 200, | |||
| "Average session interaction statistics retrieved successfully", | |||
| fields.List(fields.Raw(description="Average session interaction data")), | |||
| ) | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -319,7 +389,21 @@ ORDER BY | |||
| return jsonify({"data": response_data}) | |||
| @console_ns.route("/apps/<uuid:app_id>/statistics/user-satisfaction-rate") | |||
| class UserSatisfactionRateStatistic(Resource): | |||
| @api.doc("get_user_satisfaction_rate_statistics") | |||
| @api.doc(description="Get user satisfaction rate statistics for an application") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.parser() | |||
| .add_argument("start", type=str, location="args", help="Start date (YYYY-MM-DD HH:MM)") | |||
| .add_argument("end", type=str, location="args", help="End date (YYYY-MM-DD HH:MM)") | |||
| ) | |||
| @api.response( | |||
| 200, | |||
| "User satisfaction rate statistics retrieved successfully", | |||
| fields.List(fields.Raw(description="User satisfaction rate data")), | |||
| ) | |||
| @get_app_model | |||
| @setup_required | |||
| @login_required | |||
| @@ -385,7 +469,21 @@ WHERE | |||
| return jsonify({"data": response_data}) | |||
| @console_ns.route("/apps/<uuid:app_id>/statistics/average-response-time") | |||
| class AverageResponseTimeStatistic(Resource): | |||
| @api.doc("get_average_response_time_statistics") | |||
| @api.doc(description="Get average response time statistics for an application") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.parser() | |||
| .add_argument("start", type=str, location="args", help="Start date (YYYY-MM-DD HH:MM)") | |||
| .add_argument("end", type=str, location="args", help="End date (YYYY-MM-DD HH:MM)") | |||
| ) | |||
| @api.response( | |||
| 200, | |||
| "Average response time statistics retrieved successfully", | |||
| fields.List(fields.Raw(description="Average response time data")), | |||
| ) | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @@ -442,7 +540,21 @@ WHERE | |||
| return jsonify({"data": response_data}) | |||
| @console_ns.route("/apps/<uuid:app_id>/statistics/tokens-per-second") | |||
| class TokensPerSecondStatistic(Resource): | |||
| @api.doc("get_tokens_per_second_statistics") | |||
| @api.doc(description="Get tokens per second statistics for an application") | |||
| @api.doc(params={"app_id": "Application ID"}) | |||
| @api.expect( | |||
| api.parser() | |||
| .add_argument("start", type=str, location="args", help="Start date (YYYY-MM-DD HH:MM)") | |||
| .add_argument("end", type=str, location="args", help="End date (YYYY-MM-DD HH:MM)") | |||
| ) | |||
| @api.response( | |||
| 200, | |||
| "Tokens per second statistics retrieved successfully", | |||
| fields.List(fields.Raw(description="Tokens per second data")), | |||
| ) | |||
| @get_app_model | |||
| @setup_required | |||
| @login_required | |||
| @@ -500,13 +612,3 @@ WHERE | |||
| response_data.append({"date": str(i.date), "tps": round(i.tokens_per_second, 4)}) | |||
| return jsonify({"data": response_data}) | |||
| api.add_resource(DailyMessageStatistic, "/apps/<uuid:app_id>/statistics/daily-messages") | |||
| api.add_resource(DailyConversationStatistic, "/apps/<uuid:app_id>/statistics/daily-conversations") | |||
| api.add_resource(DailyTerminalsStatistic, "/apps/<uuid:app_id>/statistics/daily-end-users") | |||
| api.add_resource(DailyTokenCostStatistic, "/apps/<uuid:app_id>/statistics/token-costs") | |||
| api.add_resource(AverageSessionInteractionStatistic, "/apps/<uuid:app_id>/statistics/average-session-interactions") | |||
| api.add_resource(UserSatisfactionRateStatistic, "/apps/<uuid:app_id>/statistics/user-satisfaction-rate") | |||
| api.add_resource(AverageResponseTimeStatistic, "/apps/<uuid:app_id>/statistics/average-response-time") | |||
| api.add_resource(TokensPerSecondStatistic, "/apps/<uuid:app_id>/statistics/tokens-per-second") | |||