Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions api/controllers/console/apikey.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@

api_key_list = {"data": fields.List(fields.Nested(api_key_fields), attribute="items")}

api_key_item_model = api.model("ApiKeyItem", api_key_fields)

api_key_list_model = api.model(
"ApiKeyList", {"data": fields.List(fields.Nested(api_key_item_model), attribute="items")}
)


def _get_resource(resource_id, tenant_id, resource_model):
if resource_model == App:
Expand Down Expand Up @@ -52,7 +58,7 @@ class BaseApiKeyListResource(Resource):
token_prefix: str | None = None
max_keys = 10

@marshal_with(api_key_list)
@marshal_with(api_key_list_model)
def get(self, resource_id):
assert self.resource_id_field is not None, "resource_id_field must be set"
resource_id = str(resource_id)
Expand All @@ -66,7 +72,7 @@ def get(self, resource_id):
).all()
return {"items": keys}

@marshal_with(api_key_fields)
@marshal_with(api_key_item_model)
@edit_permission_required
def post(self, resource_id):
assert self.resource_id_field is not None, "resource_id_field must be set"
Expand Down Expand Up @@ -139,15 +145,15 @@ class AppApiKeyListResource(BaseApiKeyListResource):
@api.doc("get_app_api_keys")
@api.doc(description="Get all API keys for an app")
@api.doc(params={"resource_id": "App ID"})
@api.response(200, "Success", api_key_list)
@api.response(200, "Success", api_key_list_model)
def get(self, resource_id):
"""Get all API keys for an app"""
return super().get(resource_id)

@api.doc("create_app_api_key")
@api.doc(description="Create a new API key for an app")
@api.doc(params={"resource_id": "App ID"})
@api.response(201, "API key created successfully", api_key_fields)
@api.response(201, "API key created successfully", api_key_item_model)
@api.response(400, "Maximum keys exceeded")
def post(self, resource_id):
"""Create a new API key for an app"""
Expand Down Expand Up @@ -179,15 +185,15 @@ class DatasetApiKeyListResource(BaseApiKeyListResource):
@api.doc("get_dataset_api_keys")
@api.doc(description="Get all API keys for a dataset")
@api.doc(params={"resource_id": "Dataset ID"})
@api.response(200, "Success", api_key_list)
@api.response(200, "Success", api_key_list_model)
def get(self, resource_id):
"""Get all API keys for a dataset"""
return super().get(resource_id)

@api.doc("create_dataset_api_key")
@api.doc(description="Create a new API key for a dataset")
@api.doc(params={"resource_id": "Dataset ID"})
@api.response(201, "API key created successfully", api_key_fields)
@api.response(201, "API key created successfully", api_key_item_model)
@api.response(400, "Maximum keys exceeded")
def post(self, resource_id):
"""Create a new API key for a dataset"""
Expand Down
18 changes: 14 additions & 4 deletions api/controllers/console/app/annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from fields.annotation_fields import (
annotation_fields,
annotation_hit_history_fields,
build_annotation_model,
)
from libs.helper import uuid_value
from libs.login import login_required
Expand Down Expand Up @@ -184,7 +185,7 @@ def get(self, app_id):
},
)
)
@api.response(201, "Annotation created successfully", annotation_fields)
@api.response(201, "Annotation created successfully", build_annotation_model(api))
@api.response(403, "Insufficient permissions")
@setup_required
@login_required
Expand Down Expand Up @@ -238,7 +239,11 @@ 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(
200,
"Annotations exported successfully",
api.model("AnnotationList", {"data": fields.List(fields.Nested(build_annotation_model(api)))}),
)
@api.response(403, "Insufficient permissions")
@setup_required
@login_required
Expand All @@ -263,7 +268,7 @@ 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(200, "Annotation updated successfully", build_annotation_model(api))
@api.response(204, "Annotation deleted successfully")
@api.response(403, "Insufficient permissions")
@api.expect(parser)
Expand Down Expand Up @@ -359,7 +364,12 @@ class AnnotationHitHistoryListApi(Resource):
.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))
200,
"Hit histories retrieved successfully",
api.model(
"AnnotationHitHistoryList",
{"data": fields.List(fields.Nested(api.model("AnnotationHitHistoryItem", annotation_hit_history_fields)))},
),
)
@api.response(403, "Insufficient permissions")
@setup_required
Expand Down
147 changes: 130 additions & 17 deletions api/controllers/console/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,15 @@
from core.ops.ops_trace_manager import OpsTraceManager
from core.workflow.enums import NodeType
from extensions.ext_database import db
from fields.app_fields import app_detail_fields, app_detail_fields_with_site, app_pagination_fields
from fields.app_fields import (
deleted_tool_fields,
model_config_fields,
model_config_partial_fields,
site_fields,
tag_fields,
)
from fields.workflow_fields import workflow_partial_fields as _workflow_partial_fields_dict
from libs.helper import AppIconUrlField, TimestampField
from libs.login import current_account_with_tenant, login_required
from libs.validators import validate_description_length
from models import App, Workflow
Expand All @@ -28,6 +36,111 @@

ALLOW_CREATE_APP_MODES = ["chat", "agent-chat", "advanced-chat", "workflow", "completion"]

# Register models for flask_restx to avoid dict type issues in Swagger
# Register base models first
tag_model = api.model("Tag", tag_fields)

workflow_partial_model = api.model("WorkflowPartial", _workflow_partial_fields_dict)

model_config_model = api.model("ModelConfig", model_config_fields)

model_config_partial_model = api.model("ModelConfigPartial", model_config_partial_fields)

deleted_tool_model = api.model("DeletedTool", deleted_tool_fields)

site_model = api.model("Site", site_fields)

app_partial_model = api.model(
"AppPartial",
{
"id": fields.String,
"name": fields.String,
"max_active_requests": fields.Raw(),
"description": fields.String(attribute="desc_or_prompt"),
"mode": fields.String(attribute="mode_compatible_with_agent"),
"icon_type": fields.String,
"icon": fields.String,
"icon_background": fields.String,
"icon_url": AppIconUrlField,
"model_config": fields.Nested(model_config_partial_model, attribute="app_model_config", allow_null=True),
"workflow": fields.Nested(workflow_partial_model, allow_null=True),
"use_icon_as_answer_icon": fields.Boolean,
"created_by": fields.String,
"created_at": TimestampField,
"updated_by": fields.String,
"updated_at": TimestampField,
"tags": fields.List(fields.Nested(tag_model)),
"access_mode": fields.String,
"create_user_name": fields.String,
"author_name": fields.String,
"has_draft_trigger": fields.Boolean,
},
)

app_detail_model = api.model(
"AppDetail",
{
"id": fields.String,
"name": fields.String,
"description": fields.String,
"mode": fields.String(attribute="mode_compatible_with_agent"),
"icon": fields.String,
"icon_background": fields.String,
"enable_site": fields.Boolean,
"enable_api": fields.Boolean,
"model_config": fields.Nested(model_config_model, attribute="app_model_config", allow_null=True),
"workflow": fields.Nested(workflow_partial_model, allow_null=True),
"tracing": fields.Raw,
"use_icon_as_answer_icon": fields.Boolean,
"created_by": fields.String,
"created_at": TimestampField,
"updated_by": fields.String,
"updated_at": TimestampField,
"access_mode": fields.String,
"tags": fields.List(fields.Nested(tag_model)),
},
)

app_detail_with_site_model = api.model(
"AppDetailWithSite",
{
"id": fields.String,
"name": fields.String,
"description": fields.String,
"mode": fields.String(attribute="mode_compatible_with_agent"),
"icon_type": fields.String,
"icon": fields.String,
"icon_background": fields.String,
"icon_url": AppIconUrlField,
"enable_site": fields.Boolean,
"enable_api": fields.Boolean,
"model_config": fields.Nested(model_config_model, attribute="app_model_config", allow_null=True),
"workflow": fields.Nested(workflow_partial_model, allow_null=True),
"api_base_url": fields.String,
"use_icon_as_answer_icon": fields.Boolean,
"max_active_requests": fields.Integer,
"created_by": fields.String,
"created_at": TimestampField,
"updated_by": fields.String,
"updated_at": TimestampField,
"deleted_tools": fields.List(fields.Nested(deleted_tool_model)),
"access_mode": fields.String,
"tags": fields.List(fields.Nested(tag_model)),
"site": fields.Nested(site_model),
},
)

app_pagination_model = api.model(
"AppPagination",
{
"page": fields.Integer,
"limit": fields.Integer(attribute="per_page"),
"total": fields.Integer,
"has_more": fields.Boolean(attribute="has_next"),
"data": fields.List(fields.Nested(app_partial_model), attribute="items"),
},
)


@console_ns.route("/apps")
class AppListApi(Resource):
Expand All @@ -49,7 +162,7 @@ class AppListApi(Resource):
.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)
@api.response(200, "Success", app_pagination_model)
@setup_required
@login_required
@account_initialization_required
Expand Down Expand Up @@ -136,7 +249,7 @@ def uuid_list(value):
for app in app_pagination.items:
app.has_draft_trigger = str(app.id) in draft_trigger_app_ids

return marshal(app_pagination, app_pagination_fields), 200
return marshal(app_pagination, app_pagination_model), 200

@api.doc("create_app")
@api.doc(description="Create a new application")
Expand All @@ -153,13 +266,13 @@ def uuid_list(value):
},
)
)
@api.response(201, "App created successfully", app_detail_fields)
@api.response(201, "App created successfully", app_detail_model)
@api.response(403, "Insufficient permissions")
@api.response(400, "Invalid request parameters")
@setup_required
@login_required
@account_initialization_required
@marshal_with(app_detail_fields)
@marshal_with(app_detail_model)
@cloud_edition_billing_resource_check("apps")
@edit_permission_required
def post(self):
Expand Down Expand Up @@ -190,13 +303,13 @@ 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)
@api.response(200, "Success", app_detail_with_site_model)
@setup_required
@login_required
@account_initialization_required
@enterprise_license_required
@get_app_model
@marshal_with(app_detail_fields_with_site)
@marshal_with(app_detail_with_site_model)
def get(self, app_model):
"""Get app detail"""
app_service = AppService()
Expand Down Expand Up @@ -226,15 +339,15 @@ def get(self, app_model):
},
)
)
@api.response(200, "App updated successfully", app_detail_fields_with_site)
@api.response(200, "App updated successfully", app_detail_with_site_model)
@api.response(403, "Insufficient permissions")
@api.response(400, "Invalid request parameters")
@setup_required
@login_required
@account_initialization_required
@get_app_model
@edit_permission_required
@marshal_with(app_detail_fields_with_site)
@marshal_with(app_detail_with_site_model)
def put(self, app_model):
"""Update app"""
parser = (
Expand Down Expand Up @@ -299,14 +412,14 @@ class AppCopyApi(Resource):
},
)
)
@api.response(201, "App copied successfully", app_detail_fields_with_site)
@api.response(201, "App copied successfully", app_detail_with_site_model)
@api.response(403, "Insufficient permissions")
@setup_required
@login_required
@account_initialization_required
@get_app_model
@edit_permission_required
@marshal_with(app_detail_fields_with_site)
@marshal_with(app_detail_with_site_model)
def post(self, app_model):
"""Copy app"""
# The role of the current user in the ta table must be admin, owner, or editor
Expand Down Expand Up @@ -395,7 +508,7 @@ class AppNameApi(Resource):
@login_required
@account_initialization_required
@get_app_model
@marshal_with(app_detail_fields)
@marshal_with(app_detail_model)
@edit_permission_required
def post(self, app_model):
args = parser.parse_args()
Expand Down Expand Up @@ -427,7 +540,7 @@ class AppIconApi(Resource):
@login_required
@account_initialization_required
@get_app_model
@marshal_with(app_detail_fields)
@marshal_with(app_detail_model)
@edit_permission_required
def post(self, app_model):
parser = (
Expand All @@ -453,13 +566,13 @@ class AppSiteStatus(Resource):
"AppSiteStatusRequest", {"enable_site": fields.Boolean(required=True, description="Enable or disable site")}
)
)
@api.response(200, "Site status updated successfully", app_detail_fields)
@api.response(200, "Site status updated successfully", app_detail_model)
@api.response(403, "Insufficient permissions")
@setup_required
@login_required
@account_initialization_required
@get_app_model
@marshal_with(app_detail_fields)
@marshal_with(app_detail_model)
@edit_permission_required
def post(self, app_model):
parser = reqparse.RequestParser().add_argument("enable_site", type=bool, required=True, location="json")
Expand All @@ -481,13 +594,13 @@ class AppApiStatus(Resource):
"AppApiStatusRequest", {"enable_api": fields.Boolean(required=True, description="Enable or disable API")}
)
)
@api.response(200, "API status updated successfully", app_detail_fields)
@api.response(200, "API status updated successfully", app_detail_model)
@api.response(403, "Insufficient permissions")
@setup_required
@login_required
@account_initialization_required
@get_app_model
@marshal_with(app_detail_fields)
@marshal_with(app_detail_model)
def post(self, app_model):
# The role of the current user in the ta table must be admin or owner
current_user, _ = current_account_with_tenant()
Expand Down
Loading