mirror of
https://github.com/langgenius/dify.git
synced 2026-07-14 17:07:03 +08:00
152 lines
6.3 KiB
Python
152 lines
6.3 KiB
Python
"""Service layer for Agent-backend core tool invocations.
|
|
|
|
The `dify.core.tools` layer executes inside the API service boundary so Dify
|
|
Agent can expose API-owned tool providers (`plugin`, `builtin`, `api`,
|
|
`workflow`, and `mcp`) without learning their storage, credential, or runtime
|
|
internals. Production workflow and agent-app request builders still keep
|
|
existing plugin tool configs on the direct `dify.plugin.tools` route by
|
|
default; this service is the lower-level API execution path used when a caller
|
|
explicitly submits a `dify.core.tools` declaration. The service validates
|
|
tenant/app ownership, reuses `ToolManager.get_agent_tool_runtime(...)` to build
|
|
the runtime with `ToolInvokeFrom.AGENT`, invokes through
|
|
`ToolEngine.generic_invoke(...)`, and formats observations through the shared
|
|
public `ToolEngine.tool_response_to_str` helper so Agent and workflow paths
|
|
stay consistent.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
from core.agent.entities import AgentToolEntity
|
|
from core.app.entities.app_invoke_entities import InvokeFrom
|
|
from core.callback_handler.workflow_tool_callback_handler import DifyWorkflowCallbackHandler
|
|
from core.tools.entities.tool_entities import ToolProviderType
|
|
from core.tools.errors import (
|
|
ToolInvokeError,
|
|
ToolNotFoundError,
|
|
ToolNotSupportedError,
|
|
ToolParameterValidationError,
|
|
ToolProviderCredentialValidationError,
|
|
ToolProviderNotFoundError,
|
|
)
|
|
from core.tools.tool_engine import ToolEngine
|
|
from core.tools.tool_manager import ToolManager
|
|
from core.tools.utils.message_transformer import ToolFileMessageTransformer
|
|
from models.model import App
|
|
from services.entities.agent_tool_inner import AgentToolInvokeRequest, AgentToolInvokeResponse
|
|
from services.errors.agent_tool_inner import AgentToolInnerServiceError
|
|
|
|
|
|
class AgentToolInnerService:
|
|
"""Invoke one API-owned Agent tool declaration, including explicit plugin-via-core calls."""
|
|
|
|
def invoke(self, request: AgentToolInvokeRequest, *, session: Session) -> AgentToolInvokeResponse:
|
|
app = session.get(App, request.caller.app_id)
|
|
if app is None:
|
|
raise AgentToolInnerServiceError(
|
|
error_code="app_not_found",
|
|
description="App not found.",
|
|
status_code=404,
|
|
)
|
|
if app.tenant_id != request.caller.tenant_id:
|
|
raise AgentToolInnerServiceError(
|
|
error_code="app_tenant_mismatch",
|
|
description="App does not belong to the caller tenant.",
|
|
status_code=403,
|
|
)
|
|
|
|
agent_tool = AgentToolEntity(
|
|
provider_type=ToolProviderType.value_of(request.tool.provider_type),
|
|
provider_id=request.tool.provider_id,
|
|
tool_name=request.tool.tool_name,
|
|
tool_parameters=dict(request.tool.runtime_parameters),
|
|
credential_id=request.tool.credential_id,
|
|
)
|
|
try:
|
|
tool_runtime = ToolManager.get_agent_tool_runtime(
|
|
tenant_id=request.caller.tenant_id,
|
|
app_id=request.caller.app_id,
|
|
agent_tool=agent_tool,
|
|
user_id=request.caller.user_id,
|
|
invoke_from=InvokeFrom.value_of(request.caller.invoke_from),
|
|
variable_pool=None,
|
|
allow_file_parameters=True,
|
|
use_default_for_missing_form_parameters=True,
|
|
)
|
|
messages = ToolEngine.generic_invoke(
|
|
session=session,
|
|
tool=tool_runtime,
|
|
tool_parameters=dict(request.tool.tool_parameters),
|
|
user_id=request.caller.user_id,
|
|
workflow_tool_callback=DifyWorkflowCallbackHandler(),
|
|
workflow_call_depth=0,
|
|
conversation_id=request.caller.conversation_id,
|
|
app_id=request.caller.app_id,
|
|
)
|
|
transformed_messages = list(
|
|
ToolFileMessageTransformer.transform_tool_invoke_messages(
|
|
messages=messages,
|
|
user_id=request.caller.user_id,
|
|
tenant_id=request.caller.tenant_id,
|
|
conversation_id=request.caller.conversation_id,
|
|
)
|
|
)
|
|
except ToolProviderNotFoundError as exc:
|
|
raise AgentToolInnerServiceError(
|
|
error_code="agent_tool_declaration_not_found",
|
|
description=str(exc),
|
|
status_code=404,
|
|
) from exc
|
|
except ToolProviderCredentialValidationError as exc:
|
|
raise AgentToolInnerServiceError(
|
|
error_code="agent_tool_credential_invalid",
|
|
description=str(exc),
|
|
status_code=422,
|
|
) from exc
|
|
except ToolParameterValidationError as exc:
|
|
raise AgentToolInnerServiceError(
|
|
error_code="tool_parameters_invalid",
|
|
description=str(exc),
|
|
status_code=422,
|
|
) from exc
|
|
except (ToolInvokeError, ToolNotFoundError, ToolNotSupportedError) as exc:
|
|
raise AgentToolInnerServiceError(
|
|
error_code="agent_tool_invoke_failed",
|
|
description=str(exc),
|
|
status_code=422,
|
|
) from exc
|
|
except ValueError as exc:
|
|
raise _map_value_error(exc) from exc
|
|
except Exception as exc:
|
|
raise AgentToolInnerServiceError(
|
|
error_code="agent_tool_invoke_unexpected_error",
|
|
description=str(exc),
|
|
status_code=500,
|
|
) from exc
|
|
|
|
return AgentToolInvokeResponse(
|
|
messages=[message.model_dump(mode="json") for message in transformed_messages],
|
|
observation=ToolEngine.tool_response_to_str(transformed_messages),
|
|
metadata={
|
|
"provider_type": request.tool.provider_type,
|
|
"provider_id": request.tool.provider_id,
|
|
"tool_name": request.tool.tool_name,
|
|
},
|
|
)
|
|
|
|
|
|
def _map_value_error(error: ValueError) -> AgentToolInnerServiceError:
|
|
description = str(error)
|
|
if description == "app not found":
|
|
return AgentToolInnerServiceError(
|
|
error_code="app_not_found",
|
|
description="App not found.",
|
|
status_code=404,
|
|
)
|
|
return AgentToolInnerServiceError(
|
|
error_code="agent_tool_invoke_failed",
|
|
description=description,
|
|
status_code=422,
|
|
)
|