mirror of
https://github.com/langgenius/dify.git
synced 2026-11-11 22:02:23 +08:00
217 lines
9.1 KiB
Python
217 lines
9.1 KiB
Python
"""Exact installed metadata for the qualified JSON Process parse action.
|
|
|
|
The fixed qualifications describe reviewed administrator-installed runtimes,
|
|
not atomic leases on daemon bytes. Callers read metadata before and after dispatch.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import TYPE_CHECKING
|
|
|
|
from core.plugin.entities.plugin import PluginInstallationSource
|
|
from core.plugin.impl.plugin import PluginInstaller
|
|
from core.plugin.impl.tool import PluginToolManager
|
|
from runtime_contracts.builder.execution import (
|
|
BuilderExecutionPolicyError,
|
|
QualifiedJsonToolBinding,
|
|
canonical_digest,
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
from core.plugin.entities.plugin_daemon import PluginToolProviderEntity
|
|
from core.tools.entities.tool_entities import ToolEntity, ToolProviderEntityWithPlugin
|
|
|
|
PLUGIN_ID = "langgenius/json_process"
|
|
PROVIDER_ID = "langgenius/json_process/json_process"
|
|
NATIVE_PROVIDER_NAME = "json_process"
|
|
TOOL_NAME = "parse"
|
|
VERSION = "0.0.8"
|
|
CHECKSUM = "3333687ec0412e1f2a1798d632fc5f3187f36ebd4bf2432a599a96c74e92f503"
|
|
UNIQUE_IDENTIFIER = f"{PLUGIN_ID}:{VERSION}@{CHECKSUM}"
|
|
PROVIDER_SCHEMA_DIGEST = "90493459b7c95b78148c085cb33833c932c671f5e7296c0e7d8b050f2a6998a3"
|
|
TOOL_SCHEMA_DIGEST = "bf4897c1c2e3b3a04e9abc8bfdd4d7715abbd7bc5b2148ad4067b6b9fe11e449"
|
|
QUALIFICATION_REVISION = "local-json-process-0.0.8-sdk-0.10.2-jsonpath-ng-1.8.0-python-3.12.3-v1"
|
|
QUALIFICATION_DIGEST = canonical_digest(
|
|
{
|
|
"revision": QUALIFICATION_REVISION,
|
|
"identifier": UNIQUE_IDENTIFIER,
|
|
"provider_schema_digest": PROVIDER_SCHEMA_DIGEST,
|
|
"tool_schema_digest": TOOL_SCHEMA_DIGEST,
|
|
"archive_sha256": "a43cbcb37762c512285f0b9ce2a5eb239e205e0b3a2c0fa2d9dba23a5aff6944",
|
|
"runtime_type": "local",
|
|
}
|
|
)
|
|
SERVERLESS_QUALIFICATION_REVISION = "serverless-json-process-0.0.8-sdk-0.10.2-jsonpath-ng-1.8.0-python-3.12.14-v1"
|
|
SERVERLESS_QUALIFICATION_DIGEST = canonical_digest(
|
|
{
|
|
"revision": SERVERLESS_QUALIFICATION_REVISION,
|
|
"identifier": UNIQUE_IDENTIFIER,
|
|
"provider_schema_digest": PROVIDER_SCHEMA_DIGEST,
|
|
"tool_schema_digest": TOOL_SCHEMA_DIGEST,
|
|
"archive_sha256": "a43cbcb37762c512285f0b9ce2a5eb239e205e0b3a2c0fa2d9dba23a5aff6944",
|
|
"runtime_type": "serverless",
|
|
}
|
|
)
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class InstalledJsonParse:
|
|
tenant_id: str
|
|
installation_id: str
|
|
plugin_id: str
|
|
provider_id: str
|
|
tool_name: str
|
|
unique_identifier: str
|
|
version: str
|
|
checksum: str
|
|
provider_schema_digest: str
|
|
tool_schema_digest: str
|
|
runtime_type: str
|
|
qualification_digest: str
|
|
|
|
def binding_fields(self) -> dict[str, str]:
|
|
"""Project only the reviewed installation identity, without reflective access."""
|
|
return {
|
|
"tenant_id": self.tenant_id,
|
|
"installation_id": self.installation_id,
|
|
"plugin_id": self.plugin_id,
|
|
"provider_id": self.provider_id,
|
|
"tool_name": self.tool_name,
|
|
"unique_identifier": self.unique_identifier,
|
|
"version": self.version,
|
|
"checksum": self.checksum,
|
|
"provider_schema_digest": self.provider_schema_digest,
|
|
"tool_schema_digest": self.tool_schema_digest,
|
|
"runtime_type": self.runtime_type,
|
|
"qualification_digest": self.qualification_digest,
|
|
}
|
|
|
|
@classmethod
|
|
def from_binding(cls, binding: QualifiedJsonToolBinding) -> InstalledJsonParse:
|
|
return cls(
|
|
tenant_id=binding.tenant_id,
|
|
installation_id=binding.installation_id,
|
|
plugin_id=binding.plugin_id,
|
|
provider_id=binding.provider_id,
|
|
tool_name=binding.tool_name,
|
|
unique_identifier=binding.unique_identifier,
|
|
version=binding.version,
|
|
checksum=binding.checksum,
|
|
provider_schema_digest=binding.provider_schema_digest,
|
|
tool_schema_digest=binding.tool_schema_digest,
|
|
runtime_type=binding.runtime_type,
|
|
qualification_digest=binding.qualification_digest,
|
|
)
|
|
|
|
@property
|
|
def binding_digest(self) -> str:
|
|
return canonical_digest(self.binding_fields())
|
|
|
|
|
|
def _validate_declaration(declaration: ToolProviderEntityWithPlugin) -> ToolEntity:
|
|
"""Validate the full normalized declaration before any native credential branch."""
|
|
if (
|
|
declaration.identity.name != PROVIDER_ID
|
|
or declaration.plugin_id is not None
|
|
or declaration.credentials_schema
|
|
or declaration.oauth_schema is not None
|
|
or canonical_digest(declaration.model_dump(mode="json")) != PROVIDER_SCHEMA_DIGEST
|
|
):
|
|
raise BuilderExecutionPolicyError("unsupported_json_tool_installation")
|
|
parses = [tool for tool in declaration.tools if tool.identity.name == TOOL_NAME]
|
|
if len(parses) != 1:
|
|
raise BuilderExecutionPolicyError("unsupported_json_tool_installation")
|
|
parse = parses[0]
|
|
if (
|
|
parse.identity.provider != PROVIDER_ID
|
|
or parse.has_runtime_parameters
|
|
or parse.output_schema
|
|
or canonical_digest(parse.model_dump(mode="json")) != TOOL_SCHEMA_DIGEST
|
|
):
|
|
raise BuilderExecutionPolicyError("unsupported_json_tool_installation")
|
|
return parse
|
|
|
|
|
|
def validate_current_json_controller(controller: object, tool: object, *, expected: InstalledJsonParse) -> None:
|
|
"""Check the exact controller/tool returned by ToolManager, including cached results."""
|
|
from core.tools.plugin_tool.provider import PluginToolProviderController
|
|
from core.tools.plugin_tool.tool import PluginTool
|
|
|
|
if (
|
|
type(controller) is not PluginToolProviderController
|
|
or type(tool) is not PluginTool
|
|
or controller.tenant_id != expected.tenant_id
|
|
or controller.plugin_id != expected.plugin_id
|
|
or controller.plugin_unique_identifier != expected.unique_identifier
|
|
or controller.need_credentials
|
|
):
|
|
raise BuilderExecutionPolicyError("json_tool_controller_mismatch")
|
|
_validate_declaration(controller.entity)
|
|
if (
|
|
tool.entity.identity.name != TOOL_NAME
|
|
or tool.entity.identity.provider != PROVIDER_ID
|
|
or tool.plugin_unique_identifier != expected.unique_identifier
|
|
or tool.tenant_id != expected.tenant_id
|
|
or canonical_digest(tool.entity.model_dump(mode="json")) != TOOL_SCHEMA_DIGEST
|
|
):
|
|
raise BuilderExecutionPolicyError("json_tool_controller_mismatch")
|
|
|
|
|
|
def resolve_installed_json_parse(*, tenant_id: str) -> InstalledJsonParse:
|
|
"""Read the installation and provider from their native owners, outside SQL."""
|
|
if type(tenant_id) is not str or not 1 <= len(tenant_id) <= 128:
|
|
raise BuilderExecutionPolicyError("installed_json_tool_unavailable")
|
|
try:
|
|
installations = PluginInstaller().fetch_plugin_installation_by_ids(tenant_id, [PLUGIN_ID])
|
|
provider: PluginToolProviderEntity = PluginToolManager().fetch_tool_provider(tenant_id, PROVIDER_ID)
|
|
except Exception:
|
|
raise BuilderExecutionPolicyError("installed_json_tool_unavailable") from None
|
|
try:
|
|
if len(installations) != 1:
|
|
raise BuilderExecutionPolicyError("installed_json_tool_unavailable")
|
|
installation = installations[0]
|
|
if (
|
|
installation.tenant_id != tenant_id
|
|
or installation.plugin_id != PLUGIN_ID
|
|
or installation.plugin_unique_identifier != UNIQUE_IDENTIFIER
|
|
or installation.version != VERSION
|
|
or installation.checksum != CHECKSUM
|
|
or installation.source != PluginInstallationSource.Marketplace
|
|
or provider.provider != NATIVE_PROVIDER_NAME
|
|
or provider.plugin_id != PLUGIN_ID
|
|
or provider.plugin_unique_identifier != UNIQUE_IDENTIFIER
|
|
):
|
|
raise BuilderExecutionPolicyError("unsupported_json_tool_installation")
|
|
if installation.runtime_type == "local":
|
|
qualification_digest = QUALIFICATION_DIGEST
|
|
elif installation.runtime_type == "serverless":
|
|
qualification_digest = SERVERLESS_QUALIFICATION_DIGEST
|
|
else:
|
|
raise BuilderExecutionPolicyError("unsupported_json_tool_installation")
|
|
_validate_declaration(provider.declaration)
|
|
return InstalledJsonParse(
|
|
tenant_id=tenant_id,
|
|
installation_id=installation.id,
|
|
plugin_id=PLUGIN_ID,
|
|
provider_id=PROVIDER_ID,
|
|
tool_name=TOOL_NAME,
|
|
unique_identifier=UNIQUE_IDENTIFIER,
|
|
version=VERSION,
|
|
checksum=CHECKSUM,
|
|
provider_schema_digest=PROVIDER_SCHEMA_DIGEST,
|
|
tool_schema_digest=TOOL_SCHEMA_DIGEST,
|
|
runtime_type=installation.runtime_type,
|
|
qualification_digest=qualification_digest,
|
|
)
|
|
except BuilderExecutionPolicyError:
|
|
raise
|
|
except Exception:
|
|
raise BuilderExecutionPolicyError("installed_json_tool_unavailable") from None
|
|
|
|
|
|
def require_same_json_installation(expected: QualifiedJsonToolBinding, actual: InstalledJsonParse) -> None:
|
|
"""Require the persisted expected identity to match this exact metadata read."""
|
|
if InstalledJsonParse.from_binding(expected) != actual:
|
|
raise BuilderExecutionPolicyError("json_tool_installation_changed")
|