mirror of
https://github.com/langgenius/dify.git
synced 2026-08-24 12:32:54 -04:00
239 lines
8.3 KiB
Python
239 lines
8.3 KiB
Python
"""Inner API for Agent Soul-backed config assets.
|
|
|
|
These endpoints are called by the dify-agent server with the inner API key.
|
|
They resolve the requested Agent config version directly from Agent Soul JSON
|
|
and authorize downloads with the existing Config target, tenant, and source
|
|
ownership semantics. Download requests return metadata plus a short-lived,
|
|
origin-free ``/files/*`` URI, never file bytes; the Sandbox fetches those bytes
|
|
directly from the Dify API data plane.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Literal
|
|
|
|
from flask import request
|
|
from flask_restx import Resource
|
|
from pydantic import BaseModel, ConfigDict, ValidationError, model_validator
|
|
|
|
from controllers.console.wraps import setup_required
|
|
from controllers.inner_api import inner_api_ns
|
|
from controllers.inner_api.wraps import plugin_inner_api_only
|
|
from models.agent_config_entities import validate_config_name, validate_config_skill_name
|
|
from services.agent_config_service import (
|
|
AgentConfigService,
|
|
AgentConfigServiceError,
|
|
AgentConfigVersionKind,
|
|
ConfigPushPayload,
|
|
)
|
|
|
|
|
|
class _ConfigTargetQuery(BaseModel):
|
|
tenant_id: str
|
|
user_id: str | None = None
|
|
config_version_id: str
|
|
config_version_kind: AgentConfigVersionKind
|
|
|
|
|
|
class _ConfigMutationRequest(BaseModel):
|
|
tenant_id: str
|
|
user_id: str
|
|
config_version_id: str
|
|
config_version_kind: AgentConfigVersionKind
|
|
|
|
|
|
class _ConfigDownloadSource(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
kind: Literal["file", "skill"]
|
|
name: str
|
|
|
|
@model_validator(mode="after")
|
|
def validate_name(self) -> _ConfigDownloadSource:
|
|
self.name = validate_config_skill_name(self.name) if self.kind == "skill" else validate_config_name(self.name)
|
|
return self
|
|
|
|
|
|
class _ConfigDownloadRequest(_ConfigTargetQuery):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
config: _ConfigDownloadSource
|
|
|
|
|
|
class _ConfigPushRequest(_ConfigMutationRequest):
|
|
files: list[dict] = []
|
|
skills: list[dict] = []
|
|
env_text: str | None = None
|
|
note: str | None = None
|
|
|
|
def to_payload(self) -> ConfigPushPayload:
|
|
return ConfigPushPayload.model_validate(
|
|
{
|
|
"files": self.files,
|
|
"skills": self.skills,
|
|
"env_text": self.env_text,
|
|
"note": self.note,
|
|
}
|
|
)
|
|
|
|
|
|
class _ConfigEnvUpdateRequest(_ConfigMutationRequest):
|
|
env_text: str
|
|
|
|
|
|
class _ConfigNoteUpdateRequest(_ConfigMutationRequest):
|
|
note: str
|
|
|
|
|
|
def _target_query_from_request() -> _ConfigTargetQuery:
|
|
return _ConfigTargetQuery.model_validate(
|
|
{
|
|
"tenant_id": request.args.get("tenant_id"),
|
|
"user_id": request.args.get("user_id"),
|
|
"config_version_id": request.args.get("config_version_id"),
|
|
"config_version_kind": request.args.get("config_version_kind"),
|
|
}
|
|
)
|
|
|
|
|
|
def _error_response(exc: AgentConfigServiceError) -> tuple[dict[str, str], int]:
|
|
return {"code": exc.code, "message": exc.message}, exc.status_code
|
|
|
|
|
|
@inner_api_ns.route("/agent-config/<string:agent_id>/manifest")
|
|
class AgentConfigManifestApi(Resource):
|
|
@setup_required
|
|
@plugin_inner_api_only
|
|
@inner_api_ns.doc("agent_config_manifest")
|
|
def get(self, agent_id: str):
|
|
try:
|
|
query = _target_query_from_request()
|
|
return AgentConfigService().manifest(
|
|
tenant_id=query.tenant_id,
|
|
agent_id=agent_id,
|
|
user_id=query.user_id,
|
|
config_version_id=query.config_version_id,
|
|
config_version_kind=query.config_version_kind,
|
|
)
|
|
except ValidationError as exc:
|
|
return {"code": "invalid_request", "message": str(exc)}, 400
|
|
except AgentConfigServiceError as exc:
|
|
return _error_response(exc)
|
|
|
|
|
|
@inner_api_ns.route("/agent-config/<string:agent_id>/download-request")
|
|
class AgentConfigDownloadRequestApi(Resource):
|
|
@setup_required
|
|
@plugin_inner_api_only
|
|
@inner_api_ns.doc("agent_config_download_request")
|
|
def post(self, agent_id: str):
|
|
try:
|
|
body = _ConfigDownloadRequest.model_validate(request.get_json(silent=True) or {})
|
|
result = AgentConfigService().request_download(
|
|
tenant_id=body.tenant_id,
|
|
agent_id=agent_id,
|
|
user_id=body.user_id,
|
|
config_version_id=body.config_version_id,
|
|
config_version_kind=body.config_version_kind,
|
|
kind=body.config.kind,
|
|
name=body.config.name,
|
|
)
|
|
return {
|
|
"filename": result.filename,
|
|
"mime_type": result.mime_type,
|
|
"size": result.size,
|
|
"download_uri": result.download_uri,
|
|
}
|
|
except ValidationError as exc:
|
|
return {"code": "invalid_request", "message": str(exc)}, 400
|
|
except AgentConfigServiceError as exc:
|
|
return _error_response(exc)
|
|
|
|
|
|
@inner_api_ns.route("/agent-config/<string:agent_id>/skills/<string:name>/inspect")
|
|
class AgentConfigSkillInspectApi(Resource):
|
|
@setup_required
|
|
@plugin_inner_api_only
|
|
@inner_api_ns.doc("agent_config_skill_inspect")
|
|
def get(self, agent_id: str, name: str):
|
|
try:
|
|
query = _target_query_from_request()
|
|
return AgentConfigService().inspect_skill(
|
|
tenant_id=query.tenant_id,
|
|
agent_id=agent_id,
|
|
user_id=query.user_id,
|
|
config_version_id=query.config_version_id,
|
|
config_version_kind=query.config_version_kind,
|
|
name=name,
|
|
)
|
|
except ValidationError as exc:
|
|
return {"code": "invalid_request", "message": str(exc)}, 400
|
|
except AgentConfigServiceError as exc:
|
|
return _error_response(exc)
|
|
|
|
|
|
@inner_api_ns.route("/agent-config/<string:agent_id>/push")
|
|
class AgentConfigPushApi(Resource):
|
|
@setup_required
|
|
@plugin_inner_api_only
|
|
@inner_api_ns.doc("agent_config_push")
|
|
def post(self, agent_id: str):
|
|
try:
|
|
body = _ConfigPushRequest.model_validate(request.get_json(silent=True) or {})
|
|
return AgentConfigService().push(
|
|
tenant_id=body.tenant_id,
|
|
agent_id=agent_id,
|
|
user_id=body.user_id,
|
|
config_version_id=body.config_version_id,
|
|
config_version_kind=body.config_version_kind,
|
|
payload=body.to_payload(),
|
|
)
|
|
except ValidationError as exc:
|
|
return {"code": "invalid_request", "message": str(exc)}, 400
|
|
except AgentConfigServiceError as exc:
|
|
return _error_response(exc)
|
|
|
|
|
|
@inner_api_ns.route("/agent-config/<string:agent_id>/env")
|
|
class AgentConfigEnvApi(Resource):
|
|
@setup_required
|
|
@plugin_inner_api_only
|
|
@inner_api_ns.doc("agent_config_env")
|
|
def patch(self, agent_id: str):
|
|
try:
|
|
body = _ConfigEnvUpdateRequest.model_validate(request.get_json(silent=True) or {})
|
|
return AgentConfigService().update_env(
|
|
tenant_id=body.tenant_id,
|
|
agent_id=agent_id,
|
|
user_id=body.user_id,
|
|
config_version_id=body.config_version_id,
|
|
config_version_kind=body.config_version_kind,
|
|
env_text=body.env_text,
|
|
)
|
|
except ValidationError as exc:
|
|
return {"code": "invalid_request", "message": str(exc)}, 400
|
|
except AgentConfigServiceError as exc:
|
|
return _error_response(exc)
|
|
|
|
|
|
@inner_api_ns.route("/agent-config/<string:agent_id>/note")
|
|
class AgentConfigNoteApi(Resource):
|
|
@setup_required
|
|
@plugin_inner_api_only
|
|
@inner_api_ns.doc("agent_config_note")
|
|
def put(self, agent_id: str):
|
|
try:
|
|
body = _ConfigNoteUpdateRequest.model_validate(request.get_json(silent=True) or {})
|
|
return AgentConfigService().update_note(
|
|
tenant_id=body.tenant_id,
|
|
agent_id=agent_id,
|
|
user_id=body.user_id,
|
|
config_version_id=body.config_version_id,
|
|
config_version_kind=body.config_version_kind,
|
|
note=body.note,
|
|
)
|
|
except ValidationError as exc:
|
|
return {"code": "invalid_request", "message": str(exc)}, 400
|
|
except AgentConfigServiceError as exc:
|
|
return _error_response(exc)
|