This commit is contained in:
yangqiyong@vip.qq.com 2025-03-12 14:10:34 +08:00
commit 9e38123239
16 changed files with 55 additions and 64 deletions

View File

@ -325,8 +325,8 @@ class DatasetInitApi(Resource):
@cloud_edition_billing_resource_check("vector_space") @cloud_edition_billing_resource_check("vector_space")
@cloud_edition_billing_rate_limit_check("knowledge") @cloud_edition_billing_rate_limit_check("knowledge")
def post(self): def post(self):
# The role of the current user in the ta table must be admin, owner, or editor # The role of the current user in the ta table must be admin, owner, dataset_operator, or editor
if not current_user.is_editor: if not current_user.is_dataset_editor:
raise Forbidden() raise Forbidden()
parser = reqparse.RequestParser() parser = reqparse.RequestParser()
@ -704,8 +704,8 @@ class DocumentProcessingApi(DocumentResource):
document_id = str(document_id) document_id = str(document_id)
document = self.get_document(dataset_id, document_id) document = self.get_document(dataset_id, document_id)
# The role of the current user in the ta table must be admin, owner, or editor # The role of the current user in the ta table must be admin, owner, dataset_operator, or editor
if not current_user.is_editor: if not current_user.is_dataset_editor:
raise Forbidden() raise Forbidden()
if action == "pause": if action == "pause":
@ -769,8 +769,8 @@ class DocumentMetadataApi(DocumentResource):
doc_type = req_data.get("doc_type") doc_type = req_data.get("doc_type")
doc_metadata = req_data.get("doc_metadata") doc_metadata = req_data.get("doc_metadata")
# The role of the current user in the ta table must be admin, owner, or editor # The role of the current user in the ta table must be admin, owner, dataset_operator, or editor
if not current_user.is_editor: if not current_user.is_dataset_editor:
raise Forbidden() raise Forbidden()
if doc_type is None or doc_metadata is None: if doc_type is None or doc_metadata is None:

View File

