feat(agent-v2): resolve Dify core tools in agent runtime runner (#38316)

This commit is contained in:
林玮 (Jade Lin) 2026-07-02 17:25:11 +08:00 committed by GitHub
parent 88864ae086
commit 92618f5e20
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 114 additions and 0 deletions

View File

@ -38,6 +38,7 @@ from pydantic_ai.tools import DeferredToolRequests, DeferredToolResults
from agenton.compositor import CompositorSessionSnapshot, LayerProviderInput
from agenton.layers.types import PydanticAITool
from dify_agent.layers.ask_human.layer import get_ask_human_layer, validate_ask_human_layer_composition
from dify_agent.layers.dify_core_tools.layer import DifyCoreToolsLayer
from dify_agent.layers.dify_plugin.llm_layer import DifyPluginLLMLayer
from dify_agent.layers.dify_plugin.tools_layer import DifyPluginToolsLayer
from dify_agent.layers.knowledge.layer import DifyKnowledgeBaseLayer
@ -285,6 +286,8 @@ async def _resolve_run_tools(
layer = slot.layer
if isinstance(layer, DifyPluginToolsLayer):
resolved_tools.extend(await layer.get_tools(http_client=plugin_daemon_http_client))
if isinstance(layer, DifyCoreToolsLayer):
resolved_tools.extend(await layer.get_tools(http_client=dify_api_http_client))
if isinstance(layer, DifyKnowledgeBaseLayer):
resolved_tools.extend(await layer.get_tools(http_client=dify_api_http_client))
_validate_unique_tool_names(resolved_tools)

View File

@ -42,6 +42,12 @@ from dify_agent.layers.dify_plugin.configs import (
)
from dify_agent.layers.dify_plugin.llm_layer import DifyPluginLLMLayer
from dify_agent.layers.dify_plugin.tools_layer import DifyPluginToolsLayer
from dify_agent.layers.dify_core_tools.configs import (
DIFY_CORE_TOOLS_LAYER_TYPE_ID,
DifyCoreToolConfig,
DifyCoreToolsLayerConfig,
)
from dify_agent.layers.dify_core_tools.layer import DifyCoreToolsLayer
from dify_agent.layers.knowledge.configs import DIFY_KNOWLEDGE_BASE_LAYER_TYPE_ID, DifyKnowledgeBaseLayerConfig
from dify_agent.layers.knowledge.layer import DifyKnowledgeBaseLayer
from dify_agent.layers.output import DIFY_OUTPUT_LAYER_TYPE_ID, DifyOutputLayerConfig
@ -1093,6 +1099,111 @@ def test_runner_passes_dynamic_dify_knowledge_tools_to_agent(monkeypatch: pytest
assert [tool.name for tool in seen_tools] == ["knowledge_base_search"]
def test_runner_passes_dynamic_dify_core_tools_to_agent(monkeypatch: pytest.MonkeyPatch) -> None:
seen_tools: list[Tool[object]] = []
async def core_tool() -> str:
return "core"
def fake_get_model(_self: DifyPluginLLMLayer, *, http_client: httpx.AsyncClient):
assert http_client.is_closed is False
return TestModel(custom_output_text="done") # pyright: ignore[reportReturnType]
async def fake_get_tools(self: DifyCoreToolsLayer, *, http_client: httpx.AsyncClient) -> list[Tool[object]]:
assert self.config.tools[0].provider_type == "builtin"
assert self.config.tools[0].tool_name == "draft_message"
assert http_client.headers.get("X-Test-Client") == "dify-api"
return [Tool(core_tool, name="draft_message")]
class FakeResult:
output: str = "done"
def new_messages(self) -> list[ModelMessage]:
return []
class FakeAgent:
async def run(self, *_args: object, **_kwargs: object) -> FakeResult:
return FakeResult()
def fake_create_agent(model: object, *, tools: list[Tool[object]], output_type: object) -> FakeAgent:
del model, output_type
seen_tools.extend(tools)
return FakeAgent()
monkeypatch.setattr(DifyPluginLLMLayer, "get_model", fake_get_model)
monkeypatch.setattr(DifyCoreToolsLayer, "get_tools", fake_get_tools)
monkeypatch.setattr("dify_agent.runtime.runner.create_agent", fake_create_agent)
request = CreateRunRequest(
composition=RunComposition(
layers=[
RunLayerSpec(
name="prompt",
type="plain.prompt",
config=PromptLayerConfig(prefix="system", user="hello"),
),
RunLayerSpec(
name="execution_context",
type=DIFY_EXECUTION_CONTEXT_LAYER_TYPE_ID,
config=DifyExecutionContextLayerConfig(
tenant_id="tenant-1",
user_id="user-1",
user_from="account",
app_id="app-1",
agent_mode="workflow_run",
invoke_from="service-api",
),
),
RunLayerSpec(
name=DIFY_AGENT_MODEL_LAYER_ID,
type="dify.plugin.llm",
deps={"execution_context": "execution_context"},
config=DifyPluginLLMLayerConfig(
plugin_id="langgenius/openai",
model_provider="openai",
model="demo-model",
credentials={"api_key": "secret"},
),
),
RunLayerSpec(
name="core-tools",
type=DIFY_CORE_TOOLS_LAYER_TYPE_ID,
deps={"execution_context": "execution_context"},
config=DifyCoreToolsLayerConfig(
tools=[
DifyCoreToolConfig(
provider_type="builtin",
provider_id="langgenius/dify-gmail/dify-gmail",
tool_name="draft_message",
credential_id="credential-1",
parameters_json_schema={"type": "object", "properties": {}, "required": []},
)
]
),
),
]
)
)
sink = InMemoryRunEventSink()
async def scenario() -> None:
async with (
httpx.AsyncClient() as plugin_client,
httpx.AsyncClient(headers={"X-Test-Client": "dify-api"}) as dify_api_client,
):
await AgentRunRunner(
sink=sink,
request=request,
run_id="run-core-tools",
plugin_daemon_http_client=plugin_client,
dify_api_http_client=dify_api_client,
).run()
asyncio.run(scenario())
assert [tool.name for tool in seen_tools] == ["draft_message"]
def test_runner_rejects_duplicate_tool_names_across_dynamic_tool_layers(
monkeypatch: pytest.MonkeyPatch,
) -> None: