mirror of
https://github.com/langgenius/dify.git
synced 2026-11-11 13:51:25 +08:00
94 lines
3.6 KiB
Python
94 lines
3.6 KiB
Python
"""Reconcile Builder-owned setup reminders without granting Tool execution.
|
|
|
|
Only verified installed inventory entries may reach this owner. Generated
|
|
credential IDs are untrusted: preserve an exact actor/provider/tenant-visible
|
|
selection, otherwise remove it and keep the real installed Tool unconfigured.
|
|
Descriptions preserve user text. Only unchanged recognizable generated blocks
|
|
are removed/replaced; edits to a block remain user text. Fresh native admission
|
|
never reads these descriptions or stores a readiness Boolean on the node.
|
|
"""
|
|
|
|
from collections.abc import Mapping
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
from services.tools.draft_tool_setup import (
|
|
ToolDeclaration,
|
|
inventory_tool_nodes,
|
|
selected_credential_is_available,
|
|
tool_key,
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
from services.dify_builder.agent.resources import ResourceRef
|
|
|
|
_START = "[Builder tool setup]"
|
|
_END = "[/Builder tool setup]"
|
|
_SETUP_TEXT = "Configure or select authorized credentials before testing or publishing."
|
|
_UNVERIFIED_TEXT = (
|
|
"Credential setup could not be verified. Configure or select authorized credentials before testing or publishing."
|
|
)
|
|
|
|
|
|
def _description(description: str, *, action: str, needs_setup: bool, unverified: bool) -> str:
|
|
"""Replace only exact generated blocks; leave modified text verbatim."""
|
|
label = " ".join(action.split())
|
|
blocks = [f"{_START}\n{label}: {text}\n{_END}" for text in (_SETUP_TEXT, _UNVERIFIED_TEXT)]
|
|
for block in blocks:
|
|
description = description.replace("\n\n" + block, "").replace(block, "")
|
|
if needs_setup:
|
|
note = blocks[1 if unverified else 0]
|
|
return description + ("\n\n" if description else "") + note
|
|
return description
|
|
|
|
|
|
def reconcile_generated_tool_setup(
|
|
*,
|
|
tenant_id: str,
|
|
actor_id: str | None,
|
|
graph: Mapping[str, Any],
|
|
tools: Mapping[tuple[str, str], "ResourceRef"],
|
|
) -> None:
|
|
"""Sanitize generated references and annotate delayed setup before preflight."""
|
|
for node in inventory_tool_nodes(graph):
|
|
data = node["data"]
|
|
ref = tools.get(tool_key(data))
|
|
if ref is None:
|
|
# Unknown installed identities will be refused by the generation
|
|
# allowlist; do not echo a generated credential into diagnostics.
|
|
data.pop("credential_id", None)
|
|
continue
|
|
declaration = ToolDeclaration(
|
|
provider=tool_key(data)[0],
|
|
action=tool_key(data)[1],
|
|
label=ref.label,
|
|
needs_credentials=ref.needs_credentials or ref.readiness == "missing_config",
|
|
is_plugin=bool(ref.plugin_id),
|
|
)
|
|
unverified = False
|
|
if not declaration.needs_credentials:
|
|
# Native credential-free declarations do not use a selected ID.
|
|
data.pop("credential_id", None)
|
|
available = True
|
|
else:
|
|
try:
|
|
from extensions.ext_application_services import application_services
|
|
|
|
available = selected_credential_is_available(
|
|
query=application_services().credential_queries,
|
|
tenant_id=tenant_id,
|
|
actor_id=actor_id,
|
|
declaration=declaration,
|
|
credential_id=data.get("credential_id"),
|
|
)
|
|
except Exception:
|
|
available = False
|
|
unverified = True
|
|
if not available:
|
|
data.pop("credential_id", None)
|
|
data["desc"] = _description(
|
|
str(data.get("desc") or ""),
|
|
action=ref.label,
|
|
needs_setup=not available,
|
|
unverified=unverified,
|
|
)
|