This commit is contained in:
chariri 2026-06-25 18:39:20 +00:00 committed by GitHub
commit bd628d35cf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 1146 additions and 825 deletions

View File

@ -167,12 +167,16 @@ register_schema_models(
ChatMessagesQuery,
MessageFeedbackPayload,
FeedbackExportQuery,
)
register_response_schema_models(
console_ns,
AnnotationCountResponse,
SuggestedQuestionsResponse,
MessageDetailResponse,
MessageInfiniteScrollPaginationResponse,
SimpleResultResponse,
TextFileResponse,
)
register_response_schema_models(console_ns, SimpleResultResponse, TextFileResponse)
@console_ns.route("/apps/<uuid:app_id>/chat-messages")

View File

@ -1,12 +1,12 @@
from __future__ import annotations
from datetime import datetime
from typing import Any, Literal
from typing import Literal
import pytz
from flask import request
from flask_restx import Resource
from pydantic import BaseModel, Field, RootModel, field_validator, model_validator
from pydantic import BaseModel, Field, field_validator, model_validator
from sqlalchemy import select
from werkzeug.exceptions import NotFound
@ -47,7 +47,7 @@ from controllers.console.wraps import (
)
from extensions.ext_database import db
from fields.base import ResponseModel
from fields.member_fields import Account as AccountResponse
from fields.member_fields import AccountResponse
from graphon.file import helpers as file_helpers
from libs.datetime_utils import naive_utc_now
from libs.helper import EmailStr, dump_response, extract_remote_ip, timezone, to_timestamp
@ -194,10 +194,6 @@ register_schema_models(
)
def _serialize_account(account) -> dict[str, Any]:
return AccountResponse.model_validate(account, from_attributes=True).model_dump(mode="json")
class AccountIntegrateResponse(ResponseModel):
provider: str
created_at: int | None = None
@ -236,23 +232,15 @@ class EducationAutocompleteResponse(ResponseModel):
has_next: bool | None = None
class EducationActivateResponse(RootModel[dict[str, Any]]):
root: dict[str, Any]
register_schema_models(
console_ns,
AccountIntegrateResponse,
AccountIntegrateListResponse,
EducationVerifyResponse,
EducationStatusResponse,
EducationAutocompleteResponse,
)
register_response_schema_models(
console_ns,
AccountResponse,
AccountIntegrateResponse,
AccountIntegrateListResponse,
AvatarUrlResponse,
EducationActivateResponse,
EducationVerifyResponse,
EducationStatusResponse,
EducationAutocompleteResponse,
SimpleResultDataResponse,
SimpleResultResponse,
VerificationTokenResponse,
@ -302,7 +290,7 @@ class AccountInitApi(Resource):
account.initialized_at = naive_utc_now()
db.session.commit()
return {"result": "success"}
return SimpleResultResponse(result="success").model_dump(mode="json")
@console_ns.route("/account/profile")
@ -314,7 +302,7 @@ class AccountProfileApi(Resource):
@enterprise_license_required
@with_current_user
def get(self, current_user: Account):
return _serialize_account(current_user)
return dump_response(AccountResponse, current_user)
@console_ns.route("/account/name")
@ -330,7 +318,7 @@ class AccountNameApi(Resource):
args = AccountNamePayload.model_validate(payload)
updated_account = AccountService.update_account(current_user, session=db.session, name=args.name)
return _serialize_account(updated_account)
return dump_response(AccountResponse, updated_account)
@console_ns.route("/account/avatar")
@ -349,7 +337,7 @@ class AccountAvatarApi(Resource):
avatar = args.avatar
if avatar.startswith(("http://", "https://")):
return dump_response(AvatarUrlResponse, {"avatar_url": avatar})
return AvatarUrlResponse(avatar_url=avatar).model_dump(mode="json")
upload_file = db.session.scalar(select(UploadFile).where(UploadFile.id == avatar).limit(1))
if upload_file is None:
@ -362,7 +350,7 @@ class AccountAvatarApi(Resource):
raise NotFound("Avatar file not found")
avatar_url = file_helpers.get_signed_file_url(upload_file_id=upload_file.id)
return dump_response(AvatarUrlResponse, {"avatar_url": avatar_url})
return AvatarUrlResponse(avatar_url=avatar_url).model_dump(mode="json")
@console_ns.expect(console_ns.models[AccountAvatarPayload.__name__])
@setup_required
@ -376,7 +364,7 @@ class AccountAvatarApi(Resource):
updated_account = AccountService.update_account(current_user, session=db.session, avatar=args.avatar)
return _serialize_account(updated_account)
return dump_response(AccountResponse, updated_account)
@console_ns.route("/account/interface-language")
@ -395,7 +383,7 @@ class AccountInterfaceLanguageApi(Resource):
current_user, session=db.session, interface_language=args.interface_language
)
return _serialize_account(updated_account)
return dump_response(AccountResponse, updated_account)
@console_ns.route("/account/interface-theme")
@ -414,7 +402,7 @@ class AccountInterfaceThemeApi(Resource):
current_user, session=db.session, interface_theme=args.interface_theme
)
return _serialize_account(updated_account)
return dump_response(AccountResponse, updated_account)
@console_ns.route("/account/timezone")
@ -431,7 +419,7 @@ class AccountTimezoneApi(Resource):
updated_account = AccountService.update_account(current_user, session=db.session, timezone=args.timezone)
return _serialize_account(updated_account)
return dump_response(AccountResponse, updated_account)
@console_ns.route("/account/password")
@ -452,7 +440,7 @@ class AccountPasswordApi(Resource):
except ServiceCurrentPasswordIncorrectError:
raise CurrentPasswordIncorrectError()
return _serialize_account(current_user)
return dump_response(AccountResponse, current_user)
@console_ns.route("/account/integrates")
@ -471,33 +459,29 @@ class AccountIntegrateApi(Resource):
oauth_base_path = "/console/api/oauth/login"
providers = ["github", "google"]
integrate_data = []
integrate_data: list[AccountIntegrateResponse] = []
for provider in providers:
existing_integrate = next((ai for ai in account_integrates if ai.provider == provider), None)
if existing_integrate:
integrate_data.append(
{
"id": existing_integrate.id,
"provider": provider,
"created_at": existing_integrate.created_at,
"is_bound": True,
"link": None,
}
AccountIntegrateResponse(
provider=provider,
created_at=to_timestamp(existing_integrate.created_at),
is_bound=True,
link=None,
)
)
else:
integrate_data.append(
{
"id": None,
"provider": provider,
"created_at": None,
"is_bound": False,
"link": f"{base_url}{oauth_base_path}/{provider}",
}
AccountIntegrateResponse(
provider=provider,
created_at=None,
is_bound=False,
link=f"{base_url}{oauth_base_path}/{provider}",
)
)
return AccountIntegrateListResponse(
data=[AccountIntegrateResponse.model_validate(item) for item in integrate_data]
).model_dump(mode="json")
return AccountIntegrateListResponse(data=integrate_data).model_dump(mode="json")
@console_ns.route("/account/delete/verify")
@ -511,7 +495,7 @@ class AccountDeleteVerifyApi(Resource):
token, code = AccountService.generate_account_deletion_verification_code(account)
AccountService.send_account_deletion_verification_email(account, code)
return {"result": "success", "data": token}
return SimpleResultDataResponse(result="success", data=token).model_dump(mode="json")
@console_ns.route("/account/delete")
@ -531,7 +515,7 @@ class AccountDeleteApi(Resource):
AccountService.delete_account(account)
return {"result": "success"}
return SimpleResultResponse(result="success").model_dump(mode="json")
@console_ns.route("/account/delete/feedback")
@ -545,7 +529,7 @@ class AccountDeleteUpdateFeedbackApi(Resource):
BillingService.update_account_deletion_feedback(args.email, args.feedback)
return {"result": "success"}
return SimpleResultResponse(result="success").model_dump(mode="json")
@console_ns.route("/account/education/verify")
@ -558,15 +542,16 @@ class EducationVerifyApi(Resource):
@console_ns.response(200, "Success", console_ns.models[EducationVerifyResponse.__name__])
@with_current_user
def get(self, account: Account):
return EducationVerifyResponse.model_validate(
BillingService.EducationIdentity.verify(account.id, account.email) or {}
).model_dump(mode="json")
return dump_response(
EducationVerifyResponse, BillingService.EducationIdentity.verify(account.id, account.email) or {}
)
@console_ns.route("/account/education")
class EducationApi(Resource):
@console_ns.expect(console_ns.models[EducationActivatePayload.__name__])
@console_ns.response(200, "Success", console_ns.models[EducationActivateResponse.__name__])
# response-contract:ignore billing-service activation payload; TODO: model education activation result.
@console_ns.response(200, "Success")
@setup_required
@login_required
@account_initialization_required
@ -577,7 +562,8 @@ class EducationApi(Resource):
payload = console_ns.payload or {}
args = EducationActivatePayload.model_validate(payload)
return BillingService.EducationIdentity.activate(account, args.token, args.institution, args.role)
result = BillingService.EducationIdentity.activate(account, args.token, args.institution, args.role)
return result
@setup_required
@login_required
@ -591,7 +577,7 @@ class EducationApi(Resource):
# convert expire_at to UTC timestamp from isoformat
if res and "expire_at" in res:
res["expire_at"] = datetime.fromisoformat(res["expire_at"]).astimezone(pytz.utc)
return EducationStatusResponse.model_validate(res).model_dump(mode="json")
return dump_response(EducationStatusResponse, res)
@console_ns.route("/account/education/autocomplete")
@ -607,9 +593,10 @@ class EducationAutoCompleteApi(Resource):
payload = request.args.to_dict(flat=True)
args = EducationAutocompleteQuery.model_validate(payload)
return EducationAutocompleteResponse.model_validate(
BillingService.EducationIdentity.autocomplete(args.keywords, args.page, args.limit) or {}
).model_dump(mode="json")
return dump_response(
EducationAutocompleteResponse,
BillingService.EducationIdentity.autocomplete(args.keywords, args.page, args.limit) or {},
)
@console_ns.route("/account/change-email")
@ -669,7 +656,7 @@ class ChangeEmailSendEmailApi(Resource):
language=language,
phase=send_phase,
)
return {"result": "success", "data": token}
return SimpleResultDataResponse(result="success", data=token).model_dump(mode="json")
@console_ns.route("/account/change-email/validity")
@ -716,7 +703,9 @@ class ChangeEmailCheckApi(Resource):
new_token = AccountService.generate_change_email_token(refreshed_token_data, current_user)
AccountService.reset_change_email_error_rate_limit(user_email)
return {"is_valid": True, "email": normalized_token_email, "token": new_token}
return VerificationTokenResponse(is_valid=True, email=normalized_token_email, token=new_token).model_dump(
mode="json"
)
@console_ns.route("/account/change-email/reset")
@ -768,7 +757,7 @@ class ChangeEmailResetApi(Resource):
email=normalized_new_email,
)
return _serialize_account(updated_account)
return dump_response(AccountResponse, updated_account)
@console_ns.route("/account/change-email/check-email-unique")
@ -784,4 +773,4 @@ class CheckEmailUnique(Resource):
raise AccountInFreezeError()
if not AccountService.check_email_unique(normalized_email, session=db.session):
raise EmailAlreadyInUseError()
return {"result": "success"}
return SimpleResultResponse(result="success").model_dump(mode="json")

View File