@ -123,8 +123,8 @@ class DatasetDocumentSegmentListApi(Resource):
raise NotFound("Document not found.") raise NotFound("Document not found.")
segment_ids = request.args.getlist("segment_id") segment_ids = request.args.getlist("segment_id")
# The role of the current user in the ta table must be admin or owner # The role of the current user in the ta table must be admin, owner, dataset_operator, or editor
if not current_user.is_editor: if not current_user.is_dataset_editor:
raise Forbidden() raise Forbidden()
try: try:
DatasetService.check_dataset_permission(dataset, current_user) DatasetService.check_dataset_permission(dataset, current_user)
@ -151,8 +151,8 @@ class DatasetDocumentSegmentApi(Resource):
raise NotFound("Document not found.") raise NotFound("Document not found.")
# check user's model setting # check user's model setting
DatasetService.check_dataset_model_setting(dataset) DatasetService.check_dataset_model_setting(dataset)
# The role of the current user in the ta table must be admin, owner, or editor # The role of the current user in the ta table must be admin, owner, dataset_operator, or editor
if not current_user.is_editor: if not current_user.is_dataset_editor:
raise Forbidden() raise Forbidden()
try: try:
@ -206,7 +206,7 @@ class DatasetDocumentSegmentAddApi(Resource):
document = DocumentService.get_document(dataset_id, document_id) document = DocumentService.get_document(dataset_id, document_id)
if not document: if not document:
raise NotFound("Document not found.") raise NotFound("Document not found.")
if not current_user.is_editor: if not current_user.is_dataset_editor:
raise Forbidden() raise Forbidden()
# check embedding model setting # check embedding model setting
if dataset.indexing_technique == "high_quality": if dataset.indexing_technique == "high_quality":
@ -281,8 +281,8 @@ class DatasetDocumentSegmentUpdateApi(Resource):
).first() ).first()
if not segment: if not segment:
raise NotFound("Segment not found.") raise NotFound("Segment not found.")
# The role of the current user in the ta table must be admin, owner, or editor # The role of the current user in the ta table must be admin, owner, dataset_operator, or editor
if not current_user.is_editor: if not current_user.is_dataset_editor:
raise Forbidden() raise Forbidden()
try: try:
DatasetService.check_dataset_permission(dataset, current_user) DatasetService.check_dataset_permission(dataset, current_user)
@ -325,8 +325,8 @@ class DatasetDocumentSegmentUpdateApi(Resource):
).first() ).first()
if not segment: if not segment:
raise NotFound("Segment not found.") raise NotFound("Segment not found.")
# The role of the current user in the ta table must be admin or owner # The role of the current user in the ta table must be admin, owner, dataset_operator, or editor
if not current_user.is_editor: if not current_user.is_dataset_editor:
raise Forbidden() raise Forbidden()
try: try:
DatasetService.check_dataset_permission(dataset, current_user) DatasetService.check_dataset_permission(dataset, current_user)
@ -428,7 +428,7 @@ class ChildChunkAddApi(Resource):
).first() ).first()
if not segment: if not segment:
raise NotFound("Segment not found.") raise NotFound("Segment not found.")
if not current_user.is_editor: if not current_user.is_dataset_editor:
raise Forbidden() raise Forbidden()
# check embedding model setting # check embedding model setting
if dataset.indexing_technique == "high_quality": if dataset.indexing_technique == "high_quality":
@ -528,8 +528,8 @@ class ChildChunkAddApi(Resource):
).first() ).first()
if not segment: if not segment:
raise NotFound("Segment not found.") raise NotFound("Segment not found.")
# The role of the current user in the ta table must be admin, owner, or editor # The role of the current user in the ta table must be admin, owner, dataset_operator, or editor
if not current_user.is_editor: if not current_user.is_dataset_editor:
raise Forbidden() raise Forbidden()
try: try:
DatasetService.check_dataset_permission(dataset, current_user) DatasetService.check_dataset_permission(dataset, current_user)
@ -579,8 +579,8 @@ class ChildChunkUpdateApi(Resource):
).first() ).first()
if not child_chunk: if not child_chunk:
raise NotFound("Child chunk not found.") raise NotFound("Child chunk not found.")
# The role of the current user in the ta table must be admin or owner # The role of the current user in the ta table must be admin, owner, dataset_operator, or editor
if not current_user.is_editor: if not current_user.is_dataset_editor:
raise Forbidden() raise Forbidden()
try: try:
DatasetService.check_dataset_permission(dataset, current_user) DatasetService.check_dataset_permission(dataset, current_user)
@ -624,8 +624,8 @@ class ChildChunkUpdateApi(Resource):
).first() ).first()
if not child_chunk: if not child_chunk:
raise NotFound("Child chunk not found.") raise NotFound("Child chunk not found.")
# The role of the current user in the ta table must be admin or owner # The role of the current user in the ta table must be admin, owner, dataset_operator, or editor
if not current_user.is_editor: if not current_user.is_dataset_editor:
raise Forbidden() raise Forbidden()
try: try:
DatasetService.check_dataset_permission(dataset, current_user) DatasetService.check_dataset_permission(dataset, current_user)

View File

