mirror of
https://github.com/langgenius/dify.git
synced 2026-08-24 12:32:54 -04:00
208 lines
7.1 KiB
Python
208 lines
7.1 KiB
Python
"""Agent-owned inner endpoints for CLI file URL allocation."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Literal
|
|
|
|
from flask_restx import Resource
|
|
from pydantic import BaseModel, ConfigDict, Field, ValidationError
|
|
from sqlalchemy.orm import Session
|
|
|
|
from configs import dify_config
|
|
from controllers.common.schema import register_response_schema_models, register_schema_models
|
|
from controllers.common.session import with_session
|
|
from controllers.console.wraps import setup_required
|
|
from controllers.inner_api import inner_api_ns
|
|
from controllers.inner_api.plugin.wraps import get_user
|
|
from controllers.inner_api.wraps import plugin_inner_api_only
|
|
from core.plugin.entities.request import RequestDownloadFileMapping, RequestRequestUploadFile
|
|
from core.tools.signature import bind_file_uri, get_signed_file_uri_for_plugin
|
|
from fields.base import ResponseModel
|
|
from libs.exception import BaseHTTPException
|
|
from services.account_service import TenantService
|
|
from services.file_request_service import FileRequestService
|
|
|
|
|
|
class AgentFileRequestHttpError(BaseHTTPException):
|
|
error_code = "agent_file_request_failed"
|
|
description = "Agent file request failed."
|
|
code = 500
|
|
|
|
def __init__(self, *, error_code: str, description: str, status_code: int) -> None:
|
|
self.error_code = error_code
|
|
self.description = description
|
|
self.code = status_code
|
|
super().__init__(description)
|
|
|
|
|
|
class AgentFileUploadRequestPayload(RequestRequestUploadFile):
|
|
tenant_id: str
|
|
user_id: str
|
|
user_from: Literal["account", "end-user"] | None = None
|
|
max_size: int = Field(ge=0, description="Maximum upload size in bytes")
|
|
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
|
|
class AgentFileDownloadRequestPayload(BaseModel):
|
|
tenant_id: str
|
|
user_id: str
|
|
user_from: Literal["account", "end-user"]
|
|
invoke_from: Literal[
|
|
"service-api",
|
|
"openapi",
|
|
"web-app",
|
|
"trigger",
|
|
"explore",
|
|
"debugger",
|
|
"published",
|
|
"validation",
|
|
]
|
|
file: RequestDownloadFileMapping
|
|
for_frontend: bool = True
|
|
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
|
|
class AgentFileUploadRequestResponse(ResponseModel):
|
|
upload_uri: str
|
|
|
|
|
|
class AgentFileDownloadRequestResponse(ResponseModel):
|
|
filename: str
|
|
mime_type: str | None = None
|
|
size: int
|
|
download_uri: str
|
|
|
|
|
|
register_schema_models(inner_api_ns, AgentFileUploadRequestPayload, AgentFileDownloadRequestPayload)
|
|
register_response_schema_models(
|
|
inner_api_ns,
|
|
AgentFileUploadRequestResponse,
|
|
AgentFileDownloadRequestResponse,
|
|
)
|
|
|
|
|
|
@inner_api_ns.route("/agent/files/upload-request")
|
|
class AgentFileUploadRequestApi(Resource):
|
|
"""Allocate an origin-free signed upload URI for the Agent CLI."""
|
|
|
|
@setup_required
|
|
@plugin_inner_api_only
|
|
@inner_api_ns.doc("inner_agent_file_upload_request")
|
|
@inner_api_ns.expect(inner_api_ns.models[AgentFileUploadRequestPayload.__name__])
|
|
@inner_api_ns.response(
|
|
200,
|
|
"Upload URI allocated",
|
|
inner_api_ns.models[AgentFileUploadRequestResponse.__name__],
|
|
)
|
|
@with_session(write=False)
|
|
def post(self, session: Session) -> dict[str, object]:
|
|
try:
|
|
payload = AgentFileUploadRequestPayload.model_validate(inner_api_ns.payload or {})
|
|
except ValidationError as exc:
|
|
raise AgentFileRequestHttpError(
|
|
error_code="invalid_request",
|
|
description=str(exc),
|
|
status_code=400,
|
|
) from exc
|
|
|
|
tenant = TenantService.get_tenant_by_id(payload.tenant_id, session=session)
|
|
if tenant is None:
|
|
raise AgentFileRequestHttpError(
|
|
error_code="tenant_not_found",
|
|
description="tenant not found",
|
|
status_code=404,
|
|
)
|
|
try:
|
|
if payload.user_from == "account":
|
|
if not TenantService.account_belongs_to_tenant(payload.user_id, tenant.id, session=session):
|
|
raise ValueError("account not found")
|
|
owner_id = payload.user_id
|
|
else:
|
|
owner_id = get_user(tenant.id, payload.user_id).id
|
|
upload_uri = get_signed_file_uri_for_plugin(
|
|
filename=payload.filename,
|
|
mimetype=payload.mimetype,
|
|
tenant_id=tenant.id,
|
|
user_id=owner_id,
|
|
conversation_id=payload.conversation_id,
|
|
user_from=payload.user_from,
|
|
max_size=payload.max_size,
|
|
)
|
|
except ValueError as exc:
|
|
raise AgentFileRequestHttpError(
|
|
error_code="user_not_found",
|
|
description=str(exc),
|
|
status_code=404,
|
|
) from exc
|
|
|
|
return AgentFileUploadRequestResponse(upload_uri=upload_uri).model_dump(mode="json")
|
|
|
|
|
|
@inner_api_ns.route("/agent/files/download-request")
|
|
class AgentFileDownloadRequestApi(Resource):
|
|
"""Allocate a transfer URI or frontend URL for one Agent CLI file."""
|
|
|
|
@setup_required
|
|
@plugin_inner_api_only
|
|
@inner_api_ns.doc("inner_agent_file_download_request")
|
|
@inner_api_ns.expect(inner_api_ns.models[AgentFileDownloadRequestPayload.__name__])
|
|
@inner_api_ns.response(
|
|
200,
|
|
"Download URI allocated",
|
|
inner_api_ns.models[AgentFileDownloadRequestResponse.__name__],
|
|
)
|
|
@with_session(write=False)
|
|
def post(self, session: Session) -> dict[str, object]:
|
|
try:
|
|
payload = AgentFileDownloadRequestPayload.model_validate(inner_api_ns.payload or {})
|
|
except ValidationError as exc:
|
|
raise AgentFileRequestHttpError(
|
|
error_code="invalid_request",
|
|
description=str(exc),
|
|
status_code=400,
|
|
) from exc
|
|
|
|
if TenantService.get_tenant_by_id(payload.tenant_id, session=session) is None:
|
|
raise AgentFileRequestHttpError(
|
|
error_code="tenant_not_found",
|
|
description="tenant not found",
|
|
status_code=404,
|
|
)
|
|
try:
|
|
result = FileRequestService().request_download(
|
|
tenant_id=payload.tenant_id,
|
|
user_id=payload.user_id,
|
|
user_from=payload.user_from,
|
|
invoke_from=payload.invoke_from,
|
|
file_mapping=payload.file.model_dump(mode="python", exclude_none=True),
|
|
)
|
|
except ValueError as exc:
|
|
raise AgentFileRequestHttpError(
|
|
error_code="file_not_accessible",
|
|
description=str(exc),
|
|
status_code=404,
|
|
) from exc
|
|
|
|
download_uri = result.download_uri
|
|
if payload.for_frontend:
|
|
download_uri = bind_file_uri(download_uri, dify_config.FILES_URL)
|
|
|
|
return AgentFileDownloadRequestResponse(
|
|
filename=result.filename,
|
|
mime_type=result.mime_type,
|
|
size=result.size,
|
|
download_uri=download_uri,
|
|
).model_dump(mode="json")
|
|
|
|
|
|
__all__ = [
|
|
"AgentFileDownloadRequestApi",
|
|
"AgentFileDownloadRequestPayload",
|
|
"AgentFileDownloadRequestResponse",
|
|
"AgentFileUploadRequestApi",
|
|
"AgentFileUploadRequestPayload",
|
|
"AgentFileUploadRequestResponse",
|
|
]
|