@ -12,7 +12,8 @@ from flask import request
from flask_restx import Resource
from pydantic import BaseModel, Field
from controllers.common.schema import query_params_from_model, register_schema_models
from controllers.common.fields import SuccessResponse
from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models
from controllers.console import console_ns
from controllers.console.wraps import (
RBACPermission,
@ -24,8 +25,9 @@ from controllers.console.wraps import (
with_current_tenant_id,
with_current_user_id,
)
from core.plugin.entities.endpoint import EndpointEntityWithInstance
from core.plugin.impl.exc import PluginPermissionDeniedError
from graphon.model_runtime.utils.encoders import jsonable_encoder
from fields.base import ResponseModel
from libs.login import login_required
from services.plugin.endpoint_service import EndpointService
@ -40,14 +42,17 @@ class EndpointIdPayload(BaseModel):
endpoint_id: str
class EndpointUpdatePayload(BaseModel):
class EndpointSettingsPayload(BaseModel):
settings: dict[str, Any]
name: str = Field(min_length=1)
class LegacyEndpointUpdatePayload(EndpointIdPayload):
settings: dict[str, Any]
name: str = Field(min_length=1)
class EndpointUpdatePayload(EndpointSettingsPayload):
pass
class LegacyEndpointUpdatePayload(EndpointIdPayload, EndpointSettingsPayload):
pass
class EndpointListQuery(BaseModel):
@ -59,98 +64,85 @@ class EndpointListForPluginQuery(EndpointListQuery):
plugin_id: str
class EndpointCreateResponse(BaseModel):
success: bool = Field(description="Operation success")
class EndpointListResponse(BaseModel):
endpoints: list[dict[str, Any]] = Field(
description="Endpoint information",
)
class PluginEndpointListResponse(BaseModel):
endpoints: list[dict[str, Any]] = Field(
description="Endpoint information",
)
class EndpointDeleteResponse(BaseModel):
success: bool = Field(description="Operation success")
class EndpointUpdateResponse(BaseModel):
success: bool = Field(description="Operation success")
class EndpointEnableResponse(BaseModel):
success: bool = Field(description="Operation success")
class EndpointDisableResponse(BaseModel):
success: bool = Field(description="Operation success")
class EndpointListResponse(ResponseModel):
endpoints: list[EndpointEntityWithInstance] = Field(description="Endpoint information")
register_schema_models(
console_ns,
EndpointCreatePayload,
EndpointIdPayload,
EndpointSettingsPayload,
EndpointUpdatePayload,
LegacyEndpointUpdatePayload,
EndpointListQuery,
EndpointListForPluginQuery,
EndpointCreateResponse,
)
register_response_schema_models(
console_ns,
SuccessResponse,
EndpointListResponse,
PluginEndpointListResponse,
EndpointDeleteResponse,
EndpointUpdateResponse,
EndpointEnableResponse,
EndpointDisableResponse,
)
def _create_endpoint(tenant_id: str, user_id: str) -> dict[str, bool]:
def _create_endpoint(tenant_id: str, user_id: str) -> bool:
"""Create a plugin endpoint for the injected workspace and user."""
args = EndpointCreatePayload.model_validate(console_ns.payload)
try:
return {
"success": EndpointService.create_endpoint(
tenant_id=tenant_id,
user_id=user_id,
plugin_unique_identifier=args.plugin_unique_identifier,
name=args.name,
settings=args.settings,
)
}
return EndpointService.create_endpoint(
tenant_id=tenant_id,
user_id=user_id,
plugin_unique_identifier=args.plugin_unique_identifier,
name=args.name,
settings=args.settings,
)
except PluginPermissionDeniedError as e:
raise ValueError(e.description) from e
def _update_endpoint(tenant_id: str, user_id: str, endpoint_id: str) -> dict[str, bool]:
def _update_endpoint(tenant_id: str, user_id: str, endpoint_id: str) -> bool:
"""Update a plugin endpoint identified by the canonical path parameter."""
args = EndpointUpdatePayload.model_validate(console_ns.payload)
return {
"success": EndpointService.update_endpoint(
tenant_id=tenant_id,
user_id=user_id,
endpoint_id=endpoint_id,
name=args.name,
settings=args.settings,
)
}
return EndpointService.update_endpoint(
tenant_id=tenant_id,
user_id=user_id,
endpoint_id=endpoint_id,
name=args.name,
settings=args.settings,
)
def _delete_endpoint(tenant_id: str, user_id: str, endpoint_id: str) -> dict[str, bool]:
def _legacy_update_endpoint(tenant_id: str, user_id: str) -> bool:
args = LegacyEndpointUpdatePayload.model_validate(console_ns.payload)
return EndpointService.update_endpoint(
tenant_id=tenant_id,
user_id=user_id,
endpoint_id=args.endpoint_id,
name=args.name,
settings=args.settings,
)
def _delete_endpoint(tenant_id: str, user_id: str, endpoint_id: str) -> bool:
"""Delete a plugin endpoint identified by the canonical path parameter."""
return {
"success": EndpointService.delete_endpoint(
tenant_id=tenant_id,
user_id=user_id,
endpoint_id=endpoint_id,
)
}
return EndpointService.delete_endpoint(
tenant_id=tenant_id,
user_id=user_id,
endpoint_id=endpoint_id,
)
def _delete_endpoint_from_payload(tenant_id: str, user_id: str) -> bool:
args = EndpointIdPayload.model_validate(console_ns.payload)
return _delete_endpoint(tenant_id=tenant_id, user_id=user_id, endpoint_id=args.endpoint_id)
def _set_endpoint_enabled(tenant_id: str, user_id: str, *, enabled: bool) -> bool:
args = EndpointIdPayload.model_validate(console_ns.payload)
action = EndpointService.enable_endpoint if enabled else EndpointService.disable_endpoint
return action(tenant_id=tenant_id, user_id=user_id, endpoint_id=args.endpoint_id)
@console_ns.route("/workspaces/current/endpoints")
@ -163,7 +155,7 @@ class EndpointCollectionApi(Resource):
@console_ns.response(
200,
"Endpoint created successfully",
console_ns.models[EndpointCreateResponse.__name__],
console_ns.models[SuccessResponse.__name__],
)
@console_ns.response(403, "Admin privileges required")
@setup_required
@ -174,7 +166,7 @@ class EndpointCollectionApi(Resource):
@with_current_user_id
@with_current_tenant_id
def post(self, tenant_id: str, user_id: str):
return _create_endpoint(tenant_id=tenant_id, user_id=user_id)
return SuccessResponse(success=_create_endpoint(tenant_id=tenant_id, user_id=user_id)).model_dump(mode="json")
@console_ns.route("/workspaces/current/endpoints/create")
@ -192,7 +184,7 @@ class DeprecatedEndpointCreateApi(Resource):
@console_ns.response(
200,
"Endpoint created successfully",
console_ns.models[EndpointCreateResponse.__name__],
console_ns.models[SuccessResponse.__name__],
)
@console_ns.response(403, "Admin privileges required")
@setup_required
@ -203,7 +195,7 @@ class DeprecatedEndpointCreateApi(Resource):
@with_current_user_id
@with_current_tenant_id
def post(self, tenant_id: str, user_id: str):
return _create_endpoint(tenant_id=tenant_id, user_id=user_id)
return SuccessResponse(success=_create_endpoint(tenant_id=tenant_id, user_id=user_id)).model_dump(mode="json")
@console_ns.route("/workspaces/current/endpoints/list")
@ -224,19 +216,14 @@ class EndpointListApi(Resource):
def get(self, tenant_id: str, user_id: str):
args = EndpointListQuery.model_validate(request.args.to_dict(flat=True))
page = args.page
page_size = args.page_size
return jsonable_encoder(
{
"endpoints": EndpointService.list_endpoints(
tenant_id=tenant_id,
user_id=user_id,
page=page,
page_size=page_size,
)
}
)
return EndpointListResponse(
endpoints=EndpointService.list_endpoints(
tenant_id=tenant_id,
user_id=user_id,
page=args.page,
page_size=args.page_size,
)
).model_dump(mode="json")
@console_ns.route("/workspaces/current/endpoints/list/plugin")
@ -247,7 +234,7 @@ class EndpointListForSinglePluginApi(Resource):
@console_ns.response(
200,
"Success",
console_ns.models[PluginEndpointListResponse.__name__],
console_ns.models[EndpointListResponse.__name__],
)
@setup_required
@login_required
@ -257,21 +244,15 @@ class EndpointListForSinglePluginApi(Resource):
def get(self, tenant_id: str, user_id: str):
args = EndpointListForPluginQuery.model_validate(request.args.to_dict(flat=True))
page = args.page
page_size = args.page_size
plugin_id = args.plugin_id
return jsonable_encoder(
{
"endpoints": EndpointService.list_endpoints_for_single_plugin(
tenant_id=tenant_id,
user_id=user_id,
plugin_id=plugin_id,
page=page,
page_size=page_size,
)
}
)
return EndpointListResponse(
endpoints=EndpointService.list_endpoints_for_single_plugin(
tenant_id=tenant_id,
user_id=user_id,
plugin_id=args.plugin_id,
page=args.page,
page_size=args.page_size,
)
).model_dump(mode="json")
@console_ns.route("/workspaces/current/endpoints/<string:id>")
@ -284,7 +265,7 @@ class EndpointItemApi(Resource):
@console_ns.response(
200,
"Endpoint deleted successfully",
console_ns.models[EndpointDeleteResponse.__name__],
console_ns.models[SuccessResponse.__name__],
)
@console_ns.response(403, "Admin privileges required")
@setup_required
@ -295,7 +276,9 @@ class EndpointItemApi(Resource):
@with_current_user_id
@with_current_tenant_id
def delete(self, tenant_id: str, user_id: str, id: str):
return _delete_endpoint(tenant_id=tenant_id, user_id=user_id, endpoint_id=id)
return SuccessResponse(
success=_delete_endpoint(tenant_id=tenant_id, user_id=user_id, endpoint_id=id)
).model_dump(mode="json")
@console_ns.doc("update_endpoint")
@console_ns.doc(description="Update a plugin endpoint")
@ -304,7 +287,7 @@ class EndpointItemApi(Resource):
@console_ns.response(
200,
"Endpoint updated successfully",
console_ns.models[EndpointUpdateResponse.__name__],
console_ns.models[SuccessResponse.__name__],
)
@console_ns.response(403, "Admin privileges required")
@setup_required
@ -315,7 +298,9 @@ class EndpointItemApi(Resource):
@with_current_user_id
@with_current_tenant_id
def patch(self, tenant_id: str, user_id: str, id: str):
return _update_endpoint(tenant_id=tenant_id, user_id=user_id, endpoint_id=id)
return SuccessResponse(
success=_update_endpoint(tenant_id=tenant_id, user_id=user_id, endpoint_id=id)
).model_dump(mode="json")
@console_ns.route("/workspaces/current/endpoints/delete")
@ -334,7 +319,7 @@ class DeprecatedEndpointDeleteApi(Resource):
@console_ns.response(
200,
"Endpoint deleted successfully",
console_ns.models[EndpointDeleteResponse.__name__],
console_ns.models[SuccessResponse.__name__],
)
@console_ns.response(403, "Admin privileges required")
@setup_required
@ -345,8 +330,9 @@ class DeprecatedEndpointDeleteApi(Resource):
@with_current_user_id
@with_current_tenant_id
def post(self, tenant_id: str, user_id: str):
args = EndpointIdPayload.model_validate(console_ns.payload)
return _delete_endpoint(tenant_id=tenant_id, user_id=user_id, endpoint_id=args.endpoint_id)
return SuccessResponse( #
success=_delete_endpoint_from_payload(tenant_id=tenant_id, user_id=user_id)
).model_dump(mode="json")
@console_ns.route("/workspaces/current/endpoints/update")
@ -365,7 +351,7 @@ class DeprecatedEndpointUpdateApi(Resource):
@console_ns.response(
200,
"Endpoint updated successfully",
console_ns.models[EndpointUpdateResponse.__name__],
console_ns.models[SuccessResponse.__name__],
)
@console_ns.response(403, "Admin privileges required")
@setup_required
@ -376,8 +362,9 @@ class DeprecatedEndpointUpdateApi(Resource):
@with_current_user_id
@with_current_tenant_id
def post(self, tenant_id: str, user_id: str):
args = LegacyEndpointUpdatePayload.model_validate(console_ns.payload)
return _update_endpoint(tenant_id=tenant_id, user_id=user_id, endpoint_id=args.endpoint_id)
return SuccessResponse( #
success=_legacy_update_endpoint(tenant_id=tenant_id, user_id=user_id)
).model_dump(mode="json")
@console_ns.route("/workspaces/current/endpoints/enable")
@ -388,7 +375,7 @@ class EndpointEnableApi(Resource):
@console_ns.response(
200,
"Endpoint enabled successfully",
console_ns.models[EndpointEnableResponse.__name__],
console_ns.models[SuccessResponse.__name__],
)
@console_ns.response(403, "Admin privileges required")
@setup_required
@ -399,13 +386,9 @@ class EndpointEnableApi(Resource):
@with_current_user_id
@with_current_tenant_id
def post(self, tenant_id: str, user_id: str):
args = EndpointIdPayload.model_validate(console_ns.payload)
return {
"success": EndpointService.enable_endpoint(
tenant_id=tenant_id, user_id=user_id, endpoint_id=args.endpoint_id
)
}
return SuccessResponse(
success=_set_endpoint_enabled(tenant_id=tenant_id, user_id=user_id, enabled=True)
).model_dump(mode="json")
@console_ns.route("/workspaces/current/endpoints/disable")
@ -416,7 +399,7 @@ class EndpointDisableApi(Resource):
@console_ns.response(
200,
"Endpoint disabled successfully",
console_ns.models[EndpointDisableResponse.__name__],
console_ns.models[SuccessResponse.__name__],
)
@console_ns.response(403, "Admin privileges required")
@setup_required
@ -427,10 +410,6 @@ class EndpointDisableApi(Resource):
@with_current_user_id
@with_current_tenant_id
def post(self, tenant_id: str, user_id: str):
args = EndpointIdPayload.model_validate(console_ns.payload)
return {
"success": EndpointService.disable_endpoint(
tenant_id=tenant_id, user_id=user_id, endpoint_id=args.endpoint_id
)
}
return SuccessResponse(
success=_set_endpoint_enabled(tenant_id=tenant_id, user_id=user_id, enabled=False)
).model_dump(mode="json")

View File

@ -3,7 +3,7 @@ from uuid import UUID
from flask import abort, request
from flask_restx import Resource
from pydantic import BaseModel, Field, TypeAdapter
from pydantic import BaseModel, Field
from sqlalchemy import func, select
import services
@ -30,8 +30,8 @@ from controllers.console.wraps import (
from extensions.ext_database import db
from extensions.ext_redis import redis_client
from fields.base import ResponseModel
from fields.member_fields import AccountWithRole, AccountWithRoleList
from libs.helper import extract_remote_ip
from fields.member_fields import AccountWithRoleListResponse, AccountWithRoleResponse
from libs.helper import dump_response, extract_remote_ip
from libs.login import current_account_with_tenant, login_required
from models.account import Account, TenantAccountJoin, TenantAccountRole
from services.account_service import AccountService, RegisterService, TenantService
@ -70,22 +70,20 @@ class MemberInviteResultResponse(ResponseModel):
message: str | None = None
class MemberActionResponse(ResponseModel):
result: str
tenant_id: str = ""
class MemberInviteResponse(ResponseModel):
result: str
invitation_results: list[MemberInviteResultResponse]
tenant_id: str
class MemberActionTenantResponse(ResponseModel):
result: str
tenant_id: str
register_enum_models(console_ns, TenantAccountRole)
register_schema_models(
console_ns,
AccountWithRole,
AccountWithRoleList,
MemberInvitePayload,
MemberRoleUpdatePayload,
OwnerTransferEmailPayload,
@ -94,11 +92,14 @@ register_schema_models(
)
register_response_schema_models(
console_ns,
AccountWithRoleResponse,
AccountWithRoleListResponse,
MemberActionResponse,
MemberInviteResponse,
MemberInviteResultResponse,
SimpleResultDataResponse,
SimpleResultResponse,
VerificationTokenResponse,
MemberInviteResponse,
MemberActionTenantResponse,
)
@ -179,7 +180,7 @@ class MemberListApi(Resource):
@setup_required
@login_required
@account_initialization_required
@console_ns.response(200, "Success", console_ns.models[AccountWithRoleList.__name__])
@console_ns.response(200, "Success", console_ns.models[AccountWithRoleListResponse.__name__])
@with_current_user
def get(self, current_user: Account | None = None):
if current_user is None:
@ -216,9 +217,7 @@ class MemberListApi(Resource):
}
)
member_models = TypeAdapter(list[AccountWithRole]).validate_python(serialized_members)
response = AccountWithRoleList(accounts=member_models)
return response.model_dump(mode="json"), 200
return dump_response(AccountWithRoleListResponse, {"accounts": serialized_members}), 200
@console_ns.route("/workspaces/current/members/invite-email")
@ -254,7 +253,7 @@ class MemberInviteEmailApi(Resource):
check_workspace_member_invite_permission(inviter.current_tenant.id)
invitation_results = []
invitation_results: list[MemberInviteResultResponse] = []
console_web_url = dify_config.CONSOLE_WEB_URL
tenant_id = inviter.current_tenant.id
@ -277,38 +276,40 @@ class MemberInviteEmailApi(Resource):
)
encoded_invitee_email = parse.quote(invitee_email)
invitation_results.append(
{
"status": "success",
"email": invitee_email,
"url": f"{console_web_url}/activate?email={encoded_invitee_email}&token={token}",
}
MemberInviteResultResponse(
status="success",
email=invitee_email,
url=f"{console_web_url}/activate?email={encoded_invitee_email}&token={token}",
)
)
except AccountAlreadyInTenantError:
invitation_results.append(
{
"status": "already_member",
"email": invitee_email,
"message": "Account already in workspace.",
}
MemberInviteResultResponse(
status="already_member",
email=invitee_email,
message="Account already in workspace.",
)
)
except Exception as e:
invitation_results.append({"status": "failed", "email": invitee_email, "message": str(e)})
invitation_results.append(
MemberInviteResultResponse(status="failed", email=invitee_email, message=str(e))
)
return {
"result": "success",
"invitation_results": invitation_results,
"tenant_id": str(inviter.current_tenant.id) if inviter.current_tenant else "",
}, 201
return MemberInviteResponse(
result="success",
invitation_results=invitation_results,
tenant_id=inviter.current_tenant.id if inviter.current_tenant else "",
).model_dump(mode="json"), 201
@console_ns.route("/workspaces/current/members/<uuid:member_id>")
class MemberCancelInviteApi(Resource):
"""Cancel an invitation by member id."""
@console_ns.response(200, "Success", console_ns.models[MemberActionTenantResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@console_ns.response(200, "Success", console_ns.models[MemberActionResponse.__name__])
@with_current_user
def delete(self, current_user: Account, member_id: UUID):
if not current_user.current_tenant:
@ -330,10 +331,10 @@ class MemberCancelInviteApi(Resource):
except Exception as e:
raise ValueError(str(e))
return {
"result": "success",
"tenant_id": str(current_user.current_tenant.id) if current_user.current_tenant else "",
}, 200
return MemberActionResponse(
result="success",
tenant_id=current_user.current_tenant.id if current_user.current_tenant else "",
).model_dump(mode="json"), 200
@console_ns.route("/workspaces/current/members/<uuid:member_id>/update-role")
@ -377,7 +378,7 @@ class MemberUpdateRoleApi(Resource):
except Exception as e:
raise ValueError(str(e))
return {"result": "success"}
return SimpleResultResponse(result="success").model_dump(mode="json")
@console_ns.route("/workspaces/current/dataset-operators")
@ -387,15 +388,13 @@ class DatasetOperatorMemberListApi(Resource):
@setup_required
@login_required
@account_initialization_required
@console_ns.response(200, "Success", console_ns.models[AccountWithRoleList.__name__])
@console_ns.response(200, "Success", console_ns.models[AccountWithRoleListResponse.__name__])
@with_current_user
def get(self, current_user: Account):
if not current_user.current_tenant:
raise ValueError("No current tenant")
members = TenantService.get_dataset_operator_members(current_user.current_tenant, session=db.session)
member_models = TypeAdapter(list[AccountWithRole]).validate_python(members, from_attributes=True)
response = AccountWithRoleList(accounts=member_models)
return response.model_dump(mode="json"), 200
return dump_response(AccountWithRoleListResponse, {"accounts": members}), 200
@console_ns.route("/workspaces/current/members/send-owner-transfer-confirm-email")
@ -435,7 +434,7 @@ class SendOwnerTransferEmailApi(Resource):
workspace_name=current_user.current_tenant.name if current_user.current_tenant else "",
)
return {"result": "success", "data": token}
return SimpleResultDataResponse(result="success", data=token).model_dump(mode="json")
@console_ns.route("/workspaces/current/members/owner-transfer-check")
@ -480,7 +479,7 @@ class OwnerTransferCheckApi(Resource):
_, new_token = AccountService.generate_owner_transfer_token(user_email, code=args.code, additional_data={})
AccountService.reset_owner_transfer_error_rate_limit(user_email)
return {"is_valid": True, "email": token_data.get("email"), "token": new_token}
return VerificationTokenResponse(is_valid=True, email=user_email, token=new_token).model_dump(mode="json")
@console_ns.route("/workspaces/current/members/<uuid:member_id>/owner-transfer")
@ -546,4 +545,4 @@ class OwnerTransfer(Resource):
except Exception as e:
raise ValueError(str(e))
return {"result": "success"}
return SimpleResultResponse(result="success").model_dump(mode="json")

View File

@ -2,7 +2,7 @@ import logging
from datetime import datetime
from flask import request
from flask_restx import Resource, fields, marshal
from flask_restx import Resource
from pydantic import BaseModel, Field, field_validator
from sqlalchemy import select
from werkzeug.exceptions import Unauthorized
@ -16,7 +16,12 @@ from controllers.common.errors import (
TooManyFilesError,
UnsupportedFileTypeError,
)
from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models
from controllers.common.schema import (
query_params_from_model,
query_params_from_request,
register_response_schema_models,
register_schema_models,
)
from controllers.console import console_ns
from controllers.console.admin import admin_required
from controllers.console.error import AccountNotLinkTenantError
@ -31,7 +36,7 @@ from controllers.console.wraps import (
from enums.cloud_plan import CloudPlan
from extensions.ext_database import db
from fields.base import ResponseModel
from libs.helper import OptionalTimestampField, TimestampField, dump_response, to_timestamp
from libs.helper import dump_response, to_timestamp
from libs.login import login_required
from models.account import Account, Tenant, TenantAccountJoin, TenantCustomConfigDict, TenantStatus
from services.account_service import TenantService
@ -102,6 +107,7 @@ class TenantListItemResponse(ResponseModel):
plan: str | None = None
status: str | None = None
created_at: int | None = None
last_opened_at: int | None = None
current: bool
@field_validator("plan", "status", mode="before")
@ -113,9 +119,9 @@ class TenantListItemResponse(ResponseModel):
return value
return str(getattr(value, "value", value))
@field_validator("created_at", mode="before")
@field_validator("created_at", "last_opened_at", mode="before")
@classmethod
def _normalize_created_at(cls, value: datetime | int | None):
def _normalize_timestamp(cls, value: datetime | int | None):
return to_timestamp(value)
@ -131,7 +137,7 @@ class WorkspaceListItemResponse(ResponseModel):
@field_validator("status", mode="before")
@classmethod
def _normalize_status(cls, value):
def _normalize_enum_like(cls, value):
if value is None:
return None
if isinstance(value, str):
@ -144,7 +150,7 @@ class WorkspaceListItemResponse(ResponseModel):
return to_timestamp(value)
class WorkspaceListResponse(ResponseModel):
class WorkspacePaginationResponse(ResponseModel):
data: list[WorkspaceListItemResponse]
has_more: bool
limit: int
@ -157,7 +163,7 @@ class SwitchWorkspaceResponse(ResponseModel):
new_tenant: TenantInfoResponse
class WorkspaceMutationResponse(ResponseModel):
class WorkspaceTenantResultResponse(ResponseModel):
result: str
tenant: TenantInfoResponse
@ -172,6 +178,16 @@ class WorkspacePermissionResponse(ResponseModel):
allow_owner_transfer: bool
WORKSPACE_LOGO_UPLOAD_PARAMS = {
"file": {
"in": "formData",
"type": "file",
"required": True,
"description": "Workspace web app logo file. Only SVG and PNG files are supported.",
}
}
register_schema_models(
console_ns,
WorkspaceListQuery,
@ -182,49 +198,17 @@ register_schema_models(
register_response_schema_models(
console_ns,
TenantInfoResponse,
TenantListItemResponse,
TenantListResponse,
WorkspaceListResponse,
SwitchWorkspaceResponse,
WorkspaceMutationResponse,
WorkspaceLogoUploadResponse,
WorkspaceCustomConfigResponse,
WorkspaceListItemResponse,
WorkspacePaginationResponse,
SwitchWorkspaceResponse,
WorkspaceTenantResultResponse,
WorkspaceLogoUploadResponse,
WorkspacePermissionResponse,
)
provider_fields = {
"provider_name": fields.String,
"provider_type": fields.String,
"is_valid": fields.Boolean,
"token_is_set": fields.Boolean,
}
tenant_fields = {
"id": fields.String,
"name": fields.String,
"plan": fields.String,
"status": fields.String,
"created_at": TimestampField,
"role": fields.String,
"in_trial": fields.Boolean,
"trial_end_reason": fields.String,
"custom_config": fields.Raw(attribute="custom_config"),
"trial_credits": fields.Integer,
"trial_credits_used": fields.Integer,
"next_credit_reset_date": fields.Integer,
}
tenants_fields = {
"id": fields.String,
"name": fields.String,
"plan": fields.String,
"status": fields.String,
"created_at": TimestampField,
"last_opened_at": OptionalTimestampField,
"current": fields.Boolean,
}
workspace_fields = {"id": fields.String, "name": fields.String, "status": fields.String, "created_at": TimestampField}
@console_ns.route("/workspaces")
class TenantListApi(Resource):
@ -279,18 +263,17 @@ class TenantListApi(Resource):
tenant_dicts.append(tenant_dict)
return {"workspaces": marshal(tenant_dicts, tenants_fields)}, 200
return dump_response(TenantListResponse, {"workspaces": tenant_dicts}), 200
@console_ns.route("/all-workspaces")
class WorkspaceListApi(Resource):
@console_ns.doc(params=query_params_from_model(WorkspaceListQuery))
@console_ns.response(200, "Success", console_ns.models[WorkspaceListResponse.__name__])
@console_ns.response(200, "Success", console_ns.models[WorkspacePaginationResponse.__name__])
@setup_required
@admin_required
def get(self):
payload = request.args.to_dict(flat=True)
args = WorkspaceListQuery.model_validate(payload)
args = query_params_from_request(WorkspaceListQuery)
stmt = select(Tenant).order_by(Tenant.created_at.desc())
tenants = db.paginate(select=stmt, page=args.page, per_page=args.limit, error_out=False)
@ -299,13 +282,9 @@ class WorkspaceListApi(Resource):
if tenants.has_next:
has_more = True
return {
"data": marshal(tenants.items, workspace_fields),
"has_more": has_more,
"limit": args.limit,
"page": args.page,
"total": tenants.total,
}, 200
return WorkspacePaginationResponse(
data=tenants.items, has_more=has_more, limit=args.limit, page=args.page, total=tenants.total or 0
).model_dump(mode="json"), 200
@console_ns.route("/workspaces/current", endpoint="workspaces_current")
@ -359,13 +338,15 @@ class SwitchWorkspaceApi(Resource):
if new_tenant is None:
raise ValueError("Tenant not found")
return {"result": "success", "new_tenant": marshal(WorkspaceService.get_tenant_info(new_tenant), tenant_fields)}
return SwitchWorkspaceResponse(
result="success", new_tenant=WorkspaceService.get_tenant_info(new_tenant)
).model_dump(mode="json")
@console_ns.route("/workspaces/custom-config")
class CustomConfigWorkspaceApi(Resource):
@console_ns.expect(console_ns.models[WorkspaceCustomConfigPayload.__name__])
@console_ns.response(200, "Success", console_ns.models[WorkspaceMutationResponse.__name__])
@console_ns.response(200, "Success", console_ns.models[WorkspaceTenantResultResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@ -388,11 +369,14 @@ class CustomConfigWorkspaceApi(Resource):
tenant.custom_config_dict = custom_config_dict
db.session.commit()
return {"result": "success", "tenant": marshal(WorkspaceService.get_tenant_info(tenant), tenant_fields)}
return WorkspaceTenantResultResponse(
result="success", tenant=WorkspaceService.get_tenant_info(tenant)
).model_dump(mode="json")
@console_ns.route("/workspaces/custom-config/webapp-logo/upload")
class WebappLogoWorkspaceApi(Resource):
@console_ns.doc(consumes=["multipart/form-data"], params=WORKSPACE_LOGO_UPLOAD_PARAMS)
@console_ns.response(201, "Logo uploaded", console_ns.models[WorkspaceLogoUploadResponse.__name__])
@setup_required
@login_required
@ -429,13 +413,13 @@ class WebappLogoWorkspaceApi(Resource):
except services.errors.file.UnsupportedFileTypeError:
raise UnsupportedFileTypeError()
return {"id": upload_file.id}, 201
return WorkspaceLogoUploadResponse(id=upload_file.id).model_dump(mode="json"), 201
@console_ns.route("/workspaces/info")
class WorkspaceInfoApi(Resource):
@console_ns.expect(console_ns.models[WorkspaceInfoPayload.__name__])
@console_ns.response(200, "Success", console_ns.models[WorkspaceMutationResponse.__name__])
@console_ns.response(200, "Success", console_ns.models[WorkspaceTenantResultResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@ -451,7 +435,9 @@ class WorkspaceInfoApi(Resource):
tenant.name = args.name
db.session.commit()
return {"result": "success", "tenant": marshal(WorkspaceService.get_tenant_info(tenant), tenant_fields)}
return WorkspaceTenantResultResponse(
result="success", tenant=WorkspaceService.get_tenant_info(tenant)
).model_dump(mode="json")
@console_ns.route("/workspaces/current/permission")
@ -475,8 +461,8 @@ class WorkspacePermissionApi(Resource):
# Get workspace permissions from enterprise service
permission = EnterpriseService.WorkspacePermissionService.get_permission(current_tenant_id)
return {
"workspace_id": permission.workspace_id,
"allow_member_invite": permission.allow_member_invite,
"allow_owner_transfer": permission.allow_owner_transfer,
}, 200
return WorkspacePermissionResponse(
workspace_id=permission.workspace_id,
allow_member_invite=permission.allow_member_invite,
allow_owner_transfer=permission.allow_owner_transfer,
).model_dump(mode="json"), 200

View File

@ -15,13 +15,13 @@ simple_account_fields = {
}
class SimpleAccount(ResponseModel):
class SimpleAccountResponse(ResponseModel):
id: str
name: str
email: str
class _AccountAvatar(ResponseModel):
class _AccountAvatarResponseMixin(ResponseModel):
avatar: str | None = None
@computed_field(return_type=str | None) # type: ignore[prop-decorator]
@ -30,7 +30,7 @@ class _AccountAvatar(ResponseModel):
return build_avatar_url(self.avatar)
class Account(_AccountAvatar):
class AccountResponse(_AccountAvatarResponseMixin):
id: str
name: str
email: str
@ -48,7 +48,7 @@ class Account(_AccountAvatar):
return to_timestamp(value)
class AccountWithRole(_AccountAvatar):
class AccountWithRoleResponse(_AccountAvatarResponseMixin):
id: str
name: str
email: str
@ -65,5 +65,11 @@ class AccountWithRole(_AccountAvatar):
return to_timestamp(value)
class AccountWithRoleList(ResponseModel):
accounts: list[AccountWithRole]
class AccountWithRoleListResponse(ResponseModel):
accounts: list[AccountWithRoleResponse]
SimpleAccount = SimpleAccountResponse
Account = AccountResponse
AccountWithRole = AccountWithRoleResponse
AccountWithRoleList = AccountWithRoleListResponse

View File

@ -38,7 +38,7 @@ Get account avatar url
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [Account](#account)<br> |
| 200 | Success | **application/json**: [AccountResponse](#accountresponse)<br> |
### [POST] /account/change-email
#### Request Body
@ -77,7 +77,7 @@ Get account avatar url
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [Account](#account)<br> |
| 200 | Success | **application/json**: [AccountResponse](#accountresponse)<br> |
### [POST] /account/change-email/validity
#### Request Body
@ -141,9 +141,9 @@ Get account avatar url
#### Responses
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [EducationActivateResponse](#educationactivateresponse)<br> |
| Code | Description |
| ---- | ----------- |
| 200 | Success |
### [GET] /account/education/autocomplete
#### Parameters
@ -198,7 +198,7 @@ Get account avatar url
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [Account](#account)<br> |
| 200 | Success | **application/json**: [AccountResponse](#accountresponse)<br> |
### [POST] /account/interface-theme
#### Request Body
@ -211,7 +211,7 @@ Get account avatar url
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [Account](#account)<br> |
| 200 | Success | **application/json**: [AccountResponse](#accountresponse)<br> |
### [POST] /account/name
#### Request Body
@ -224,7 +224,7 @@ Get account avatar url
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [Account](#account)<br> |
| 200 | Success | **application/json**: [AccountResponse](#accountresponse)<br> |
### [POST] /account/password
#### Request Body
@ -237,14 +237,14 @@ Get account avatar url
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [Account](#account)<br> |
| 200 | Success | **application/json**: [AccountResponse](#accountresponse)<br> |
### [GET] /account/profile
#### Responses
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [Account](#account)<br> |
| 200 | Success | **application/json**: [AccountResponse](#accountresponse)<br> |
### [POST] /account/timezone
#### Request Body
@ -257,7 +257,7 @@ Get account avatar url
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [Account](#account)<br> |
| 200 | Success | **application/json**: [AccountResponse](#accountresponse)<br> |
### [POST] /activate
Activate account with invitation token
@ -1050,7 +1050,7 @@ Infer CLI tool + ENV suggestions from a standardized Agent App skill
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [WorkspaceListResponse](#workspacelistresponse)<br> |
| 200 | Success | **application/json**: [WorkspacePaginationResponse](#workspacepaginationresponse)<br> |
### [GET] /api-based-extension
Get all API-based extensions for current tenant
@ -9268,7 +9268,7 @@ Increment snippet use count by 1
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [AccountWithRoleList](#accountwithrolelist)<br> |
| 200 | Success | **application/json**: [AccountWithRoleListResponse](#accountwithrolelistresponse)<br> |
### [GET] /workspaces/current/default-model
#### Parameters
@ -9309,7 +9309,7 @@ Create a new plugin endpoint
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Endpoint created successfully | **application/json**: [EndpointCreateResponse](#endpointcreateresponse)<br> |
| 200 | Endpoint created successfully | **application/json**: [SuccessResponse](#successresponse)<br> |
| 403 | Admin privileges required | |
### ~~[POST] /workspaces/current/endpoints/create~~
@ -9328,7 +9328,7 @@ Deprecated legacy alias for creating a plugin endpoint. Use POST /workspaces/cur
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Endpoint created successfully | **application/json**: [EndpointCreateResponse](#endpointcreateresponse)<br> |
| 200 | Endpoint created successfully | **application/json**: [SuccessResponse](#successresponse)<br> |
| 403 | Admin privileges required | |
### ~~[POST] /workspaces/current/endpoints/delete~~
@ -9347,7 +9347,7 @@ Deprecated legacy alias for deleting a plugin endpoint. Use DELETE /workspaces/c
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Endpoint deleted successfully | **application/json**: [EndpointDeleteResponse](#endpointdeleteresponse)<br> |
| 200 | Endpoint deleted successfully | **application/json**: [SuccessResponse](#successresponse)<br> |
| 403 | Admin privileges required | |
### [POST] /workspaces/current/endpoints/disable
@ -9363,7 +9363,7 @@ Disable a plugin endpoint
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Endpoint disabled successfully | **application/json**: [EndpointDisableResponse](#endpointdisableresponse)<br> |
| 200 | Endpoint disabled successfully | **application/json**: [SuccessResponse](#successresponse)<br> |
| 403 | Admin privileges required | |
### [POST] /workspaces/current/endpoints/enable
@ -9379,7 +9379,7 @@ Enable a plugin endpoint
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Endpoint enabled successfully | **application/json**: [EndpointEnableResponse](#endpointenableresponse)<br> |
| 200 | Endpoint enabled successfully | **application/json**: [SuccessResponse](#successresponse)<br> |
| 403 | Admin privileges required | |
### [GET] /workspaces/current/endpoints/list
@ -9413,7 +9413,7 @@ List endpoints for a specific plugin
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [PluginEndpointListResponse](#pluginendpointlistresponse)<br> |
| 200 | Success | **application/json**: [EndpointListResponse](#endpointlistresponse)<br> |
### ~~[POST] /workspaces/current/endpoints/update~~
@ -9431,7 +9431,7 @@ Deprecated legacy alias for updating a plugin endpoint. Use PATCH /workspaces/cu
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Endpoint updated successfully | **application/json**: [EndpointUpdateResponse](#endpointupdateresponse)<br> |
| 200 | Endpoint updated successfully | **application/json**: [SuccessResponse](#successresponse)<br> |
| 403 | Admin privileges required | |
### [DELETE] /workspaces/current/endpoints/{id}
@ -9447,7 +9447,7 @@ Delete a plugin endpoint
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Endpoint deleted successfully | **application/json**: [EndpointDeleteResponse](#endpointdeleteresponse)<br> |
| 200 | Endpoint deleted successfully | **application/json**: [SuccessResponse](#successresponse)<br> |
| 403 | Admin privileges required | |
### [PATCH] /workspaces/current/endpoints/{id}
@ -9469,7 +9469,7 @@ Update a plugin endpoint
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Endpoint updated successfully | **application/json**: [EndpointUpdateResponse](#endpointupdateresponse)<br> |
| 200 | Endpoint updated successfully | **application/json**: [SuccessResponse](#successresponse)<br> |
| 403 | Admin privileges required | |
### [GET] /workspaces/current/members
@ -9477,7 +9477,7 @@ Update a plugin endpoint
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [AccountWithRoleList](#accountwithrolelist)<br> |
| 200 | Success | **application/json**: [AccountWithRoleListResponse](#accountwithrolelistresponse)<br> |
### [POST] /workspaces/current/members/invite-email
#### Request Body
@ -9529,7 +9529,7 @@ Update a plugin endpoint
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [MemberActionTenantResponse](#memberactiontenantresponse)<br> |
| 200 | Success | **application/json**: [MemberActionResponse](#memberactionresponse)<br> |
### [POST] /workspaces/current/members/{member_id}/owner-transfer
#### Parameters
@ -11739,9 +11739,15 @@ Returns permission flags that control workspace features like member invitations
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [WorkspaceMutationResponse](#workspacemutationresponse)<br> |
| 200 | Success | **application/json**: [WorkspaceTenantResultResponse](#workspacetenantresultresponse)<br> |
### [POST] /workspaces/custom-config/webapp-logo/upload
#### Request Body
| Required | Schema |
| -------- | ------ |
| Yes | **multipart/form-data**: { **"file"**: binary }<br> |
#### Responses
| Code | Description | Schema |
@ -11759,7 +11765,7 @@ Returns permission flags that control workspace features like member invitations
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [WorkspaceMutationResponse](#workspacemutationresponse)<br> |
| 200 | Success | **application/json**: [WorkspaceTenantResultResponse](#workspacetenantresultresponse)<br> |
### [POST] /workspaces/switch
#### Request Body
@ -11928,23 +11934,6 @@ Default namespace
| role_name | string | | No |
| tenant_id | string | | No |
#### Account
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| avatar | string | | No |
| avatar_url | string | | Yes |
| created_at | integer | | No |
| email | string | | Yes |
| id | string | | Yes |
| interface_language | string | | No |
| interface_theme | string | | No |
| is_password_set | boolean | | Yes |
| last_login_at | integer | | No |
| last_login_ip | string | | No |
| name | string | | Yes |
| timezone | string | | No |
#### AccountAvatarPayload
| Name | Type | Description | Required |
@ -12020,17 +12009,41 @@ Default namespace
| password | string | | No |
| repeat_new_password | string | | Yes |
#### AccountResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| avatar | string | | No |
| avatar_url | string | | Yes |
| created_at | integer | | No |
| email | string | | Yes |
| id | string | | Yes |
| interface_language | string | | No |
| interface_theme | string | | No |
| is_password_set | boolean | | Yes |
| last_login_at | integer | | No |
| last_login_ip | string | | No |
| name | string | | Yes |
| timezone | string | | No |
#### AccountTimezonePayload
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| timezone | string | | Yes |
#### AccountWithRole
#### AccountWithRoleListResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| accounts | [ [AccountWithRoleResponse](#accountwithroleresponse) ] | | Yes |
#### AccountWithRoleResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| avatar | string | | No |
| avatar_url | string | | Yes |
| created_at | integer | | No |
| email | string | | Yes |
| id | string | | Yes |
@ -12041,12 +12054,6 @@ Default namespace
| roles | [ object ] | | No |
| status | string | | Yes |
#### AccountWithRoleList
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| accounts | [ [AccountWithRole](#accountwithrole) ] | | Yes |
#### ActivateCheckQuery
| Name | Type | Description | Required |
@ -12095,7 +12102,7 @@ Default namespace
| ---- | ---- | ----------- | -------- |
| conversation_id | string | | No |
| created_at | integer | | No |
| created_by_account | [SimpleAccount](#simpleaccount) | | No |
| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No |
| elapsed_time | number | | No |
| exceptions_count | integer | | No |
| finished_at | integer | | No |
@ -13429,7 +13436,6 @@ Soft lifecycle state for Agent records.
| created_at | integer | | No |
| files | [ string ] | | Yes |
| id | string | | Yes |
| message_chain_id | string | | No |
| message_id | string | | Yes |
| observation | string | | No |
| position | integer | | Yes |
@ -13909,6 +13915,12 @@ AppMCPServer Status Enum
| use_icon_as_answer_icon | boolean | | No |
| workflow | [WorkflowPartial](#workflowpartial) | | No |
#### AppSelectorScope
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| AppSelectorScope | string | | |
#### AppSiteResponse
| Name | Type | Description | Required |
@ -14540,8 +14552,8 @@ Enum class for configurate method of provider model.
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| annotation_create_account | [SimpleAccount](#simpleaccount) | | No |
| annotation_id | string | | Yes |
| created_at | integer | | No |
| id | string | | Yes |
#### ConversationDetail
@ -15782,12 +15794,6 @@ Request payload for bulk downloading documents as a zip archive.
| role | string | | Yes |
| token | string | | Yes |
#### EducationActivateResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| EducationActivateResponse | object | | |
#### EducationAutocompleteQuery
| Name | Type | Description | Required |
@ -15897,29 +15903,32 @@ Request payload for bulk downloading documents as a zip archive.
| plugin_unique_identifier | string | | Yes |
| settings | object | | Yes |
#### EndpointCreateResponse
#### EndpointDeclaration
declaration of an endpoint
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| success | boolean | Operation success | Yes |
| hidden | boolean | | No |
| method | string | | Yes |
| path | string | | Yes |
#### EndpointDeleteResponse
#### EndpointEntityWithInstance
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| success | boolean | Operation success | Yes |
#### EndpointDisableResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| success | boolean | Operation success | Yes |
#### EndpointEnableResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| success | boolean | Operation success | Yes |
| created_at | dateTime | | Yes |
| declaration | [EndpointProviderDeclaration](#endpointproviderdeclaration) | | No |
| enabled | boolean | | Yes |
| expired_at | dateTime | | Yes |
| hook_id | string | | Yes |
| id | string | | Yes |
| name | string | | Yes |
| plugin_id | string | | Yes |
| settings | object | | Yes |
| tenant_id | string | | Yes |
| updated_at | dateTime | | Yes |
| url | string | | Yes |
#### EndpointIdPayload
@ -15946,7 +15955,23 @@ Request payload for bulk downloading documents as a zip archive.
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| endpoints | [ object ] | Endpoint information | Yes |
| endpoints | [ [EndpointEntityWithInstance](#endpointentitywithinstance) ] | Endpoint information | Yes |
#### EndpointProviderDeclaration
declaration of an endpoint group
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| endpoints | [ [EndpointDeclaration](#endpointdeclaration) ] | | No |
| settings | [ [ProviderConfig](#providerconfig) ] | | No |
#### EndpointSettingsPayload
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| name | string | | Yes |
| settings | object | | Yes |
#### EndpointUpdatePayload
@ -15955,12 +15980,6 @@ Request payload for bulk downloading documents as a zip archive.
| name | string | | Yes |
| settings | object | | Yes |
#### EndpointUpdateResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| success | boolean | Operation success | Yes |
#### EnvSuggestion
| Name | Type | Description | Required |
@ -16986,12 +17005,12 @@ Enum class for large language model mode.
| marketplace_plugin_unique_identifier | string | | Yes |
| version | string | | No |
#### MemberActionTenantResponse
#### MemberActionResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| result | string | | Yes |
| tenant_id | string | | Yes |
| tenant_id | string | | No |
#### MemberBindingsResponse
@ -17079,6 +17098,7 @@ Enum class for large language model mode.
| agent_thoughts | [ [AgentThought](#agentthought) ] | | No |
| annotation | [ConversationAnnotation](#conversationannotation) | | No |
| annotation_hit_history | [ConversationAnnotationHitHistory](#conversationannotationhithistory) | | No |
| answer | string | | Yes |
| answer_tokens | integer | | No |
| conversation_id | string | | Yes |
| created_at | integer | | No |
@ -17092,12 +17112,11 @@ Enum class for large language model mode.
| inputs | object | | Yes |
| message | [JSONValue](#jsonvalue) | | No |
| message_files | [ [MessageFile](#messagefile) ] | | No |
| message_metadata_dict | [JSONValue](#jsonvalue) | | No |
| message_tokens | integer | | No |
| metadata | [JSONValue](#jsonvalue) | | No |
| parent_message_id | string | | No |
| provider_response_latency | number | | No |
| query | string | | Yes |
| re_sign_file_url_answer | string | | Yes |
| status | string | | Yes |
| workflow_run_id | string | | No |
@ -17281,6 +17300,12 @@ Enum class for model property key.
| ---- | ---- | ----------- | -------- |
| payment_link | string | | Yes |
#### ModelSelectorScope
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| ModelSelectorScope | string | | |
#### ModelStatus
Enum class for model status.
@ -17588,6 +17613,13 @@ Coarse node-level status used by Inspector to pick a banner.
| ---- | ---- | ----------- | -------- |
| OpaqueObjectResponse | object | | |
#### Option
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| label | [I18nObject](#i18nobject) | The label of the option | Yes |
| value | string | The value of the option | Yes |
#### OutputErrorStrategy
Per-output failure handling strategy.
@ -18287,12 +18319,6 @@ Shared permission levels for resources (datasets, credentials, etc.)
| ---- | ---- | ----------- | -------- |
| options | | | Yes |
#### PluginEndpointListResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| endpoints | [ object ] | Endpoint information | Yes |
#### PluginInstallationPermissionModel
| Name | Type | Description | Required |
@ -18429,6 +18455,24 @@ Dataset Process Rule Mode
| ---- | ---- | ----------- | -------- |
| ProcessRuleMode | string | Dataset Process Rule Mode | |
#### ProviderConfig
Model class for common provider settings like credentials
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| default | integer<br>string<br>number<br>boolean | | No |
| help | [I18nObject](#i18nobject) | | No |
| label | [I18nObject](#i18nobject) | | No |
| multiple | boolean | | No |
| name | string | The name of the credentials | Yes |
| options | [ [Option](#option) ] | | No |
| placeholder | [I18nObject](#i18nobject) | | No |
| required | boolean | | No |
| scope | [AppSelectorScope](#appselectorscope)<br>[ModelSelectorScope](#modelselectorscope)<br>[ToolSelectorScope](#toolselectorscope) | | No |
| type | [Type](#type) | The type of the credentials | Yes |
| url | string | | No |
#### ProviderCredentialResponse
| Name | Type | Description | Required |
@ -19151,6 +19195,14 @@ Model class for provider quota configuration.
| id | string | | Yes |
| name | string | | Yes |
#### SimpleAccountResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| email | string | | Yes |
| id | string | | Yes |
| name | string | | Yes |
#### SimpleConversation
| Name | Type | Description | Required |
@ -19458,7 +19510,7 @@ Query parameters for listing snippet published workflows.
| ---- | ---- | ----------- | -------- |
| conversation_variables | [ [WorkflowConversationVariableResponse](#workflowconversationvariableresponse) ] | | Yes |
| created_at | integer | | Yes |
| created_by | [SimpleAccount](#simpleaccount) | | No |
| created_by | [SimpleAccountResponse](#simpleaccountresponse) | | No |
| environment_variables | [ [WorkflowEnvironmentVariableResponse](#workflowenvironmentvariableresponse) ] | | Yes |
| features | object | | Yes |
| graph | object | | Yes |
@ -19470,7 +19522,7 @@ Query parameters for listing snippet published workflows.
| rag_pipeline_variables | [ [PipelineVariableResponse](#pipelinevariableresponse) ] | | Yes |
| tool_published | boolean | | Yes |
| updated_at | integer | | Yes |
| updated_by | [SimpleAccount](#simpleaccount) | | No |
| updated_by | [SimpleAccountResponse](#simpleaccountresponse) | | No |
| version | string | | Yes |
#### StarredAppListQuery
@ -19722,6 +19774,7 @@ Tag type
| created_at | integer | | No |
| current | boolean | | Yes |
| id | string | | Yes |
| last_opened_at | integer | | No |
| name | string | | No |
| plan | string | | No |
| status | string | | No |
@ -19841,6 +19894,12 @@ Enum class for tool provider
| ---- | ---- | ----------- | -------- |
| ToolProviderType | string | Enum class for tool provider | |
#### ToolSelectorScope
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| ToolSelectorScope | string | | |
#### TraceAppConfigResponse
| Name | Type | Description | Required |
@ -20390,7 +20449,7 @@ How a workflow node is bound to an Agent.
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| created_at | integer | | No |
| created_by_account | [SimpleAccount](#simpleaccount) | | No |
| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No |
| created_by_end_user | [SimpleEndUser](#simpleenduser) | | No |
| created_by_role | string | | No |
| created_from | string | | No |
@ -20427,7 +20486,7 @@ How a workflow node is bound to an Agent.
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| created_at | integer | | No |
| created_by_account | [SimpleAccount](#simpleaccount) | | No |
| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No |
| created_by_end_user | [SimpleEndUser](#simpleenduser) | | No |
| id | string | | Yes |
| trigger_metadata | | | No |
@ -20528,7 +20587,7 @@ How a workflow node is bound to an Agent.
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| users | [ [AccountWithRole](#accountwithrole) ] | | Yes |
| users | [ [AccountWithRoleResponse](#accountwithroleresponse) ] | | Yes |
#### WorkflowCommentReply
@ -20877,7 +20936,7 @@ can reuse its existing handler.
| ---- | ---- | ----------- | -------- |
| conversation_variables | [ [WorkflowConversationVariableResponse](#workflowconversationvariableresponse) ] | | Yes |
| created_at | integer | | Yes |
| created_by | [SimpleAccount](#simpleaccount) | | No |
| created_by | [SimpleAccountResponse](#simpleaccountresponse) | | No |
| environment_variables | [ [WorkflowEnvironmentVariableResponse](#workflowenvironmentvariableresponse) ] | | Yes |
| features | object | | Yes |
| graph | object | | Yes |
@ -20888,7 +20947,7 @@ can reuse its existing handler.
| rag_pipeline_variables | [ [PipelineVariableResponse](#pipelinevariableresponse) ] | | Yes |
| tool_published | boolean | | Yes |
| updated_at | integer | | Yes |
| updated_by | [SimpleAccount](#simpleaccount) | | No |
| updated_by | [SimpleAccountResponse](#simpleaccountresponse) | | No |
| version | string | | Yes |
#### WorkflowRestoreResponse
@ -20923,7 +20982,7 @@ can reuse its existing handler.
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| created_at | integer | | No |
| created_by_account | [SimpleAccount](#simpleaccount) | | No |
| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No |
| created_by_end_user | [SimpleEndUser](#simpleenduser) | | No |
| created_by_role | string | | No |
| elapsed_time | number | | No |
@ -20962,7 +21021,7 @@ can reuse its existing handler.
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| created_at | integer | | No |
| created_by_account | [SimpleAccount](#simpleaccount) | | No |
| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No |
| elapsed_time | number | | No |
| exceptions_count | integer | | No |
| finished_at | integer | | No |
@ -21009,7 +21068,7 @@ can reuse its existing handler.
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| created_at | integer | | No |
| created_by_account | [SimpleAccount](#simpleaccount) | | No |
| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No |
| created_by_end_user | [SimpleEndUser](#simpleenduser) | | No |
| created_by_role | string | | No |
| elapsed_time | number | | No |
@ -21202,7 +21261,13 @@ Workflow tool configuration
| limit | integer, <br>**Default:** 20 | | No |
| page | integer, <br>**Default:** 1 | | No |
#### WorkspaceListResponse
#### WorkspaceLogoUploadResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| id | string | | Yes |
#### WorkspacePaginationResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
@ -21212,19 +21277,6 @@ Workflow tool configuration
| page | integer | | Yes |
| total | integer | | Yes |
#### WorkspaceLogoUploadResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| id | string | | Yes |
#### WorkspaceMutationResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| result | string | | Yes |
| tenant | [TenantInfoResponse](#tenantinforesponse) | | Yes |
#### WorkspacePermissionResponse
| Name | Type | Description | Required |
@ -21239,6 +21291,13 @@ Workflow tool configuration
| ---- | ---- | ----------- | -------- |
| permission_keys | [ string ] | | No |
#### WorkspaceTenantResultResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| result | string | | Yes |
| tenant | [TenantInfoResponse](#tenantinforesponse) | | Yes |
#### _AccessPolicyList
| Name | Type | Description | Required |

View File

@ -3937,7 +3937,7 @@ Model class for provider with models response.
| output_variable_name | string | | Yes |
| type | string | | No |
#### SimpleAccount
#### SimpleAccountResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
@ -4147,7 +4147,7 @@ in form definiton, or a variable while the workflow is running.
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| created_at | integer | | No |
| created_by_account | [SimpleAccount](#simpleaccount) | | No |
| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No |
| created_by_end_user | [SimpleEndUser](#simpleenduser) | | No |
| created_by_role | string | | No |
| created_from | string | | No |

View File

@ -1,4 +1,5 @@
import inspect
from datetime import UTC, datetime
from unittest.mock import patch
import pytest
@ -16,9 +17,32 @@ from controllers.console.workspace.endpoint import (
EndpointListApi,
EndpointListForSinglePluginApi,
)
from core.plugin.entities.endpoint import EndpointEntityWithInstance
from core.plugin.impl.exc import PluginPermissionDeniedError
def _endpoint_entity() -> EndpointEntityWithInstance:
now = datetime(2026, 1, 1, tzinfo=UTC)
return EndpointEntityWithInstance(
id="e1",
created_at=now,
updated_at=now,
tenant_id="t1",
plugin_id="p1",
settings={
"secret": "value",
"enabled": True,
"ids": ["a", "b"],
"nested": {"limit": 3},
},
expired_at=now,
name="endpoint",
enabled=True,
url="https://example.test/hook-1",
hook_id="hook-1",
)
class TestEndpointCollectionApi:
def test_create_success(self, app: Flask):
api = EndpointCollectionApi()
@ -102,12 +126,34 @@ class TestEndpointListApi:
with (
app.test_request_context("/?page=1&page_size=10"),
patch("controllers.console.workspace.endpoint.EndpointService.list_endpoints", return_value=[{"id": "e1"}]),
patch(
"controllers.console.workspace.endpoint.EndpointService.list_endpoints",
return_value=[_endpoint_entity()],
),
):
result = method(api, "t1", "u1")
assert "endpoints" in result
assert len(result["endpoints"]) == 1
assert result["endpoints"] == [
{
"id": "e1",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z",
"settings": {
"secret": "value",
"enabled": True,
"ids": ["a", "b"],
"nested": {"limit": 3},
},
"tenant_id": "t1",
"plugin_id": "p1",
"expired_at": "2026-01-01T00:00:00Z",
"declaration": {"settings": [], "endpoints": []},
"name": "endpoint",
"enabled": True,
"url": "https://example.test/hook-1",
"hook_id": "hook-1",
}
]
def test_list_invalid_query(self, app: Flask):
api = EndpointListApi()
@ -129,12 +175,13 @@ class TestEndpointListForSinglePluginApi:
app.test_request_context("/?page=1&page_size=10&plugin_id=p1"),
patch(
"controllers.console.workspace.endpoint.EndpointService.list_endpoints_for_single_plugin",
return_value=[{"id": "e1"}],
return_value=[_endpoint_entity()],
),
):
result = method(api, "t1", "u1")
assert "endpoints" in result
assert result["endpoints"][0]["id"] == "e1"
assert result["endpoints"][0]["settings"]["nested"] == {"limit": 3}
def test_list_for_plugin_missing_param(self, app: Flask):
api = EndpointListForSinglePluginApi()

View File

@ -26,7 +26,9 @@ from controllers.console.workspace.workspace import (
WebappLogoWorkspaceApi,
WorkspaceInfoApi,
WorkspaceListApi,
WorkspaceLogoUploadResponse,
WorkspacePermissionApi,
WorkspacePermissionResponse,
)
from enums.cloud_plan import CloudPlan
from libs.datetime_utils import naive_utc_now
@ -587,7 +589,8 @@ class TestWebappLogoWorkspaceApi:
result, status = method(api, user)
assert status == 201
assert result["id"] == "file1"
assert result == {"id": "file1"}
assert WorkspaceLogoUploadResponse.model_validate(result).model_dump(mode="json") == {"id": "file1"}
def test_filename_missing(self, app: Flask):
api = WebappLogoWorkspaceApi()
@ -676,7 +679,7 @@ class TestWorkspaceInfoApi:
patch("controllers.console.workspace.workspace.db.session.commit"),
patch(
"controllers.console.workspace.workspace.WorkspaceService.get_tenant_info",
return_value={"name": "New Name"},
return_value={"id": "t1", "name": "New Name"},
),
):
result = method(api, "t1")
@ -717,7 +720,13 @@ class TestWorkspacePermissionApi:
result, status = method(api, "t1")
assert status == 200
assert result["workspace_id"] == "t1"
expected = {
"workspace_id": "t1",
"allow_member_invite": True,
"allow_owner_transfer": False,
}
assert result == expected
assert WorkspacePermissionResponse.model_validate(result).model_dump(mode="json") == expected
def test_no_current_tenant(self, app: Flask):
api = WorkspacePermissionApi()

View File

@ -27,8 +27,6 @@ import {
zPostAccountDeleteFeedbackBody,
zPostAccountDeleteFeedbackResponse,
zPostAccountDeleteResponse,
zPostAccountEducationBody,
zPostAccountEducationResponse,
zPostAccountInitBody,
zPostAccountInitResponse,
zPostAccountInterfaceLanguageBody,
@ -222,25 +220,13 @@ export const get5 = oc
})
.output(zGetAccountEducationResponse)
export const post8 = oc
.route({
inputStructure: 'detailed',
method: 'POST',
operationId: 'postAccountEducation',
path: '/account/education',
tags: ['console'],
})
.input(z.object({ body: zPostAccountEducationBody }))
.output(zPostAccountEducationResponse)
export const education = {
get: get5,
post: post8,
autocomplete,
verify: verify2,
}
export const post9 = oc
export const post8 = oc
.route({
inputStructure: 'detailed',
method: 'POST',
@ -252,7 +238,7 @@ export const post9 = oc
.output(zPostAccountInitResponse)
export const init = {
post: post9,
post: post8,
}
export const get6 = oc
@ -269,7 +255,7 @@ export const integrates = {
get: get6,
}
export const post10 = oc
export const post9 = oc
.route({
inputStructure: 'detailed',
method: 'POST',
@ -281,10 +267,10 @@ export const post10 = oc
.output(zPostAccountInterfaceLanguageResponse)
export const interfaceLanguage = {
post: post10,
post: post9,
}
export const post11 = oc
export const post10 = oc
.route({
inputStructure: 'detailed',
method: 'POST',
@ -296,10 +282,10 @@ export const post11 = oc
.output(zPostAccountInterfaceThemeResponse)
export const interfaceTheme = {
post: post11,
post: post10,
}
export const post12 = oc
export const post11 = oc
.route({
inputStructure: 'detailed',
method: 'POST',
@ -311,10 +297,10 @@ export const post12 = oc
.output(zPostAccountNameResponse)
export const name = {
post: post12,
post: post11,
}
export const post13 = oc
export const post12 = oc
.route({
inputStructure: 'detailed',
method: 'POST',
@ -326,7 +312,7 @@ export const post13 = oc
.output(zPostAccountPasswordResponse)
export const password = {
post: post13,
post: post12,
}
export const get7 = oc
@ -343,7 +329,7 @@ export const profile = {
get: get7,
}
export const post14 = oc
export const post13 = oc
.route({
inputStructure: 'detailed',
method: 'POST',
@ -355,7 +341,7 @@ export const post14 = oc
.output(zPostAccountTimezoneResponse)
export const timezone = {
post: post14,
post: post13,
}
export const account = {

View File

@ -12,7 +12,7 @@ export type AccountAvatarPayload = {
avatar: string
}
export type Account = {
export type AccountResponse = {
avatar?: string | null
readonly avatar_url: string | null
created_at?: number | null
@ -81,16 +81,6 @@ export type EducationStatusResponse = {
result?: boolean | null
}
export type EducationActivatePayload = {
institution: string
role: string
token: string
}
export type EducationActivateResponse = {
[key: string]: unknown
}
export type EducationAutocompleteResponse = {
curr_page?: number | null
data?: Array<string>
@ -140,7 +130,7 @@ export type AccountIntegrateResponse = {
provider: string
}
export type AccountWritable = {
export type AccountResponseWritable = {
avatar?: string | null
created_at?: number | null
email: string
@ -177,7 +167,7 @@ export type PostAccountAvatarData = {
}
export type PostAccountAvatarResponses = {
200: Account
200: AccountResponse
}
export type PostAccountAvatarResponse = PostAccountAvatarResponses[keyof PostAccountAvatarResponses]
@ -218,7 +208,7 @@ export type PostAccountChangeEmailResetData = {
}
export type PostAccountChangeEmailResetResponses = {
200: Account
200: AccountResponse
}
export type PostAccountChangeEmailResetResponse
@ -293,20 +283,6 @@ export type GetAccountEducationResponses = {
export type GetAccountEducationResponse
= GetAccountEducationResponses[keyof GetAccountEducationResponses]
export type PostAccountEducationData = {
body: EducationActivatePayload
path?: never
query?: never
url: '/account/education'
}
export type PostAccountEducationResponses = {
200: EducationActivateResponse
}
export type PostAccountEducationResponse
= PostAccountEducationResponses[keyof PostAccountEducationResponses]
export type GetAccountEducationAutocompleteData = {
body?: never
path?: never
@ -374,7 +350,7 @@ export type PostAccountInterfaceLanguageData = {
}
export type PostAccountInterfaceLanguageResponses = {
200: Account
200: AccountResponse
}
export type PostAccountInterfaceLanguageResponse
@ -388,7 +364,7 @@ export type PostAccountInterfaceThemeData = {
}
export type PostAccountInterfaceThemeResponses = {
200: Account
200: AccountResponse
}
export type PostAccountInterfaceThemeResponse
@ -402,7 +378,7 @@ export type PostAccountNameData = {
}
export type PostAccountNameResponses = {
200: Account
200: AccountResponse
}
export type PostAccountNameResponse = PostAccountNameResponses[keyof PostAccountNameResponses]
@ -415,7 +391,7 @@ export type PostAccountPasswordData = {
}
export type PostAccountPasswordResponses = {
200: Account
200: AccountResponse
}
export type PostAccountPasswordResponse
@ -429,7 +405,7 @@ export type GetAccountProfileData = {
}
export type GetAccountProfileResponses = {
200: Account
200: AccountResponse
}
export type GetAccountProfileResponse = GetAccountProfileResponses[keyof GetAccountProfileResponses]
@ -442,7 +418,7 @@ export type PostAccountTimezoneData = {
}
export type PostAccountTimezoneResponses = {
200: Account
200: AccountResponse
}
export type PostAccountTimezoneResponse

View File

@ -17,9 +17,9 @@ export const zAccountAvatarPayload = z.object({
})
/**
* Account
* AccountResponse
*/
export const zAccount = z.object({
export const zAccountResponse = z.object({
avatar: z.string().nullish(),
avatar_url: z.string().nullable(),
created_at: z.int().nullish(),
@ -118,20 +118,6 @@ export const zEducationStatusResponse = z.object({
result: z.boolean().nullish(),
})
/**
* EducationActivatePayload
*/
export const zEducationActivatePayload = z.object({
institution: z.string(),
role: z.string(),
token: z.string(),
})
/**
* EducationActivateResponse
*/
export const zEducationActivateResponse = z.record(z.string(), z.unknown())
/**
* EducationAutocompleteResponse
*/
@ -212,9 +198,9 @@ export const zAccountIntegrateListResponse = z.object({
})
/**
* Account
* AccountResponse
*/
export const zAccountWritable = z.object({
export const zAccountResponseWritable = z.object({
avatar: z.string().nullish(),
created_at: z.int().nullish(),
email: z.string(),
@ -242,7 +228,7 @@ export const zPostAccountAvatarBody = zAccountAvatarPayload
/**
* Success
*/
export const zPostAccountAvatarResponse = zAccount
export const zPostAccountAvatarResponse = zAccountResponse
export const zPostAccountChangeEmailBody = zChangeEmailSendPayload
@ -263,7 +249,7 @@ export const zPostAccountChangeEmailResetBody = zChangeEmailResetPayload
/**
* Success
*/
export const zPostAccountChangeEmailResetResponse = zAccount
export const zPostAccountChangeEmailResetResponse = zAccountResponse
export const zPostAccountChangeEmailValidityBody = zChangeEmailValidityPayload
@ -296,13 +282,6 @@ export const zGetAccountDeleteVerifyResponse = zSimpleResultDataResponse
*/
export const zGetAccountEducationResponse = zEducationStatusResponse
export const zPostAccountEducationBody = zEducationActivatePayload
/**
* Success
*/
export const zPostAccountEducationResponse = zEducationActivateResponse
export const zGetAccountEducationAutocompleteQuery = z.object({
keywords: z.string(),
limit: z.int().optional().default(20),
@ -336,37 +315,37 @@ export const zPostAccountInterfaceLanguageBody = zAccountInterfaceLanguagePayloa
/**
* Success
*/
export const zPostAccountInterfaceLanguageResponse = zAccount
export const zPostAccountInterfaceLanguageResponse = zAccountResponse
export const zPostAccountInterfaceThemeBody = zAccountInterfaceThemePayload
/**
* Success
*/
export const zPostAccountInterfaceThemeResponse = zAccount
export const zPostAccountInterfaceThemeResponse = zAccountResponse
export const zPostAccountNameBody = zAccountNamePayload
/**
* Success
*/
export const zPostAccountNameResponse = zAccount
export const zPostAccountNameResponse = zAccountResponse
export const zPostAccountPasswordBody = zAccountPasswordPayload
/**
* Success
*/
export const zPostAccountPasswordResponse = zAccount
export const zPostAccountPasswordResponse = zAccountResponse
/**
* Success
*/
export const zGetAccountProfileResponse = zAccount
export const zGetAccountProfileResponse = zAccountResponse
export const zPostAccountTimezoneBody = zAccountTimezonePayload
/**
* Success
*/
export const zPostAccountTimezoneResponse = zAccount
export const zPostAccountTimezoneResponse = zAccountResponse

View File

@ -269,6 +269,7 @@ export type MessageDetailResponse = {
agent_thoughts?: Array<AgentThought>
annotation?: ConversationAnnotation | null
annotation_hit_history?: ConversationAnnotationHitHistory | null
answer: string
answer_tokens?: number | null
conversation_id: string
created_at?: number | null
@ -284,12 +285,11 @@ export type MessageDetailResponse = {
}
message?: JsonValue | null
message_files?: Array<MessageFile>
message_metadata_dict?: JsonValue | null
message_tokens?: number | null
metadata?: JsonValue | null
parent_message_id?: string | null
provider_response_latency?: number | null
query: string
re_sign_file_url_answer: string
status: string
workflow_run_id?: string | null
}
@ -723,7 +723,6 @@ export type AgentThought = {
created_at?: number | null
files: Array<string>
id: string
message_chain_id?: string | null
message_id: string
observation?: string | null
position: number
@ -743,8 +742,8 @@ export type ConversationAnnotation = {
export type ConversationAnnotationHitHistory = {
annotation_create_account?: SimpleAccount | null
annotation_id: string
created_at?: number | null
id: string
}
export type HumanInputContent = {

View File

@ -570,7 +570,6 @@ export const zAgentThought = z.object({
created_at: z.int().nullish(),
files: z.array(z.string()),
id: z.string(),
message_chain_id: z.string().nullish(),
message_id: z.string(),
observation: z.string().nullish(),
position: z.int(),
@ -1056,8 +1055,8 @@ export const zConversationAnnotation = z.object({
*/
export const zConversationAnnotationHitHistory = z.object({
annotation_create_account: zSimpleAccount.nullish(),
annotation_id: z.string(),
created_at: z.int().nullish(),
id: z.string(),
})
/**
@ -2035,6 +2034,7 @@ export const zMessageDetailResponse = z.object({
agent_thoughts: z.array(zAgentThought).optional(),
annotation: zConversationAnnotation.nullish(),
annotation_hit_history: zConversationAnnotationHitHistory.nullish(),
answer: z.string(),
answer_tokens: z.int().nullish(),
conversation_id: z.string(),
created_at: z.int().nullish(),
@ -2048,12 +2048,11 @@ export const zMessageDetailResponse = z.object({
inputs: z.record(z.string(), zJsonValue),
message: zJsonValue.nullish(),
message_files: z.array(zMessageFile).optional(),
message_metadata_dict: zJsonValue.nullish(),
message_tokens: z.int().nullish(),
metadata: zJsonValue.nullish(),
parent_message_id: z.string().nullish(),
provider_response_latency: z.number().nullish(),
query: z.string(),
re_sign_file_url_answer: z.string(),
status: z.string(),
workflow_run_id: z.string().nullish(),
})

View File

@ -4,7 +4,7 @@ export type ClientOptions = {
baseUrl: `${string}://${string}/console/api` | (string & {})
}
export type WorkspaceListResponse = {
export type WorkspacePaginationResponse = {
data: Array<WorkspaceListItemResponse>
has_more: boolean
limit: number
@ -30,7 +30,7 @@ export type GetAllWorkspacesData = {
}
export type GetAllWorkspacesResponses = {
200: WorkspaceListResponse
200: WorkspacePaginationResponse
}
export type GetAllWorkspacesResponse = GetAllWorkspacesResponses[keyof GetAllWorkspacesResponses]

View File

@ -13,9 +13,9 @@ export const zWorkspaceListItemResponse = z.object({
})
/**
* WorkspaceListResponse
* WorkspacePaginationResponse
*/
export const zWorkspaceListResponse = z.object({
export const zWorkspacePaginationResponse = z.object({
data: z.array(zWorkspaceListItemResponse),
has_more: z.boolean(),
limit: z.int(),
@ -31,4 +31,4 @@ export const zGetAllWorkspacesQuery = z.object({
/**
* Success
*/
export const zGetAllWorkspacesResponse = zWorkspaceListResponse
export const zGetAllWorkspacesResponse = zWorkspacePaginationResponse

View File

@ -472,6 +472,7 @@ export type MessageDetailResponse = {
agent_thoughts?: Array<AgentThought>
annotation?: ConversationAnnotation | null
annotation_hit_history?: ConversationAnnotationHitHistory | null
answer: string
answer_tokens?: number | null
conversation_id: string
created_at?: number | null
@ -487,12 +488,11 @@ export type MessageDetailResponse = {
}
message?: JsonValue | null
message_files?: Array<MessageFile>
message_metadata_dict?: JsonValue | null
message_tokens?: number | null
metadata?: JsonValue | null
parent_message_id?: string | null
provider_response_latency?: number | null
query: string
re_sign_file_url_answer: string
status: string
workflow_run_id?: string | null
}
@ -731,7 +731,7 @@ export type WorkflowRunPaginationResponse = {
export type WorkflowRunDetailResponse = {
created_at?: number | null
created_by_account?: SimpleAccount | null
created_by_account?: SimpleAccountResponse | null
created_by_end_user?: SimpleEndUser | null
created_by_role?: string | null
elapsed_time?: number | null
@ -799,7 +799,7 @@ export type WorkflowCommentCreate = {
}
export type WorkflowCommentMentionUsersPayload = {
users: Array<AccountWithRole>
users: Array<AccountWithRoleResponse>
}
export type WorkflowCommentDetail = {
@ -887,7 +887,7 @@ export type DefaultBlockConfigResponse = {
export type WorkflowResponse = {
conversation_variables: Array<WorkflowConversationVariableResponse>
created_at: number
created_by?: SimpleAccount | null
created_by?: SimpleAccountResponse | null
environment_variables: Array<WorkflowEnvironmentVariableResponse>
features: {
[key: string]: unknown
@ -902,7 +902,7 @@ export type WorkflowResponse = {
rag_pipeline_variables: Array<PipelineVariableResponse>
tool_published: boolean
updated_at: number
updated_by?: SimpleAccount | null
updated_by?: SimpleAccountResponse | null
version: string
}
@ -1029,7 +1029,7 @@ export type AgentComposerValidateResponse = {
export type WorkflowRunNodeExecutionResponse = {
created_at?: number | null
created_by_account?: SimpleAccount | null
created_by_account?: SimpleAccountResponse | null
created_by_end_user?: SimpleEndUser | null
created_by_role?: string | null
elapsed_time?: number | null
@ -1289,7 +1289,7 @@ export type WorkflowOnlineUsersByApp = {
export type AdvancedChatWorkflowRunForListResponse = {
conversation_id?: string | null
created_at?: number | null
created_by_account?: SimpleAccount | null
created_by_account?: SimpleAccountResponse | null
elapsed_time?: number | null
exceptions_count?: number | null
finished_at?: number | null
@ -1498,7 +1498,6 @@ export type AgentThought = {
created_at?: number | null
files: Array<string>
id: string
message_chain_id?: string | null
message_id: string
observation?: string | null
position: number
@ -1518,8 +1517,8 @@ export type ConversationAnnotation = {
export type ConversationAnnotationHitHistory = {
annotation_create_account?: SimpleAccount | null
annotation_id: string
created_at?: number | null
id: string
}
export type HumanInputContent = {
@ -1596,7 +1595,7 @@ export type UserSatisfactionRateStatisticItem = {
export type WorkflowAppLogPartialResponse = {
created_at?: number | null
created_by_account?: SimpleAccount | null
created_by_account?: SimpleAccountResponse | null
created_by_end_user?: SimpleEndUser | null
created_by_role?: string | null
created_from?: string | null
@ -1607,7 +1606,7 @@ export type WorkflowAppLogPartialResponse = {
export type WorkflowArchivedLogPartialResponse = {
created_at?: number | null
created_by_account?: SimpleAccount | null
created_by_account?: SimpleAccountResponse | null
created_by_end_user?: SimpleEndUser | null
id: string
trigger_metadata?: unknown
@ -1616,7 +1615,7 @@ export type WorkflowArchivedLogPartialResponse = {
export type WorkflowRunForListResponse = {
created_at?: number | null
created_by_account?: SimpleAccount | null
created_by_account?: SimpleAccountResponse | null
elapsed_time?: number | null
exceptions_count?: number | null
finished_at?: number | null
@ -1628,7 +1627,7 @@ export type WorkflowRunForListResponse = {
version?: string | null
}
export type SimpleAccount = {
export type SimpleAccountResponse = {
email: string
id: string
name: string
@ -1671,8 +1670,9 @@ export type WorkflowCommentBasic = {
updated_at?: number | null
}
export type AccountWithRole = {
export type AccountWithRoleResponse = {
avatar?: string | null
readonly avatar_url: string | null
created_at?: number | null
email: string
id: string
@ -1984,7 +1984,14 @@ export type ModelConfigPartial = {
export type LlmMode = 'chat' | 'completion'
export type Type = 'github' | 'marketplace' | 'package'
export type Type
= | 'app-selector'
| 'array[tools]'
| 'boolean'
| 'model-selector'
| 'secret-input'
| 'select'
| 'text-input'
export type Github = {
github_plugin_unique_identifier: string
@ -2054,6 +2061,12 @@ export type SimpleMessageDetail = {
query: string
}
export type SimpleAccount = {
email: string
id: string
name: string
}
export type HumanInputFormDefinition = {
actions?: Array<UserActionConfig>
display_in_ui?: boolean
@ -2637,6 +2650,10 @@ export type WorkflowCommentBasicListWritable = {
data: Array<WorkflowCommentBasicWritable>
}
export type WorkflowCommentMentionUsersPayloadWritable = {
users: Array<AccountWithRoleResponseWritable>
}
export type WorkflowCommentDetailWritable = {
content: string
created_at?: number | null
@ -2716,6 +2733,21 @@ export type WorkflowCommentBasicWritable = {
updated_at?: number | null
}
export type AccountWithRoleResponseWritable = {
avatar?: string | null
created_at?: number | null
email: string
id: string
last_active_at?: number | null
last_login_at?: number | null
name: string
role: string
roles?: Array<{
[key: string]: string
}>
status: string
}
export type WorkflowCommentAccountWritable = {
email: string
id: string

View File

@ -1150,7 +1150,6 @@ export const zAgentThought = z.object({
created_at: z.int().nullish(),
files: z.array(z.string()),
id: z.string(),
message_chain_id: z.string().nullish(),
message_id: z.string(),
observation: z.string().nullish(),
position: z.int(),
@ -1319,9 +1318,9 @@ export const zUserSatisfactionRateStatisticResponse = z.object({
})
/**
* SimpleAccount
* SimpleAccountResponse
*/
export const zSimpleAccount = z.object({
export const zSimpleAccountResponse = z.object({
email: z.string(),
id: z.string(),
name: z.string(),
@ -1333,7 +1332,7 @@ export const zSimpleAccount = z.object({
export const zAdvancedChatWorkflowRunForListResponse = z.object({
conversation_id: z.string().nullish(),
created_at: z.int().nullish(),
created_by_account: zSimpleAccount.nullish(),
created_by_account: zSimpleAccountResponse.nullish(),
elapsed_time: z.number().nullish(),
exceptions_count: z.int().nullish(),
finished_at: z.int().nullish(),
@ -1355,72 +1354,12 @@ export const zAdvancedChatWorkflowRunPaginationResponse = z.object({
limit: z.int(),
})
/**
* ConversationAnnotation
*/
export const zConversationAnnotation = z.object({
account: zSimpleAccount.nullish(),
content: z.string(),
created_at: z.int().nullish(),
id: z.string(),
question: z.string().nullish(),
})
/**
* ConversationAnnotationHitHistory
*/
export const zConversationAnnotationHitHistory = z.object({
annotation_create_account: zSimpleAccount.nullish(),
created_at: z.int().nullish(),
id: z.string(),
})
/**
* Feedback
*/
export const zFeedback = z.object({
content: z.string().nullish(),
from_account: zSimpleAccount.nullish(),
from_end_user_id: z.string().nullish(),
from_source: z.string(),
rating: z.string(),
})
/**
* MessageDetail
*/
export const zMessageDetail = z.object({
agent_thoughts: z.array(zAgentThought),
annotation: zConversationAnnotation.nullish(),
annotation_hit_history: zConversationAnnotationHitHistory.nullish(),
answer_tokens: z.int(),
conversation_id: z.string(),
created_at: z.int().nullish(),
error: z.string().nullish(),
feedbacks: z.array(zFeedback),
from_account_id: z.string().nullish(),
from_end_user_id: z.string().nullish(),
from_source: z.string(),
id: z.string(),
inputs: z.record(z.string(), zJsonValue),
message: zJsonValue,
message_files: z.array(zMessageFile),
message_metadata_dict: zJsonValue,
message_tokens: z.int(),
parent_message_id: z.string().nullish(),
provider_response_latency: z.number(),
query: z.string(),
re_sign_file_url_answer: z.string(),
status: z.string(),
workflow_run_id: z.string().nullish(),
})
/**
* WorkflowRunForListResponse
*/
export const zWorkflowRunForListResponse = z.object({
created_at: z.int().nullish(),
created_by_account: zSimpleAccount.nullish(),
created_by_account: zSimpleAccountResponse.nullish(),
elapsed_time: z.number().nullish(),
exceptions_count: z.int().nullish(),
finished_at: z.int().nullish(),
@ -1456,7 +1395,7 @@ export const zSimpleEndUser = z.object({
*/
export const zWorkflowRunDetailResponse = z.object({
created_at: z.int().nullish(),
created_by_account: zSimpleAccount.nullish(),
created_by_account: zSimpleAccountResponse.nullish(),
created_by_end_user: zSimpleEndUser.nullish(),
created_by_role: z.string().nullish(),
elapsed_time: z.number().nullish(),
@ -1478,7 +1417,7 @@ export const zWorkflowRunDetailResponse = z.object({
*/
export const zWorkflowRunNodeExecutionResponse = z.object({
created_at: z.int().nullish(),
created_by_account: zSimpleAccount.nullish(),
created_by_account: zSimpleAccountResponse.nullish(),
created_by_end_user: zSimpleEndUser.nullish(),
created_by_role: z.string().nullish(),
elapsed_time: z.number().nullish(),
@ -1544,10 +1483,11 @@ export const zSandboxUploadResponse = z.object({
})
/**
* AccountWithRole
* AccountWithRoleResponse
*/
export const zAccountWithRole = z.object({
export const zAccountWithRoleResponse = z.object({
avatar: z.string().nullish(),
avatar_url: z.string().nullable(),
created_at: z.int().nullish(),
email: z.string(),
id: z.string(),
@ -1563,7 +1503,7 @@ export const zAccountWithRole = z.object({
* WorkflowCommentMentionUsersPayload
*/
export const zWorkflowCommentMentionUsersPayload = z.object({
users: z.array(zAccountWithRole),
users: z.array(zAccountWithRoleResponse),
})
/**
@ -1752,7 +1692,7 @@ export const zPipelineVariableResponse = z.object({
export const zWorkflowResponse = z.object({
conversation_variables: z.array(zWorkflowConversationVariableResponse),
created_at: z.int(),
created_by: zSimpleAccount.nullish(),
created_by: zSimpleAccountResponse.nullish(),
environment_variables: z.array(zWorkflowEnvironmentVariableResponse),
features: z.record(z.string(), z.unknown()),
graph: z.record(z.string(), z.unknown()),
@ -1763,7 +1703,7 @@ export const zWorkflowResponse = z.object({
rag_pipeline_variables: z.array(zPipelineVariableResponse),
tool_published: z.boolean(),
updated_at: z.int(),
updated_by: zSimpleAccount.nullish(),
updated_by: zSimpleAccountResponse.nullish(),
version: z.string(),
})
@ -2152,24 +2092,18 @@ export const zConversationDetail = z.object({
user_feedback_stats: zFeedbackStat.nullish(),
})
/**
* ConversationMessageDetail
*/
export const zConversationMessageDetail = z.object({
created_at: z.int().nullish(),
first_message: zMessageDetail.nullish(),
from_account_id: z.string().nullish(),
from_end_user_id: z.string().nullish(),
from_source: z.string(),
id: z.string(),
model_config: zModelConfig.nullish(),
status: z.string(),
})
/**
* Type
*/
export const zType = z.enum(['github', 'marketplace', 'package'])
export const zType = z.enum([
'app-selector',
'array[tools]',
'boolean',
'model-selector',
'secret-input',
'select',
'text-input',
])
/**
* Github
@ -2366,6 +2300,26 @@ export const zSimpleMessageDetail = z.object({
query: z.string(),
})
/**
* SimpleAccount
*/
export const zSimpleAccount = z.object({
email: z.string(),
id: z.string(),
name: z.string(),
})
/**
* ConversationAnnotation
*/
export const zConversationAnnotation = z.object({
account: zSimpleAccount.nullish(),
content: z.string(),
created_at: z.int().nullish(),
id: z.string(),
question: z.string().nullish(),
})
/**
* Conversation
*/
@ -2398,6 +2352,69 @@ export const zConversationPagination = z.object({
total: z.int(),
})
/**
* ConversationAnnotationHitHistory
*/
export const zConversationAnnotationHitHistory = z.object({
annotation_create_account: zSimpleAccount.nullish(),
annotation_id: z.string(),
created_at: z.int().nullish(),
})
/**
* Feedback
*/
export const zFeedback = z.object({
content: z.string().nullish(),
from_account: zSimpleAccount.nullish(),
from_end_user_id: z.string().nullish(),
from_source: z.string(),
rating: z.string(),
})
/**
* MessageDetail
*/
export const zMessageDetail = z.object({
agent_thoughts: z.array(zAgentThought),
annotation: zConversationAnnotation.nullish(),
annotation_hit_history: zConversationAnnotationHitHistory.nullish(),
answer_tokens: z.int(),
conversation_id: z.string(),
created_at: z.int().nullish(),
error: z.string().nullish(),
feedbacks: z.array(zFeedback),
from_account_id: z.string().nullish(),
from_end_user_id: z.string().nullish(),
from_source: z.string(),
id: z.string(),
inputs: z.record(z.string(), zJsonValue),
message: zJsonValue,
message_files: z.array(zMessageFile),
message_metadata_dict: zJsonValue,
message_tokens: z.int(),
parent_message_id: z.string().nullish(),
provider_response_latency: z.number(),
query: z.string(),
re_sign_file_url_answer: z.string(),
status: z.string(),
workflow_run_id: z.string().nullish(),
})
/**
* ConversationMessageDetail
*/
export const zConversationMessageDetail = z.object({
created_at: z.int().nullish(),
first_message: zMessageDetail.nullish(),
from_account_id: z.string().nullish(),
from_end_user_id: z.string().nullish(),
from_source: z.string(),
id: z.string(),
model_config: zModelConfig.nullish(),
status: z.string(),
})
/**
* ExecutionContentType
*/
@ -2425,7 +2442,7 @@ export const zWorkflowRunForLogResponse = z.object({
*/
export const zWorkflowAppLogPartialResponse = z.object({
created_at: z.int().nullish(),
created_by_account: zSimpleAccount.nullish(),
created_by_account: zSimpleAccountResponse.nullish(),
created_by_end_user: zSimpleEndUser.nullish(),
created_by_role: z.string().nullish(),
created_from: z.string().nullish(),
@ -2461,7 +2478,7 @@ export const zWorkflowRunForArchivedLogResponse = z.object({
*/
export const zWorkflowArchivedLogPartialResponse = z.object({
created_at: z.int().nullish(),
created_by_account: zSimpleAccount.nullish(),
created_by_account: zSimpleAccountResponse.nullish(),
created_by_end_user: zSimpleEndUser.nullish(),
id: z.string(),
trigger_metadata: z.unknown().optional(),
@ -3455,6 +3472,7 @@ export const zMessageDetailResponse = z.object({
agent_thoughts: z.array(zAgentThought).optional(),
annotation: zConversationAnnotation.nullish(),
annotation_hit_history: zConversationAnnotationHitHistory.nullish(),
answer: z.string(),
answer_tokens: z.int().nullish(),
conversation_id: z.string(),
created_at: z.int().nullish(),
@ -3468,12 +3486,11 @@ export const zMessageDetailResponse = z.object({
inputs: z.record(z.string(), zJsonValue),
message: zJsonValue.nullish(),
message_files: z.array(zMessageFile).optional(),
message_metadata_dict: zJsonValue.nullish(),
message_tokens: z.int().nullish(),
metadata: zJsonValue.nullish(),
parent_message_id: z.string().nullish(),
provider_response_latency: z.number().nullish(),
query: z.string(),
re_sign_file_url_answer: z.string(),
status: z.string(),
workflow_run_id: z.string().nullish(),
})
@ -3586,6 +3603,29 @@ export const zAppDetailWithSiteWritable = z.object({
workflow: zWorkflowPartial.nullish(),
})
/**
* AccountWithRoleResponse
*/
export const zAccountWithRoleResponseWritable = z.object({
avatar: z.string().nullish(),
created_at: z.int().nullish(),
email: z.string(),
id: z.string(),
last_active_at: z.int().nullish(),
last_login_at: z.int().nullish(),
name: z.string(),
role: z.string(),
roles: z.array(z.record(z.string(), z.string())).optional(),
status: z.string(),
})
/**
* WorkflowCommentMentionUsersPayload
*/
export const zWorkflowCommentMentionUsersPayloadWritable = z.object({
users: z.array(zAccountWithRoleResponseWritable),
})
/**
* WorkflowCommentAccount
*/

View File

@ -246,7 +246,6 @@ export type AgentThought = {
created_at?: number | null
files: Array<string>
id: string
message_chain_id?: string | null
message_id: string
observation?: string | null
position: number

View File

@ -266,7 +266,6 @@ export const zAgentThought = z.object({
created_at: z.int().nullish(),
files: z.array(z.string()),
id: z.string(),
message_chain_id: z.string().nullish(),
message_id: z.string(),
observation: z.string().nullish(),
position: z.int(),

View File

@ -119,7 +119,7 @@ export type SimpleResultResponse = {
export type WorkflowRunDetailResponse = {
created_at?: number | null
created_by_account?: SimpleAccount | null
created_by_account?: SimpleAccountResponse | null
created_by_end_user?: SimpleEndUser | null
created_by_role?: string | null
elapsed_time?: number | null
@ -158,7 +158,7 @@ export type DefaultBlockConfigResponse = {
export type WorkflowResponse = {
conversation_variables: Array<WorkflowConversationVariableResponse>
created_at: number
created_by?: SimpleAccount | null
created_by?: SimpleAccountResponse | null
environment_variables: Array<WorkflowEnvironmentVariableResponse>
features: {
[key: string]: unknown
@ -173,7 +173,7 @@ export type WorkflowResponse = {
rag_pipeline_variables: Array<PipelineVariableResponse>
tool_published: boolean
updated_at: number
updated_by?: SimpleAccount | null
updated_by?: SimpleAccountResponse | null
version: string
}
@ -221,7 +221,7 @@ export type DatasourceVariablesPayload = {
export type WorkflowRunNodeExecutionResponse = {
created_at?: number | null
created_by_account?: SimpleAccount | null
created_by_account?: SimpleAccountResponse | null
created_by_end_user?: SimpleEndUser | null
created_by_role?: string | null
elapsed_time?: number | null
@ -421,7 +421,7 @@ export type PluginDependency = {
export type WorkflowRunForListResponse = {
created_at?: number | null
created_by_account?: SimpleAccount | null
created_by_account?: SimpleAccountResponse | null
elapsed_time?: number | null
exceptions_count?: number | null
finished_at?: number | null
@ -433,7 +433,7 @@ export type WorkflowRunForListResponse = {
version?: string | null
}
export type SimpleAccount = {
export type SimpleAccountResponse = {
email: string
id: string
name: string
@ -515,7 +515,14 @@ export type DatasetWeightedScoreResponse = {
weight_type?: string | null
}
export type Type = 'github' | 'marketplace' | 'package'
export type Type
= | 'app-selector'
| 'array[tools]'
| 'boolean'
| 'model-selector'
| 'secret-input'
| 'select'
| 'text-input'
export type Github = {
github_plugin_unique_identifier: string

View File

@ -322,9 +322,9 @@ export const zPipelineTemplateListResponse = z.object({
})
/**
* SimpleAccount
* SimpleAccountResponse
*/
export const zSimpleAccount = z.object({
export const zSimpleAccountResponse = z.object({
email: z.string(),
id: z.string(),
name: z.string(),
@ -335,7 +335,7 @@ export const zSimpleAccount = z.object({
*/
export const zWorkflowRunForListResponse = z.object({
created_at: z.int().nullish(),
created_by_account: zSimpleAccount.nullish(),
created_by_account: zSimpleAccountResponse.nullish(),
elapsed_time: z.number().nullish(),
exceptions_count: z.int().nullish(),
finished_at: z.int().nullish(),
@ -371,7 +371,7 @@ export const zSimpleEndUser = z.object({
*/
export const zWorkflowRunDetailResponse = z.object({
created_at: z.int().nullish(),
created_by_account: zSimpleAccount.nullish(),
created_by_account: zSimpleAccountResponse.nullish(),
created_by_end_user: zSimpleEndUser.nullish(),
created_by_role: z.string().nullish(),
elapsed_time: z.number().nullish(),
@ -393,7 +393,7 @@ export const zWorkflowRunDetailResponse = z.object({
*/
export const zWorkflowRunNodeExecutionResponse = z.object({
created_at: z.int().nullish(),
created_by_account: zSimpleAccount.nullish(),
created_by_account: zSimpleAccountResponse.nullish(),
created_by_end_user: zSimpleEndUser.nullish(),
created_by_role: z.string().nullish(),
elapsed_time: z.number().nullish(),
@ -471,7 +471,7 @@ export const zPipelineVariableResponse = z.object({
export const zWorkflowResponse = z.object({
conversation_variables: z.array(zWorkflowConversationVariableResponse),
created_at: z.int(),
created_by: zSimpleAccount.nullish(),
created_by: zSimpleAccountResponse.nullish(),
environment_variables: z.array(zWorkflowEnvironmentVariableResponse),
features: z.record(z.string(), z.unknown()),
graph: z.record(z.string(), z.unknown()),
@ -482,7 +482,7 @@ export const zWorkflowResponse = z.object({
rag_pipeline_variables: z.array(zPipelineVariableResponse),
tool_published: z.boolean(),
updated_at: z.int(),
updated_by: zSimpleAccount.nullish(),
updated_by: zSimpleAccountResponse.nullish(),
version: z.string(),
})
@ -547,7 +547,15 @@ export const zDatasetRerankingModelResponse = z.object({
/**
* Type
*/
export const zType = z.enum(['github', 'marketplace', 'package'])
export const zType = z.enum([
'app-selector',
'array[tools]',
'boolean',
'model-selector',
'secret-input',
'select',
'text-input',
])
/**
* Github

View File

@ -16,7 +16,7 @@ export type SimpleResultResponse = {
export type WorkflowRunDetailResponse = {
created_at?: number | null
created_by_account?: SimpleAccount | null
created_by_account?: SimpleAccountResponse | null
created_by_end_user?: SimpleEndUser | null
created_by_role?: string | null
elapsed_time?: number | null
@ -51,7 +51,7 @@ export type DefaultBlockConfigsResponse = Array<{
export type SnippetWorkflowResponse = {
conversation_variables: Array<WorkflowConversationVariableResponse>
created_at: number
created_by?: SimpleAccount | null
created_by?: SimpleAccountResponse | null
environment_variables: Array<WorkflowEnvironmentVariableResponse>
features: {
[key: string]: unknown
@ -69,7 +69,7 @@ export type SnippetWorkflowResponse = {
rag_pipeline_variables: Array<PipelineVariableResponse>
tool_published: boolean
updated_at: number
updated_by?: SimpleAccount | null
updated_by?: SimpleAccountResponse | null
version: string
}
@ -120,7 +120,7 @@ export type SnippetLoopNodeRunPayload = {
export type WorkflowRunNodeExecutionResponse = {
created_at?: number | null
created_by_account?: SimpleAccount | null
created_by_account?: SimpleAccountResponse | null
created_by_end_user?: SimpleEndUser | null
created_by_role?: string | null
elapsed_time?: number | null
@ -210,7 +210,7 @@ export type WorkflowPublishResponse = {
export type WorkflowRunForListResponse = {
created_at?: number | null
created_by_account?: SimpleAccount | null
created_by_account?: SimpleAccountResponse | null
elapsed_time?: number | null
exceptions_count?: number | null
finished_at?: number | null
@ -222,7 +222,7 @@ export type WorkflowRunForListResponse = {
version?: string | null
}
export type SimpleAccount = {
export type SimpleAccountResponse = {
email: string
id: string
name: string

View File

@ -134,9 +134,9 @@ export const zWorkflowPublishResponse = z.object({
})
/**
* SimpleAccount
* SimpleAccountResponse
*/
export const zSimpleAccount = z.object({
export const zSimpleAccountResponse = z.object({
email: z.string(),
id: z.string(),
name: z.string(),
@ -147,7 +147,7 @@ export const zSimpleAccount = z.object({
*/
export const zWorkflowRunForListResponse = z.object({
created_at: z.int().nullish(),
created_by_account: zSimpleAccount.nullish(),
created_by_account: zSimpleAccountResponse.nullish(),
elapsed_time: z.number().nullish(),
exceptions_count: z.int().nullish(),
finished_at: z.int().nullish(),
@ -183,7 +183,7 @@ export const zSimpleEndUser = z.object({
*/
export const zWorkflowRunDetailResponse = z.object({
created_at: z.int().nullish(),
created_by_account: zSimpleAccount.nullish(),
created_by_account: zSimpleAccountResponse.nullish(),
created_by_end_user: zSimpleEndUser.nullish(),
created_by_role: z.string().nullish(),
elapsed_time: z.number().nullish(),
@ -205,7 +205,7 @@ export const zWorkflowRunDetailResponse = z.object({
*/
export const zWorkflowRunNodeExecutionResponse = z.object({
created_at: z.int().nullish(),
created_by_account: zSimpleAccount.nullish(),
created_by_account: zSimpleAccountResponse.nullish(),
created_by_end_user: zSimpleEndUser.nullish(),
created_by_role: z.string().nullish(),
elapsed_time: z.number().nullish(),
@ -283,7 +283,7 @@ export const zPipelineVariableResponse = z.object({
export const zSnippetWorkflowResponse = z.object({
conversation_variables: z.array(zWorkflowConversationVariableResponse),
created_at: z.int(),
created_by: zSimpleAccount.nullish(),
created_by: zSimpleAccountResponse.nullish(),
environment_variables: z.array(zWorkflowEnvironmentVariableResponse),
features: z.record(z.string(), z.unknown()),
graph: z.record(z.string(), z.unknown()),
@ -295,7 +295,7 @@ export const zSnippetWorkflowResponse = z.object({
rag_pipeline_variables: z.array(zPipelineVariableResponse),
tool_published: z.boolean(),
updated_at: z.int(),
updated_by: zSimpleAccount.nullish(),
updated_by: zSimpleAccountResponse.nullish(),
version: z.string(),
})

View File

@ -370,6 +370,7 @@ import {
zPostWorkspacesCurrentTriggerProviderBySubscriptionIdSubscriptionsUpdateResponse,
zPostWorkspacesCustomConfigBody,
zPostWorkspacesCustomConfigResponse,
zPostWorkspacesCustomConfigWebappLogoUploadBody,
zPostWorkspacesCustomConfigWebappLogoUploadResponse,
zPostWorkspacesInfoBody,
zPostWorkspacesInfoResponse,
@ -4121,6 +4122,7 @@ export const post72 = oc
successStatus: 201,
tags: ['console'],
})
.input(z.object({ body: zPostWorkspacesCustomConfigWebappLogoUploadBody }))
.output(zPostWorkspacesCustomConfigWebappLogoUploadResponse)
export const upload2 = {

View File

@ -104,8 +104,8 @@ export type SnippetUseCountResponse = {
use_count: number
}
export type AccountWithRoleList = {
accounts: Array<AccountWithRole>
export type AccountWithRoleListResponse = {
accounts: Array<AccountWithRoleResponse>
}
export type DefaultModelDataResponse = {
@ -128,7 +128,7 @@ export type EndpointCreatePayload = {
}
}
export type EndpointCreateResponse = {
export type SuccessResponse = {
success: boolean
}
@ -136,28 +136,8 @@ export type EndpointIdPayload = {
endpoint_id: string
}
export type EndpointDeleteResponse = {
success: boolean
}
export type EndpointDisableResponse = {
success: boolean
}
export type EndpointEnableResponse = {
success: boolean
}
export type EndpointListResponse = {
endpoints: Array<{
[key: string]: unknown
}>
}
export type PluginEndpointListResponse = {
endpoints: Array<{
[key: string]: unknown
}>
endpoints: Array<EndpointEntityWithInstance>
}
export type LegacyEndpointUpdatePayload = {
@ -168,10 +148,6 @@ export type LegacyEndpointUpdatePayload = {
}
}
export type EndpointUpdateResponse = {
success: boolean
}
export type EndpointUpdatePayload = {
name: string
settings: {
@ -211,9 +187,9 @@ export type SimpleResultDataResponse = {
result: string
}
export type MemberActionTenantResponse = {
export type MemberActionResponse = {
result: string
tenant_id: string
tenant_id?: string
}
export type OwnerTransferPayload = {
@ -391,10 +367,6 @@ export type ParserExcludePlugin = {
plugin_id: string
}
export type SuccessResponse = {
success: boolean
}
export type PluginAutoUpgradeFetchResponse = {
auto_upgrade: PluginAutoUpgradeSettingsResponseModel
category: PluginCategory
@ -836,7 +808,7 @@ export type WorkspaceCustomConfigPayload = {
replace_webapp_logo?: string | null
}
export type WorkspaceMutationResponse = {
export type WorkspaceTenantResultResponse = {
result: string
tenant: TenantInfoResponse
}
@ -862,6 +834,7 @@ export type TenantListItemResponse = {
created_at?: number | null
current: boolean
id: string
last_opened_at?: number | null
name?: string | null
plan?: string | null
status?: string | null
@ -921,8 +894,9 @@ export type AnonymousInlineModel7B8B49Ca164e = {
type?: string
}
export type AccountWithRole = {
export type AccountWithRoleResponse = {
avatar?: string | null
readonly avatar_url: string | null
created_at?: number | null
email: string
id: string
@ -948,6 +922,23 @@ export type Inner = {
provider?: string | null
}
export type EndpointEntityWithInstance = {
created_at: string
declaration?: EndpointProviderDeclaration
enabled: boolean
expired_at: string
hook_id: string
id: string
name: string
plugin_id: string
settings: {
[key: string]: unknown
}
tenant_id: string
updated_at: string
url: string
}
export type MemberInviteResultResponse = {
email: string
message?: string | null
@ -1209,6 +1200,11 @@ export type SimpleProviderEntityResponse = {
tenant_id: string
}
export type EndpointProviderDeclaration = {
endpoints?: Array<EndpointDeclaration> | null
settings?: Array<ProviderConfig>
}
export type ConfigurateMethod = 'customizable-model' | 'predefined-model'
export type CustomConfigurationResponse = {
@ -1419,6 +1415,26 @@ export type AiModelEntityResponse = {
pricing?: PriceConfigResponse | null
}
export type EndpointDeclaration = {
hidden?: boolean
method: string
path: string
}
export type ProviderConfig = {
default?: number | string | number | boolean | null
help?: I18nObject | null
label?: I18nObject | null
multiple?: boolean
name: string
options?: Array<Option> | null
placeholder?: I18nObject | null
required?: boolean
scope?: AppSelectorScope | ModelSelectorScope | ToolSelectorScope | null
type: Type
url?: string | null
}
export type UnaddedModelConfiguration = {
model: string
model_type: ModelType
@ -1490,6 +1506,33 @@ export type PriceConfigResponse = {
unit: string
}
export type Option = {
label: I18nObject
value: string
}
export type AppSelectorScope = 'all' | 'chat' | 'completion' | 'workflow'
export type ModelSelectorScope
= | 'llm'
| 'moderation'
| 'rerank'
| 'speech2text'
| 'text-embedding'
| 'tts'
| 'vision'
export type ToolSelectorScope = 'all' | 'builtin' | 'custom' | 'workflow'
export type Type
= | 'app-selector'
| 'array[tools]'
| 'boolean'
| 'model-selector'
| 'secret-input'
| 'select'
| 'text-input'
export type FormOption = {
label: GraphonModelRuntimeEntitiesCommonEntitiesI18nObject
show_on?: Array<FormShowOnObject>
@ -1511,6 +1554,25 @@ export type RestrictModel = {
model_type: ModelType
}
export type AccountWithRoleListResponseWritable = {
accounts: Array<AccountWithRoleResponseWritable>
}
export type AccountWithRoleResponseWritable = {
avatar?: string | null
created_at?: number | null
email: string
id: string
last_active_at?: number | null
last_login_at?: number | null
name: string
role: string
roles?: Array<{
[key: string]: string
}>
status: string
}
export type GetWorkspacesData = {
body?: never
path?: never
@ -1777,7 +1839,7 @@ export type GetWorkspacesCurrentDatasetOperatorsData = {
}
export type GetWorkspacesCurrentDatasetOperatorsResponses = {
200: AccountWithRoleList
200: AccountWithRoleListResponse
}
export type GetWorkspacesCurrentDatasetOperatorsResponse
@ -1825,7 +1887,7 @@ export type PostWorkspacesCurrentEndpointsErrors = {
}
export type PostWorkspacesCurrentEndpointsResponses = {
200: EndpointCreateResponse
200: SuccessResponse
}
export type PostWorkspacesCurrentEndpointsResponse
@ -1843,7 +1905,7 @@ export type PostWorkspacesCurrentEndpointsCreateErrors = {
}
export type PostWorkspacesCurrentEndpointsCreateResponses = {
200: EndpointCreateResponse
200: SuccessResponse
}
export type PostWorkspacesCurrentEndpointsCreateResponse
@ -1861,7 +1923,7 @@ export type PostWorkspacesCurrentEndpointsDeleteErrors = {
}
export type PostWorkspacesCurrentEndpointsDeleteResponses = {
200: EndpointDeleteResponse
200: SuccessResponse
}
export type PostWorkspacesCurrentEndpointsDeleteResponse
@ -1879,7 +1941,7 @@ export type PostWorkspacesCurrentEndpointsDisableErrors = {
}
export type PostWorkspacesCurrentEndpointsDisableResponses = {
200: EndpointDisableResponse
200: SuccessResponse
}
export type PostWorkspacesCurrentEndpointsDisableResponse
@ -1897,7 +1959,7 @@ export type PostWorkspacesCurrentEndpointsEnableErrors = {
}
export type PostWorkspacesCurrentEndpointsEnableResponses = {
200: EndpointEnableResponse
200: SuccessResponse
}
export type PostWorkspacesCurrentEndpointsEnableResponse
@ -1932,7 +1994,7 @@ export type GetWorkspacesCurrentEndpointsListPluginData = {
}
export type GetWorkspacesCurrentEndpointsListPluginResponses = {
200: PluginEndpointListResponse
200: EndpointListResponse
}
export type GetWorkspacesCurrentEndpointsListPluginResponse
@ -1950,7 +2012,7 @@ export type PostWorkspacesCurrentEndpointsUpdateErrors = {
}
export type PostWorkspacesCurrentEndpointsUpdateResponses = {
200: EndpointUpdateResponse
200: SuccessResponse
}
export type PostWorkspacesCurrentEndpointsUpdateResponse
@ -1970,7 +2032,7 @@ export type DeleteWorkspacesCurrentEndpointsByIdErrors = {
}
export type DeleteWorkspacesCurrentEndpointsByIdResponses = {
200: EndpointDeleteResponse
200: SuccessResponse
}
export type DeleteWorkspacesCurrentEndpointsByIdResponse
@ -1990,7 +2052,7 @@ export type PatchWorkspacesCurrentEndpointsByIdErrors = {
}
export type PatchWorkspacesCurrentEndpointsByIdResponses = {
200: EndpointUpdateResponse
200: SuccessResponse
}
export type PatchWorkspacesCurrentEndpointsByIdResponse
@ -2004,7 +2066,7 @@ export type GetWorkspacesCurrentMembersData = {
}
export type GetWorkspacesCurrentMembersResponses = {
200: AccountWithRoleList
200: AccountWithRoleListResponse
}
export type GetWorkspacesCurrentMembersResponse
@ -2062,7 +2124,7 @@ export type DeleteWorkspacesCurrentMembersByMemberIdData = {
}
export type DeleteWorkspacesCurrentMembersByMemberIdResponses = {
200: MemberActionTenantResponse
200: MemberActionResponse
}
export type DeleteWorkspacesCurrentMembersByMemberIdResponse
@ -4597,14 +4659,16 @@ export type PostWorkspacesCustomConfigData = {
}
export type PostWorkspacesCustomConfigResponses = {
200: WorkspaceMutationResponse
200: WorkspaceTenantResultResponse
}
export type PostWorkspacesCustomConfigResponse
= PostWorkspacesCustomConfigResponses[keyof PostWorkspacesCustomConfigResponses]
export type PostWorkspacesCustomConfigWebappLogoUploadData = {
body?: never
body: {
file: Blob | File
}
path?: never
query?: never
url: '/workspaces/custom-config/webapp-logo/upload'
@ -4625,7 +4689,7 @@ export type PostWorkspacesInfoData = {
}
export type PostWorkspacesInfoResponses = {
200: WorkspaceMutationResponse
200: WorkspaceTenantResultResponse
}
export type PostWorkspacesInfoResponse

View File

@ -66,9 +66,9 @@ export const zEndpointCreatePayload = z.object({
})
/**
* EndpointCreateResponse
* SuccessResponse
*/
export const zEndpointCreateResponse = z.object({
export const zSuccessResponse = z.object({
success: z.boolean(),
})
@ -79,41 +79,6 @@ export const zEndpointIdPayload = z.object({
endpoint_id: z.string(),
})
/**
* EndpointDeleteResponse
*/
export const zEndpointDeleteResponse = z.object({
success: z.boolean(),
})
/**
* EndpointDisableResponse
*/
export const zEndpointDisableResponse = z.object({
success: z.boolean(),
})
/**
* EndpointEnableResponse
*/
export const zEndpointEnableResponse = z.object({
success: z.boolean(),
})
/**
* EndpointListResponse
*/
export const zEndpointListResponse = z.object({
endpoints: z.array(z.record(z.string(), z.unknown())),
})
/**
* PluginEndpointListResponse
*/
export const zPluginEndpointListResponse = z.object({
endpoints: z.array(z.record(z.string(), z.unknown())),
})
/**
* LegacyEndpointUpdatePayload
*/
@ -123,13 +88,6 @@ export const zLegacyEndpointUpdatePayload = z.object({
settings: z.record(z.string(), z.unknown()),
})
/**
* EndpointUpdateResponse
*/
export const zEndpointUpdateResponse = z.object({
success: z.boolean(),
})
/**
* EndpointUpdatePayload
*/
@ -180,11 +138,11 @@ export const zSimpleResultDataResponse = z.object({
})
/**
* MemberActionTenantResponse
* MemberActionResponse
*/
export const zMemberActionTenantResponse = z.object({
export const zMemberActionResponse = z.object({
result: z.string(),
tenant_id: z.string(),
tenant_id: z.string().optional().default(''),
})
/**
@ -306,13 +264,6 @@ export const zPluginAutoUpgradeChangeResponse = z.object({
success: z.boolean(),
})
/**
* SuccessResponse
*/
export const zSuccessResponse = z.object({
success: z.boolean(),
})
/**
* PluginDebuggingKeyResponse
*/
@ -694,6 +645,7 @@ export const zTenantListItemResponse = z.object({
created_at: z.int().nullish(),
current: z.boolean(),
id: z.string(),
last_opened_at: z.int().nullish(),
name: z.string().nullish(),
plan: z.string().nullish(),
status: z.string().nullish(),
@ -733,9 +685,9 @@ export const zTenantInfoResponse = z.object({
})
/**
* WorkspaceMutationResponse
* WorkspaceTenantResultResponse
*/
export const zWorkspaceMutationResponse = z.object({
export const zWorkspaceTenantResultResponse = z.object({
result: z.string(),
tenant: zTenantInfoResponse,
})
@ -889,10 +841,11 @@ export const zSnippetPagination = z.object({
})
/**
* AccountWithRole
* AccountWithRoleResponse
*/
export const zAccountWithRole = z.object({
export const zAccountWithRoleResponse = z.object({
avatar: z.string().nullish(),
avatar_url: z.string().nullable(),
created_at: z.int().nullish(),
email: z.string(),
id: z.string(),
@ -905,10 +858,10 @@ export const zAccountWithRole = z.object({
})
/**
* AccountWithRoleList
* AccountWithRoleListResponse
*/
export const zAccountWithRoleList = z.object({
accounts: z.array(zAccountWithRole),
export const zAccountWithRoleListResponse = z.object({
accounts: z.array(zAccountWithRoleResponse),
})
/**
@ -1792,6 +1745,17 @@ export const zWorkflowToolUpdatePayload = z.object({
workflow_tool_id: z.string(),
})
/**
* EndpointDeclaration
*
* declaration of an endpoint
*/
export const zEndpointDeclaration = z.object({
hidden: z.boolean().optional().default(false),
method: z.string(),
path: z.string(),
})
/**
* UnaddedModelConfiguration
*
@ -1934,6 +1898,104 @@ export const zModelWithProviderListResponse = z.object({
data: z.array(zModelWithProviderEntityResponse),
})
/**
* Option
*/
export const zOption = z.object({
label: zI18nObject,
value: z.string(),
})
/**
* AppSelectorScope
*/
export const zAppSelectorScope = z.enum(['all', 'chat', 'completion', 'workflow'])
/**
* ModelSelectorScope
*/
export const zModelSelectorScope = z.enum([
'llm',
'moderation',
'rerank',
'speech2text',
'text-embedding',
'tts',
'vision',
])
/**
* ToolSelectorScope
*/
export const zToolSelectorScope = z.enum(['all', 'builtin', 'custom', 'workflow'])
/**
* Type
*/
export const zType = z.enum([
'app-selector',
'array[tools]',
'boolean',
'model-selector',
'secret-input',
'select',
'text-input',
])
/**
* ProviderConfig
*
* Model class for common provider settings like credentials
*/
export const zProviderConfig = z.object({
default: z.union([z.int(), z.string(), z.number(), z.boolean()]).nullish(),
help: zI18nObject.nullish(),
label: zI18nObject.nullish(),
multiple: z.boolean().optional().default(false),
name: z.string(),
options: z.array(zOption).nullish(),
placeholder: zI18nObject.nullish(),
required: z.boolean().optional().default(false),
scope: z.union([zAppSelectorScope, zModelSelectorScope, zToolSelectorScope]).nullish(),
type: zType,
url: z.string().nullish(),
})
/**
* EndpointProviderDeclaration
*
* declaration of an endpoint group
*/
export const zEndpointProviderDeclaration = z.object({
endpoints: z.array(zEndpointDeclaration).nullish(),
settings: z.array(zProviderConfig).optional(),
})
/**
* EndpointEntityWithInstance
*/
export const zEndpointEntityWithInstance = z.object({
created_at: z.iso.datetime(),
declaration: zEndpointProviderDeclaration.optional(),
enabled: z.boolean(),
expired_at: z.iso.datetime(),
hook_id: z.string(),
id: z.string(),
name: z.string(),
plugin_id: z.string(),
settings: z.record(z.string(), z.unknown()),
tenant_id: z.string(),
updated_at: z.iso.datetime(),
url: z.string(),
})
/**
* EndpointListResponse
*/
export const zEndpointListResponse = z.object({
endpoints: z.array(zEndpointEntityWithInstance),
})
/**
* FormShowOnObject
*
@ -2147,6 +2209,29 @@ export const zModelProviderListResponse = z.object({
data: z.array(zProviderResponse),
})
/**
* AccountWithRoleResponse
*/
export const zAccountWithRoleResponseWritable = z.object({
avatar: z.string().nullish(),
created_at: z.int().nullish(),
email: z.string(),
id: z.string(),
last_active_at: z.int().nullish(),
last_login_at: z.int().nullish(),
name: z.string(),
role: z.string(),
roles: z.array(z.record(z.string(), z.string())).optional(),
status: z.string(),
})
/**
* AccountWithRoleListResponse
*/
export const zAccountWithRoleListResponseWritable = z.object({
accounts: z.array(zAccountWithRoleResponseWritable),
})
/**
* Success
*/
@ -2274,7 +2359,7 @@ export const zPostWorkspacesCurrentCustomizedSnippetsBySnippetIdUseCountIncremen
/**
* Success
*/
export const zGetWorkspacesCurrentDatasetOperatorsResponse = zAccountWithRoleList
export const zGetWorkspacesCurrentDatasetOperatorsResponse = zAccountWithRoleListResponse
export const zGetWorkspacesCurrentDefaultModelQuery = z.object({
model_type: z.enum(['llm', 'moderation', 'rerank', 'speech2text', 'text-embedding', 'tts']),
@ -2297,35 +2382,35 @@ export const zPostWorkspacesCurrentEndpointsBody = zEndpointCreatePayload
/**
* Endpoint created successfully
*/
export const zPostWorkspacesCurrentEndpointsResponse = zEndpointCreateResponse
export const zPostWorkspacesCurrentEndpointsResponse = zSuccessResponse
export const zPostWorkspacesCurrentEndpointsCreateBody = zEndpointCreatePayload
/**
* Endpoint created successfully
*/
export const zPostWorkspacesCurrentEndpointsCreateResponse = zEndpointCreateResponse
export const zPostWorkspacesCurrentEndpointsCreateResponse = zSuccessResponse
export const zPostWorkspacesCurrentEndpointsDeleteBody = zEndpointIdPayload
/**
* Endpoint deleted successfully
*/
export const zPostWorkspacesCurrentEndpointsDeleteResponse = zEndpointDeleteResponse
export const zPostWorkspacesCurrentEndpointsDeleteResponse = zSuccessResponse
export const zPostWorkspacesCurrentEndpointsDisableBody = zEndpointIdPayload
/**
* Endpoint disabled successfully
*/
export const zPostWorkspacesCurrentEndpointsDisableResponse = zEndpointDisableResponse
export const zPostWorkspacesCurrentEndpointsDisableResponse = zSuccessResponse
export const zPostWorkspacesCurrentEndpointsEnableBody = zEndpointIdPayload
/**
* Endpoint enabled successfully
*/
export const zPostWorkspacesCurrentEndpointsEnableResponse = zEndpointEnableResponse
export const zPostWorkspacesCurrentEndpointsEnableResponse = zSuccessResponse
export const zGetWorkspacesCurrentEndpointsListQuery = z.object({
page: z.int().gte(1),
@ -2346,14 +2431,14 @@ export const zGetWorkspacesCurrentEndpointsListPluginQuery = z.object({
/**
* Success
*/
export const zGetWorkspacesCurrentEndpointsListPluginResponse = zPluginEndpointListResponse
export const zGetWorkspacesCurrentEndpointsListPluginResponse = zEndpointListResponse
export const zPostWorkspacesCurrentEndpointsUpdateBody = zLegacyEndpointUpdatePayload
/**
* Endpoint updated successfully
*/
export const zPostWorkspacesCurrentEndpointsUpdateResponse = zEndpointUpdateResponse
export const zPostWorkspacesCurrentEndpointsUpdateResponse = zSuccessResponse
export const zDeleteWorkspacesCurrentEndpointsByIdPath = z.object({
id: z.string(),
@ -2362,7 +2447,7 @@ export const zDeleteWorkspacesCurrentEndpointsByIdPath = z.object({
/**
* Endpoint deleted successfully
*/
export const zDeleteWorkspacesCurrentEndpointsByIdResponse = zEndpointDeleteResponse
export const zDeleteWorkspacesCurrentEndpointsByIdResponse = zSuccessResponse
export const zPatchWorkspacesCurrentEndpointsByIdBody = zEndpointUpdatePayload
@ -2373,12 +2458,12 @@ export const zPatchWorkspacesCurrentEndpointsByIdPath = z.object({
/**
* Endpoint updated successfully
*/
export const zPatchWorkspacesCurrentEndpointsByIdResponse = zEndpointUpdateResponse
export const zPatchWorkspacesCurrentEndpointsByIdResponse = zSuccessResponse
/**
* Success
*/
export const zGetWorkspacesCurrentMembersResponse = zAccountWithRoleList
export const zGetWorkspacesCurrentMembersResponse = zAccountWithRoleListResponse
export const zPostWorkspacesCurrentMembersInviteEmailBody = zMemberInvitePayload
@ -2410,7 +2495,7 @@ export const zDeleteWorkspacesCurrentMembersByMemberIdPath = z.object({
/**
* Success
*/
export const zDeleteWorkspacesCurrentMembersByMemberIdResponse = zMemberActionTenantResponse
export const zDeleteWorkspacesCurrentMembersByMemberIdResponse = zMemberActionResponse
export const zPostWorkspacesCurrentMembersByMemberIdOwnerTransferBody = zOwnerTransferPayload
@ -3952,7 +4037,11 @@ export const zPostWorkspacesCustomConfigBody = zWorkspaceCustomConfigPayload
/**
* Success
*/
export const zPostWorkspacesCustomConfigResponse = zWorkspaceMutationResponse
export const zPostWorkspacesCustomConfigResponse = zWorkspaceTenantResultResponse
export const zPostWorkspacesCustomConfigWebappLogoUploadBody = z.object({
file: z.custom<Blob | File>(),
})
/**
* Logo uploaded
@ -3964,7 +4053,7 @@ export const zPostWorkspacesInfoBody = zWorkspaceInfoPayload
/**
* Success
*/
export const zPostWorkspacesInfoResponse = zWorkspaceMutationResponse
export const zPostWorkspacesInfoResponse = zWorkspaceTenantResultResponse
export const zPostWorkspacesSwitchBody = zSwitchWorkspacePayload

View File

@ -1414,7 +1414,7 @@ export type SelectInputConfig = {
type?: 'select'
}
export type SimpleAccount = {
export type SimpleAccountResponse = {
email: string
id: string
name: string
@ -1572,7 +1572,7 @@ export type WorkflowAppLogPaginationResponse = {
export type WorkflowAppLogPartialResponse = {
created_at?: number | null
created_by_account?: SimpleAccount | null
created_by_account?: SimpleAccountResponse | null
created_by_end_user?: SimpleEndUser | null
created_by_role?: string | null
created_from?: string | null

View File

@ -1676,9 +1676,9 @@ export const zProcessRule = z.object({
})
/**
* SimpleAccount
* SimpleAccountResponse
*/
export const zSimpleAccount = z.object({
export const zSimpleAccountResponse = z.object({
email: z.string(),
id: z.string(),
name: z.string(),
@ -2196,7 +2196,7 @@ export const zWorkflowRunForLogResponse = z.object({
*/
export const zWorkflowAppLogPartialResponse = z.object({
created_at: z.int().nullish(),
created_by_account: zSimpleAccount.nullish(),
created_by_account: zSimpleAccountResponse.nullish(),
created_by_end_user: zSimpleEndUser.nullish(),
created_by_role: z.string().nullish(),
created_from: z.string().nullish(),

View File

@ -10,13 +10,21 @@ type SwaggerSchema = JsonObject & {
$ref?: string
}
type OpenApiMediaType = JsonObject & {
schema?: SwaggerSchema
}
type OpenApiResponse = JsonObject & {
content?: Record<string, OpenApiMediaType>
}
type OpenApiComponents = JsonObject & {
schemas?: Record<string, SwaggerSchema>
}
type SwaggerOperation = JsonObject & {
operationId?: string
responses?: Record<string, unknown>
responses?: Record<string, OpenApiResponse>
}
type SwaggerDocument = JsonObject & {
@ -52,6 +60,17 @@ const currentDir = path.dirname(fileURLToPath(import.meta.url))
const apiOpenApiDir = path.resolve(currentDir, 'openapi')
const operationMethods = new Set(['delete', 'get', 'patch', 'post', 'put'])
const pydanticDecimalStringPattern = '^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$'
const codegenSafeDecimalStringPattern = '^(?![-+.]*$)[+-]?0*\\d*\\.?\\d*$'
const opaqueJsonContent = (): Record<string, OpenApiMediaType> => ({
'application/json': {
schema: {
additionalProperties: true,
type: 'object',
},
},
})
const apiSpecs: ApiSpec[] = [
{ filename: 'console-openapi.json', name: 'console' },
@ -182,6 +201,46 @@ const addOperationIds = (document: SwaggerDocument) => {
}
}
const isOpaqueContractResponse = (response: OpenApiResponse) => {
const content = response.content
if (!isObject(content))
return false
return Object.entries(content).some(([mediaType, media]) => {
if (!isObject(media))
return false
return (mediaType === 'application/json' || mediaType === 'text/event-stream') && !('schema' in media)
})
}
const hasOpaqueContractSuccessResponse = (operation: SwaggerOperation) => {
return Object.entries(operation.responses ?? {}).some(([status, response]) => {
return /^2\d\d$/.test(status) && isObject(response) && isOpaqueContractResponse(response)
})
}
const normalizeOpaqueContractResponses = (document: SwaggerDocument) => {
// Some backend endpoints has no schema (e.g. external) and will trap heyapi here
// So we forge an opaque schema here
for (const pathItem of Object.values(document.paths ?? {})) {
for (const [method, operation] of Object.entries(pathItem)) {
if (!operationMethods.has(method) || !isObject(operation))
continue
const swaggerOperation = operation as SwaggerOperation
if (!hasOpaqueContractSuccessResponse(swaggerOperation))
continue
Object.values(swaggerOperation.responses ?? {})
.filter(response => isObject(response) && isOpaqueContractResponse(response))
.forEach((response) => {
response.content = opaqueJsonContent()
})
}
}
}
const hasSuccessResponse = (operation: SwaggerOperation) => {
return Object.entries(operation.responses ?? {}).some(([status, response]) => {
if (!/^2\d\d$/.test(status))
@ -215,6 +274,7 @@ const filterContractOperations = (document: SwaggerDocument) => {
}
const normalizeApiSwagger = (document: SwaggerDocument) => {
normalizeOpaqueContractResponses(document)
filterContractOperations(document)
addOperationIds(document)
@ -380,10 +440,20 @@ const createApiConfig = (job: ApiJob): UserConfig => ({
'name': 'zod',
'~resolvers': {
string: (ctx) => {
if (ctx.schema.format !== 'binary')
return undefined
if (ctx.schema.format === 'binary')
return $(ctx.symbols.z).attr('custom').call().generic($.type.or($.type('Blob'), $.type('File')))
return $(ctx.symbols.z).attr('custom').call().generic($.type.or($.type('Blob'), $.type('File')))
if (ctx.schema.pattern === pydanticDecimalStringPattern) {
// the pydantic generated regex will emit error like
// regexp/no-useless-assertions, so patch the regex here
return $(ctx.symbols.z)
.attr('string')
.call()
.attr('regex')
.call($.regexp(codegenSafeDecimalStringPattern))
}
return undefined
},
},
},

View File

@ -217,14 +217,8 @@ const toFeedback = (feedback: NonNullable<MessageDetailResponse['feedbacks']>[nu
}
}
type AgentDebugMessageWithLegacyAnswer = MessageDetailResponse & {
answer?: string | null
}
const getAgentDebugMessageAnswer = (message: MessageDetailResponse) => {
const legacyAnswer = (message as AgentDebugMessageWithLegacyAnswer).answer
return message.re_sign_file_url_answer ?? legacyAnswer ?? ''
return message.answer ?? ''
}
function getFormattedAgentDebugChatTree(messages: MessageDetailResponse[]): ChatItemInTree[] {