@ -10,7 +10,7 @@ from controllers.service_api.app.error import NotChatAppError
from controllers.service_api.wraps import FetchUserArg, WhereisUserArg, validate_app_token from controllers.service_api.wraps import FetchUserArg, WhereisUserArg, validate_app_token
from core.app.entities.app_invoke_entities import InvokeFrom from core.app.entities.app_invoke_entities import InvokeFrom
from fields.conversation_fields import message_file_fields from fields.conversation_fields import message_file_fields
from fields.message_fields import feedback_fields, retriever_resource_fields from fields.message_fields import agent_thought_fields, feedback_fields, retriever_resource_fields
from fields.raws import FilesContainedField from fields.raws import FilesContainedField
from libs.helper import TimestampField, uuid_value from libs.helper import TimestampField, uuid_value
from models.model import App, AppMode, EndUser from models.model import App, AppMode, EndUser
@ -19,20 +19,6 @@ from services.message_service import MessageService
class MessageListApi(Resource): class MessageListApi(Resource):
agent_thought_fields = {
"id": fields.String,
"chain_id": fields.String,
"message_id": fields.String,
"position": fields.Integer,
"thought": fields.String,
"tool": fields.String,
"tool_labels": fields.Raw,
"tool_input": fields.String,
"created_at": TimestampField,
"observation": fields.String,
"message_files": fields.List(fields.Nested(message_file_fields)),
}
message_fields = { message_fields = {
"id": fields.String, "id": fields.String,
"conversation_id": fields.String, "conversation_id": fields.String,

View File

@ -5,7 +5,6 @@ from .account import (
InvitationCode, InvitationCode,
Tenant, Tenant,
TenantAccountJoin, TenantAccountJoin,
TenantAccountJoinRole,
TenantAccountRole, TenantAccountRole,
TenantStatus, TenantStatus,
) )
@ -156,7 +155,6 @@ __all__ = [
"TagBinding", "TagBinding",
"Tenant", "Tenant",
"TenantAccountJoin", "TenantAccountJoin",
"TenantAccountJoinRole",
"TenantAccountRole", "TenantAccountRole",
"TenantDefaultModel", "TenantDefaultModel",
"TenantPreferredModelProvider", "TenantPreferredModelProvider",

View File

@ -220,13 +220,6 @@ class Tenant(db.Model): # type: ignore[name-defined]
self.custom_config = json.dumps(value) self.custom_config = json.dumps(value)
class TenantAccountJoinRole(enum.Enum):
OWNER = "owner"
ADMIN = "admin"
NORMAL = "normal"
DATASET_OPERATOR = "dataset_operator"
class TenantAccountJoin(db.Model): # type: ignore[name-defined] class TenantAccountJoin(db.Model): # type: ignore[name-defined]
__tablename__ = "tenant_account_joins" __tablename__ = "tenant_account_joins"
__table_args__ = ( __table_args__ = (

View File

@ -28,7 +28,6 @@ from models.account import (
AccountStatus, AccountStatus,
Tenant, Tenant,
TenantAccountJoin, TenantAccountJoin,
TenantAccountJoinRole,
TenantAccountRole, TenantAccountRole,
TenantStatus, TenantStatus,
) )
@ -625,8 +624,8 @@ class TenantService:
@staticmethod @staticmethod
def create_tenant_member(tenant: Tenant, account: Account, role: str = "normal") -> TenantAccountJoin: def create_tenant_member(tenant: Tenant, account: Account, role: str = "normal") -> TenantAccountJoin:
"""Create tenant member""" """Create tenant member"""
if role == TenantAccountJoinRole.OWNER.value: if role == TenantAccountRole.OWNER.value:
if TenantService.has_roles(tenant, [TenantAccountJoinRole.OWNER]): if TenantService.has_roles(tenant, [TenantAccountRole.OWNER]):
logging.error(f"Tenant {tenant.id} has already an owner.") logging.error(f"Tenant {tenant.id} has already an owner.")
raise Exception("Tenant already has an owner.") raise Exception("Tenant already has an owner.")
@ -734,10 +733,10 @@ class TenantService:
return updated_accounts return updated_accounts
@staticmethod @staticmethod
def has_roles(tenant: Tenant, roles: list[TenantAccountJoinRole]) -> bool: def has_roles(tenant: Tenant, roles: list[TenantAccountRole]) -> bool:
"""Check if user has any of the given roles for a tenant""" """Check if user has any of the given roles for a tenant"""
if not all(isinstance(role, TenantAccountJoinRole) for role in roles): if not all(isinstance(role, TenantAccountRole) for role in roles):
raise ValueError("all roles must be TenantAccountJoinRole") raise ValueError("all roles must be TenantAccountRole")
return ( return (
db.session.query(TenantAccountJoin) db.session.query(TenantAccountJoin)
@ -749,7 +748,7 @@ class TenantService:
) )
@staticmethod @staticmethod
def get_user_role(account: Account, tenant: Tenant) -> Optional[TenantAccountJoinRole]: def get_user_role(account: Account, tenant: Tenant) -> Optional[TenantAccountRole]:
"""Get the role of the current account for a given tenant""" """Get the role of the current account for a given tenant"""
join = ( join = (
db.session.query(TenantAccountJoin) db.session.query(TenantAccountJoin)

View File

@ -2,7 +2,7 @@ from flask_login import current_user # type: ignore
from configs import dify_config from configs import dify_config
from extensions.ext_database import db from extensions.ext_database import db
from models.account import Tenant, TenantAccountJoin, TenantAccountJoinRole from models.account import Tenant, TenantAccountJoin, TenantAccountRole
from services.account_service import TenantService from services.account_service import TenantService
from services.feature_service import FeatureService from services.feature_service import FeatureService
@ -33,9 +33,7 @@ class WorkspaceService:
can_replace_logo = FeatureService.get_features(tenant_info["id"]).can_replace_logo can_replace_logo = FeatureService.get_features(tenant_info["id"]).can_replace_logo
if can_replace_logo and TenantService.has_roles( if can_replace_logo and TenantService.has_roles(tenant, [TenantAccountRole.OWNER, TenantAccountRole.ADMIN]):
tenant, [TenantAccountJoinRole.OWNER, TenantAccountJoinRole.ADMIN]
):
base_url = dify_config.FILES_URL base_url = dify_config.FILES_URL
replace_webapp_logo = ( replace_webapp_logo = (
f"{base_url}/files/workspaces/{tenant.id}/webapp-logo" f"{base_url}/files/workspaces/{tenant.id}/webapp-logo"

View File

@ -723,6 +723,9 @@ SSRF_PROXY_HTTPS_URL=http://ssrf_proxy:3128
# Maximum loop count in the workflow # Maximum loop count in the workflow
LOOP_NODE_MAX_COUNT=100 LOOP_NODE_MAX_COUNT=100
# The maximum number of tools that can be used in the agent.
MAX_TOOLS_NUM=10
# ------------------------------ # ------------------------------
# Environment Variables for web Service # Environment Variables for web Service
# ------------------------------ # ------------------------------

View File

@ -68,6 +68,7 @@ services:
INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: ${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH:-} INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: ${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH:-}
PM2_INSTANCES: ${PM2_INSTANCES:-2} PM2_INSTANCES: ${PM2_INSTANCES:-2}
LOOP_NODE_MAX_COUNT: ${LOOP_NODE_MAX_COUNT:-100} LOOP_NODE_MAX_COUNT: ${LOOP_NODE_MAX_COUNT:-100}
MAX_TOOLS_NUM: ${MAX_TOOLS_NUM:-10}
# The postgres database. # The postgres database.
db: db:

View File

@ -311,6 +311,7 @@ x-shared-env: &shared-api-worker-env
SSRF_PROXY_HTTP_URL: ${SSRF_PROXY_HTTP_URL:-http://ssrf_proxy:3128} SSRF_PROXY_HTTP_URL: ${SSRF_PROXY_HTTP_URL:-http://ssrf_proxy:3128}
SSRF_PROXY_HTTPS_URL: ${SSRF_PROXY_HTTPS_URL:-http://ssrf_proxy:3128} SSRF_PROXY_HTTPS_URL: ${SSRF_PROXY_HTTPS_URL:-http://ssrf_proxy:3128}
LOOP_NODE_MAX_COUNT: ${LOOP_NODE_MAX_COUNT:-100} LOOP_NODE_MAX_COUNT: ${LOOP_NODE_MAX_COUNT:-100}
MAX_TOOLS_NUM: ${MAX_TOOLS_NUM:-10}
TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000} TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000}
PGUSER: ${PGUSER:-${DB_USERNAME}} PGUSER: ${PGUSER:-${DB_USERNAME}}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-${DB_PASSWORD}} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-${DB_PASSWORD}}
@ -486,6 +487,7 @@ services:
INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: ${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH:-} INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: ${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH:-}
PM2_INSTANCES: ${PM2_INSTANCES:-2} PM2_INSTANCES: ${PM2_INSTANCES:-2}
LOOP_NODE_MAX_COUNT: ${LOOP_NODE_MAX_COUNT:-100} LOOP_NODE_MAX_COUNT: ${LOOP_NODE_MAX_COUNT:-100}
MAX_TOOLS_NUM: ${MAX_TOOLS_NUM:-10}
# The postgres database. # The postgres database.
db: db:

View File

@ -40,3 +40,6 @@ NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH=4000
# Maximum loop count in the workflow # Maximum loop count in the workflow
NEXT_PUBLIC_LOOP_NODE_MAX_COUNT=100 NEXT_PUBLIC_LOOP_NODE_MAX_COUNT=100
# Maximum number of tools in the agent/workflow
NEXT_PUBLIC_MAX_TOOLS_NUM=10

View File

@ -641,7 +641,7 @@ Chat applications support session persistence, allowing previous chat history to
"tool_input": "{\"dalle2\": {\"prompt\": \"cat\"}}", "tool_input": "{\"dalle2\": {\"prompt\": \"cat\"}}",
"created_at": 1705988186, "created_at": 1705988186,
"observation": "image has been created and sent to user already, you should tell user to check it now.", "observation": "image has been created and sent to user already, you should tell user to check it now.",
"message_files": [ "files": [
"976990d2-5294-47e6-8f14-7356ba9d2d76" "976990d2-5294-47e6-8f14-7356ba9d2d76"
] ]
}, },
@ -655,7 +655,7 @@ Chat applications support session persistence, allowing previous chat history to
"tool_input": "", "tool_input": "",
"created_at": 1705988199, "created_at": 1705988199,
"observation": "", "observation": "",
"message_files": [] "files": []
} }
] ]
} }

View File

@ -641,7 +641,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
"tool_input": "{\"dalle2\": {\"prompt\": \"cat\"}}", "tool_input": "{\"dalle2\": {\"prompt\": \"cat\"}}",
"created_at": 1705988186, "created_at": 1705988186,
"observation": "画像はすでに作成され、ユーザーに送信されました。今すぐユーザーに確認するように伝えてください。", "observation": "画像はすでに作成され、ユーザーに送信されました。今すぐユーザーに確認するように伝えてください。",
"message_files": [ "files": [
"976990d2-5294-47e6-8f14-7356ba9d2d76" "976990d2-5294-47e6-8f14-7356ba9d2d76"
] ]
}, },
@ -655,7 +655,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
"tool_input": "", "tool_input": "",
"created_at": 1705988199, "created_at": 1705988199,
"observation": "", "observation": "",
"message_files": [] "files": []
} }
] ]
} }

View File

@ -657,7 +657,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
"tool_input": "{\"dalle2\": {\"prompt\": \"cat\"}}", "tool_input": "{\"dalle2\": {\"prompt\": \"cat\"}}",
"created_at": 1705988186, "created_at": 1705988186,
"observation": "image has been created and sent to user already, you should tell user to check it now.", "observation": "image has been created and sent to user already, you should tell user to check it now.",
"message_files": [ "files": [
"976990d2-5294-47e6-8f14-7356ba9d2d76" "976990d2-5294-47e6-8f14-7356ba9d2d76"
] ]
}, },
@ -671,7 +671,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
"tool_input": "", "tool_input": "",
"created_at": 1705988199, "created_at": 1705988199,
"observation": "", "observation": "",
"message_files": [] "files": []
} }
] ]
} }

