Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,5 @@ MAC_DEVELOPMENT_GUIDE.md
data/
sdk/benchmark/.env
/docker/.env.bak

.venv
62 changes: 43 additions & 19 deletions backend/apps/agent_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
from fastapi.encoders import jsonable_encoder
from starlette.responses import JSONResponse, Response

from consts.const import ASSET_OWNER_TENANT_ID
from consts.model import AgentRequest, AgentInfoRequest, AgentIDRequest, ConversationResponse, AgentImportRequest, AgentNameBatchCheckRequest, AgentNameBatchRegenerateRequest, VersionPublishRequest, VersionListResponse, VersionDetailResponse, VersionRollbackRequest, VersionStatusRequest, CurrentVersionResponse, VersionCompareRequest, VersionUpdateRequest
from consts.exceptions import SkillDuplicateError
from services.asset_owner_visibility import apply_agent_detail_prompt_visibility

from services.agent_service import (
get_agent_info_impl,
get_creating_sub_agent_info_impl,
Expand Down Expand Up @@ -94,7 +97,8 @@
user_id, auth_tenant_id = get_current_user_id(authorization)
# Use explicit tenant_id if provided, otherwise fall back to auth tenant_id
effective_tenant_id = tenant_id or auth_tenant_id
return await get_agent_info_impl(agent_id, effective_tenant_id, version_no, user_id)
agent_info = await get_agent_info_impl(agent_id, effective_tenant_id, version_no, user_id)
return apply_agent_detail_prompt_visibility(auth_tenant_id, agent_info)
except Exception as e:
logger.error(f"Agent search info error: {str(e)}")
raise HTTPException(
Expand Down Expand Up @@ -161,7 +165,8 @@
Delete an agent
"""
try:
user_id, auth_tenant_id, _ = get_current_user_info(authorization, http_request)
user_id, auth_tenant_id, _ = get_current_user_info(
authorization, http_request)
# Use explicit tenant_id if provided, otherwise fall back to auth tenant_id
effective_tenant_id = tenant_id or auth_tenant_id
await delete_agent_impl(request.agent_id, effective_tenant_id, user_id)
Expand Down Expand Up @@ -281,8 +286,8 @@

@agent_config_router.get("/list")
async def list_all_agent_info_api(
tenant_id: Optional[str] = Query(
None, description="Tenant ID for filtering (uses auth if not provided)"),

Check warning on line 290 in backend/apps/agent_app.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Introduce a new variable or use its initial value before reassigning 'tenant_id'.

See more on https://sonarcloud.io/project/issues?id=ModelEngine-Group_nexent&issues=AZ5tmoXZs3bl8eiEjKQl&open=AZ5tmoXZs3bl8eiEjKQl&pullRequest=3042
authorization: Optional[str] = Header(None),
request: Request = None
):
Expand All @@ -290,10 +295,18 @@
list all agent info
"""
try:
user_id, auth_tenant_id, _ = get_current_user_info(authorization, request)
# Use explicit tenant_id if provided, otherwise fall back to auth tenant_id
effective_tenant_id = tenant_id or auth_tenant_id
return await list_all_agent_info_impl(tenant_id=effective_tenant_id, user_id=user_id)
user_id, tenant_id, _ = get_current_user_info(
authorization, request)

agent_list = await list_all_agent_info_impl(
tenant_id=tenant_id, user_id=user_id
)
if tenant_id != ASSET_OWNER_TENANT_ID:
asset_agent_list = await list_all_agent_info_impl(
tenant_id=ASSET_OWNER_TENANT_ID, user_id=user_id
)
return agent_list + asset_agent_list
return agent_list
except Exception as e:
logger.error(f"Agent list error: {str(e)}")
raise HTTPException(
Expand Down Expand Up @@ -342,7 +355,8 @@
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
except Exception as e:
logger.error(f"Publish version error: {str(e)}")
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Publish version error.")
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Publish version error.")


@agent_config_router.post("/{agent_id}/versions/compare")
Expand All @@ -367,7 +381,8 @@
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
except Exception as e:
logger.error(f"Compare versions error: {str(e)}")
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Compare versions error.")
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Compare versions error.")


@agent_config_router.get("/{agent_id}/versions", response_model=VersionListResponse)
Expand All @@ -378,14 +393,14 @@
authorization: Optional[str] = Header(None),
request: Request = None
):
"""
"""versions = session.query(AgentVersion)
Get version list for an agent
"""
try:
user_id, auth_tenant_id, _ = get_current_user_info(authorization, request)
_, auth_tenant_id, _ = get_current_user_info(
authorization, request)
# Use explicit tenant_id if provided, otherwise fall back to auth tenant_id
effective_tenant_id = tenant_id or auth_tenant_id
logger.info(f"Get version list for agent_id: {agent_id}, tenant_id: {effective_tenant_id}")
result = get_version_list_impl(
agent_id=agent_id,
tenant_id=effective_tenant_id,
Expand All @@ -394,7 +409,8 @@
return JSONResponse(status_code=HTTPStatus.OK, content=jsonable_encoder(result))
except Exception as e:
logger.error(f"Get version list error: {str(e)}")
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Get version list error.")
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Get version list error.")


@agent_config_router.get("/{agent_id}/versions/{version_no}", response_model=VersionDetailResponse)
Expand All @@ -418,7 +434,9 @@
raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail=str(e))
except Exception as e:
logger.error(f"Get version detail error: {str(e)}")
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Get version detail error.")
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Get version detail error.")


@agent_config_router.get("/{agent_id}/versions/{version_no}/detail", response_model=VersionDetailResponse)
async def get_version_detail_api(
Expand All @@ -441,7 +459,8 @@
raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail=str(e))
except Exception as e:
logger.error(f"Get version detail error: {str(e)}")
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Get version detail error.")
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Get version detail error.")


@agent_config_router.post("/{agent_id}/versions/{version_no}/rollback")
Expand All @@ -468,7 +487,8 @@
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
except Exception as e:
logger.error(f"Rollback version error: {str(e)}")
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Rollback version error.")
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Rollback version error.")


@agent_config_router.patch("/{agent_id}/versions/{version_no}/status")
Expand All @@ -495,7 +515,8 @@
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
except Exception as e:
logger.error(f"Update version status error: {str(e)}")
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Update version status error.")
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
detail="Update version status error.")


@agent_config_router.put("/{agent_id}/versions/{version_no}")
Expand Down Expand Up @@ -523,7 +544,8 @@
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
except Exception as e:
logger.error(f"Update version error: {str(e)}")
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Update version error.")
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Update version error.")


@agent_config_router.delete("/{agent_id}/versions/{version_no}")
Expand All @@ -548,7 +570,8 @@
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
except Exception as e:
logger.error(f"Delete version error: {str(e)}")
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Delete version error.")
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Delete version error.")


@agent_config_router.get("/{agent_id}/current_version", response_model=CurrentVersionResponse)
Expand All @@ -570,7 +593,8 @@
raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail=str(e))
except Exception as e:
logger.error(f"Get current version error: {str(e)}")
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Get current version error.")
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Get current version error.")


@agent_config_router.get("/published_list")
Expand Down
29 changes: 13 additions & 16 deletions backend/apps/file_management_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
from consts.model import ProcessParams
from services.file_management_service import upload_to_minio, upload_files_impl, \
get_file_url_impl, get_file_stream_impl, delete_file_impl, list_files_impl, \
resolve_preview_file, get_preview_stream, check_file_access, check_file_access_batch
resolve_preview_file, get_preview_stream, check_file_access, check_file_access_batch, \
resolve_minio_upload_folder
from utils.auth_utils import get_current_user_id
from utils.file_management_utils import trigger_data_process

Expand Down Expand Up @@ -101,7 +102,9 @@ async def upload_files(
detail="No files in the request")

user_id, tenant_id = get_current_user_id(authorization)
errors, uploaded_file_paths, uploaded_filenames = await upload_files_impl(destination, file, folder, index_name, user_id)
errors, uploaded_file_paths, uploaded_filenames = await upload_files_impl(
destination, file, folder, index_name, user_id, uploader_tenant_id=tenant_id
)

if uploaded_file_paths:
return JSONResponse(
Expand Down Expand Up @@ -201,7 +204,7 @@ async def get_storage_file(
try:
user_id, tenant_id = get_current_user_id(authorization)

if not check_file_access(object_name, user_id):
if not check_file_access(object_name, user_id, tenant_id):
logger.warning(f"[get_storage_file] Access denied: object_name={object_name}, user_id={user_id}")
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
Expand Down Expand Up @@ -284,15 +287,8 @@ async def storage_upload_files(
try:
user_id, tenant_id = get_current_user_id(authorization)

if folder == "knowledge_base":
actual_folder = "knowledge_base"
else:
if user_id:
actual_folder = f"attachments/{user_id}"
else:
actual_folder = folder or "attachments"

results = await upload_to_minio(files=files, folder=actual_folder, user_id=user_id)
actual_folder = resolve_minio_upload_folder(folder, user_id, tenant_id)
results = await upload_to_minio(files=files, folder=actual_folder)

return {
"message": f"Processed {len(results)} files",
Expand Down Expand Up @@ -346,7 +342,7 @@ async def get_storage_files(
if user_id:
filtered_files = [
f for f in files
if f.get("key") and check_file_access(f.get("key"), user_id)
if f.get("key") and check_file_access(f.get("key"), user_id, tenant_id)
]
else:
filtered_files = [
Expand Down Expand Up @@ -594,7 +590,7 @@ async def remove_storage_file(
try:
user_id, tenant_id = get_current_user_id(authorization)

if not check_file_access(object_name, user_id):
if not check_file_access(object_name, user_id, tenant_id):
logger.warning(f"[remove_storage_file] Access denied: object_name={object_name}, user_id={user_id}")
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
Expand Down Expand Up @@ -645,7 +641,7 @@ async def get_storage_file_batch_urls(
results = []

for object_name in object_names:
if not check_file_access(object_name, user_id):
if not check_file_access(object_name, user_id, tenant_id):
results.append({
"object_name": object_name,
"success": False,
Expand Down Expand Up @@ -695,6 +691,7 @@ async def preview_file(
Access control:
- knowledge_base/*: All authenticated users can access
- attachments/{user_id}/*: Only the owner (user_id) can access
- attachments/asset_owner/{user_id}/*: ASSET_OWNER virtual tenant and owner only

- **object_name**: File object name in storage
- **filename**: Original filename for Content-Disposition header (optional)
Expand All @@ -705,7 +702,7 @@ async def preview_file(
try:
user_id, tenant_id = get_current_user_id(authorization)

if not check_file_access(object_name, user_id):
if not check_file_access(object_name, user_id, tenant_id):
logger.warning(f"[preview_file] Access denied: object_name={object_name}, user_id={user_id}")
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
Expand Down
12 changes: 12 additions & 0 deletions backend/apps/invitation_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ async def list_invitations_endpoint(
status_code=HTTPStatus.UNAUTHORIZED,
detail=str(exc)
)
except ValidationError as exc:
logger.warning(f"Invitation list rejected by feature flag: {str(exc)}")
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail=str(exc)
)
except Exception as exc:
logger.error(f"Unexpected error retrieving invitation list: {str(exc)}")
raise HTTPException(
Expand Down Expand Up @@ -131,6 +137,12 @@ async def create_invitation_endpoint(
status_code=HTTPStatus.BAD_REQUEST,
detail=str(exc)
)
except ValidationError as exc:
logger.warning(f"Invitation creation rejected by feature flag: {str(exc)}")
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail=str(exc)
)
except DuplicateError as exc:
logger.warning(f"Duplicate invitation code: {str(exc)}")
raise HTTPException(
Expand Down
1 change: 1 addition & 0 deletions backend/apps/model_managment_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ async def get_model_list(authorization: Optional[str] = Header(None)):
Returns each model enriched with repo-qualified `model_name` and a normalized
`connect_status` value.
"""

try:
user_id, tenant_id = get_current_user_id(authorization)
logger.debug(
Expand Down
2 changes: 2 additions & 0 deletions backend/apps/northbound_base_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from apps.app_factory import create_app
from .northbound_app import router as northbound_router
from .northbound_knowledge_app import router as northbound_knowledge_router


class A2AServerSettings(BaseModel):
Expand Down Expand Up @@ -49,6 +50,7 @@ class A2AServerSettings(BaseModel):
)

northbound_app.include_router(northbound_router)
northbound_app.include_router(northbound_knowledge_router)


# =============================================================================
Expand Down
Loading
Loading