mirror of
https://github.com/langgenius/dify.git
synced 2026-11-10 21:35:45 +08:00
160 lines
5.8 KiB
Python
160 lines
5.8 KiB
Python
import pytest
|
|
from pydantic import ValidationError
|
|
from werkzeug.exceptions import BadRequest
|
|
|
|
from controllers.openapi._models import PageQuery, ToolListQuery, ToolListResponse
|
|
from controllers.openapi.tool_providers import _credential_write_errors, tool_rows
|
|
from core.tools.__base.tool import ToolParameter
|
|
from core.tools.entities.api_entities import ToolApiEntity, ToolProviderApiEntity
|
|
from core.tools.entities.common_entities import I18nObject
|
|
from core.tools.entities.tool_entities import ToolProviderType
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"message",
|
|
[
|
|
"the credential name 'main' is already used",
|
|
"you have reached the maximum number of providers for tavily",
|
|
"provider tavily does not need credentials",
|
|
],
|
|
)
|
|
def test_other_failures_are_bad_request_with_the_service_message(message: str) -> None:
|
|
with pytest.raises(BadRequest, match=message):
|
|
with _credential_write_errors():
|
|
raise ValueError(message)
|
|
|
|
|
|
Form = ToolParameter.ToolParameterForm
|
|
ParamType = ToolParameter.ToolParameterType
|
|
|
|
|
|
def _param(
|
|
name: str, form: ToolParameter.ToolParameterForm, *, default: str | None = None, required: bool = False
|
|
) -> ToolParameter:
|
|
return ToolParameter(
|
|
name=name, label=I18nObject(en_US=name), type=ParamType.STRING, form=form, default=default, required=required
|
|
)
|
|
|
|
|
|
def _tool(name: str, parameters: list[ToolParameter] | None, label: str | None = None) -> ToolApiEntity:
|
|
return ToolApiEntity(
|
|
author="a",
|
|
name=name,
|
|
label=I18nObject(en_US=label if label is not None else name.title()),
|
|
description=I18nObject(en_US=f"{name} tool"),
|
|
parameters=parameters,
|
|
)
|
|
|
|
|
|
def _provider(
|
|
pid: str,
|
|
kind: ToolProviderType,
|
|
tools: list[ToolApiEntity],
|
|
*,
|
|
plugin_id: str = "",
|
|
plugin_unique_identifier: str = "",
|
|
server_identifier: str = "",
|
|
is_team_authorization: bool = True,
|
|
) -> ToolProviderApiEntity:
|
|
return ToolProviderApiEntity(
|
|
id=pid,
|
|
author="a",
|
|
name=pid,
|
|
description=I18nObject(en_US=pid),
|
|
icon="",
|
|
label=I18nObject(en_US=pid.split("/")[-1].title()),
|
|
type=kind,
|
|
tools=tools,
|
|
plugin_id=plugin_id,
|
|
plugin_unique_identifier=plugin_unique_identifier,
|
|
server_identifier=server_identifier,
|
|
is_team_authorization=is_team_authorization,
|
|
)
|
|
|
|
|
|
GITHUB = _provider(
|
|
"langgenius/github/github",
|
|
ToolProviderType.BUILT_IN,
|
|
[
|
|
_tool(
|
|
"get_issues",
|
|
[
|
|
_param("repo", Form.LLM, required=True),
|
|
_param("state", Form.FORM, default="open"),
|
|
_param("spec", Form.SCHEMA),
|
|
],
|
|
)
|
|
],
|
|
plugin_id="langgenius/github",
|
|
plugin_unique_identifier="langgenius/github:0.1.0@abc",
|
|
is_team_authorization=False,
|
|
)
|
|
|
|
|
|
def test_parameters_split_by_form() -> None:
|
|
[row] = tool_rows([GITHUB], words="", provider=None, language=None)
|
|
data = row.node_data
|
|
assert {k: (v.type, v.value) for k, v in data.tool_parameters.items()} == {"repo": ("constant", None)}
|
|
assert {k: (v.type, v.value) for k, v in data.tool_configurations.items()} == {"state": ("constant", "open")}
|
|
assert [p.name for p in row.parameters] == ["repo", "state", "spec"]
|
|
assert data.tool_node_version == "2"
|
|
assert (data.plugin_id, data.plugin_unique_identifier) == ("langgenius/github", "langgenius/github:0.1.0@abc")
|
|
assert (data.provider_id, data.provider_name, data.provider_type) == (
|
|
"langgenius/github/github",
|
|
"langgenius/github/github",
|
|
"builtin",
|
|
)
|
|
assert row.configured is False
|
|
|
|
|
|
def test_mcp_provider_id_is_the_server_identifier_and_no_plugin_fields() -> None:
|
|
mcp = _provider("uuid-1", ToolProviderType.MCP, [_tool("search", [])], server_identifier="my-server")
|
|
[row] = tool_rows([mcp], words="", provider=None, language=None)
|
|
assert row.node_data.provider_id == "my-server"
|
|
assert row.node_data.plugin_id is None
|
|
assert row.node_data.plugin_unique_identifier is None
|
|
|
|
|
|
def test_tool_without_parameters() -> None:
|
|
wf = _provider("wf-uuid", ToolProviderType.WORKFLOW, [_tool("summarize", None)])
|
|
[row] = tool_rows([wf], words="", provider=None, language=None)
|
|
assert row.parameters == []
|
|
assert row.node_data.tool_parameters == {}
|
|
assert row.node_data.tool_configurations == {}
|
|
assert row.node_data.tool_name == "summarize"
|
|
|
|
|
|
def test_label_falls_back_to_name() -> None:
|
|
p = _provider("x/y/z", ToolProviderType.API, [_tool("ping", [], label="")])
|
|
[row] = tool_rows([p], words="", provider=None, language=None)
|
|
assert row.label == "ping"
|
|
assert row.node_data.title == "ping"
|
|
assert row.node_data.tool_label == "ping"
|
|
|
|
|
|
def test_rows_are_flattened_sorted_and_filtered() -> None:
|
|
other = _provider("acme/slack/slack", ToolProviderType.BUILT_IN, [_tool("send", []), _tool("list", [])])
|
|
rows = tool_rows([GITHUB, other], words="", provider=None, language=None)
|
|
assert [(r.provider, r.name) for r in rows] == [
|
|
("acme/slack/slack", "list"),
|
|
("acme/slack/slack", "send"),
|
|
("langgenius/github/github", "get_issues"),
|
|
]
|
|
assert [r.name for r in tool_rows([GITHUB, other], words="GITHUB", provider=None, language=None)] == ["get_issues"]
|
|
assert [r.name for r in tool_rows([GITHUB, other], words="", provider="acme/slack/slack", language=None)] == [
|
|
"list",
|
|
"send",
|
|
]
|
|
assert tool_rows([GITHUB, other], words="", provider="no/such/provider", language=None) == []
|
|
|
|
|
|
def test_page_past_the_end() -> None:
|
|
rows = tool_rows([GITHUB], words="", provider=None, language=None)
|
|
page = ToolListResponse.page_of(rows, query=PageQuery(page=99, limit=20))
|
|
assert (page.data, page.total, page.has_more) == ([], 1, False)
|
|
|
|
|
|
def test_unknown_provider_type_is_rejected() -> None:
|
|
with pytest.raises(ValidationError):
|
|
ToolListQuery.model_validate({"provider_type": "plugin"})
|