View File

@ -47,6 +47,7 @@ const LocaleLayout = ({
data-public-maintenance-notice={process.env.NEXT_PUBLIC_MAINTENANCE_NOTICE} data-public-maintenance-notice={process.env.NEXT_PUBLIC_MAINTENANCE_NOTICE}
data-public-site-about={process.env.NEXT_PUBLIC_SITE_ABOUT} data-public-site-about={process.env.NEXT_PUBLIC_SITE_ABOUT}
data-public-text-generation-timeout-ms={process.env.NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS} data-public-text-generation-timeout-ms={process.env.NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS}
data-public-max-tools-num={process.env.NEXT_PUBLIC_MAX_TOOLS_NUM}
data-public-top-k-max-value={process.env.NEXT_PUBLIC_TOP_K_MAX_VALUE} data-public-top-k-max-value={process.env.NEXT_PUBLIC_TOP_K_MAX_VALUE}
data-public-indexing-max-segmentation-tokens-length={process.env.NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH} data-public-indexing-max-segmentation-tokens-length={process.env.NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH}
data-public-loop-node-max-count={process.env.NEXT_PUBLIC_LOOP_NODE_MAX_COUNT} data-public-loop-node-max-count={process.env.NEXT_PUBLIC_LOOP_NODE_MAX_COUNT}

View File

@ -162,7 +162,14 @@ export const ANNOTATION_DEFAULT = {
score_threshold: 0.9, score_threshold: 0.9,
} }
export const MAX_TOOLS_NUM = 10 export let maxToolsNum = 10
if (process.env.NEXT_PUBLIC_MAX_TOOLS_NUM && process.env.NEXT_PUBLIC_MAX_TOOLS_NUM !== '')
maxToolsNum = Number.parseInt(process.env.NEXT_PUBLIC_MAX_TOOLS_NUM)
else if (globalThis.document?.body?.getAttribute('data-public-max-tools-num') && globalThis.document.body.getAttribute('data-public-max-tools-num') !== '')
maxToolsNum = Number.parseInt(globalThis.document.body.getAttribute('data-public-max-tools-num') as string)
export const MAX_TOOLS_NUM = maxToolsNum
export const DEFAULT_AGENT_SETTING = { export const DEFAULT_AGENT_SETTING = {
enabled: false, enabled: false,