mirror of
https://github.com/langgenius/dify.git
synced 2026-09-21 13:20:52 +08:00
chore(agent-v2): sync daily changes (#38298)
Signed-off-by: yyh <yuanyouhuilyz@gmail.com> Co-authored-by: 盐粒 Yanli <mail@yanli.one> Co-authored-by: Joel <iamjoel007@gmail.com> Co-authored-by: zyssyz123 <916125788@qq.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: 盐粒 Yanli <yanli@dify.ai> Co-authored-by: 林玮 (Jade Lin) <linw1995@icloud.com>
This commit is contained in:
co-authored by
盐粒 Yanli
Joel
zyssyz123
autofix-ci[bot]
盐粒 Yanli
林玮
parent
f8d47616c1
commit
1b3e8e9943
@@ -11,6 +11,7 @@ from dify_agent.layers.dify_plugin import (
|
||||
DifyPluginToolCredentialType,
|
||||
DifyPluginToolParameter,
|
||||
DifyPluginToolParameterForm,
|
||||
DifyPluginToolParameterType,
|
||||
DifyPluginToolsLayerConfig,
|
||||
)
|
||||
from sqlalchemy import select
|
||||
@@ -351,7 +352,9 @@ class WorkflowAgentDifyToolsBuilder:
|
||||
@staticmethod
|
||||
def _tool_layer_destination(tool_config: AgentSoulDifyToolConfig) -> Literal["plugin", "core"]:
|
||||
provider_type = ToolProviderType.value_of(tool_config.provider_type)
|
||||
if provider_type is ToolProviderType.PLUGIN:
|
||||
if provider_type is ToolProviderType.PLUGIN or (
|
||||
provider_type is ToolProviderType.BUILT_IN and _is_plugin_provider_id(tool_config.provider_id)
|
||||
):
|
||||
return "plugin"
|
||||
if provider_type in {
|
||||
ToolProviderType.BUILT_IN,
|
||||
@@ -404,7 +407,7 @@ class WorkflowAgentDifyToolsBuilder:
|
||||
credentials=self._normalize_credentials(runtime.credentials, tool_name=exposed_name),
|
||||
runtime_parameters=runtime_parameters,
|
||||
parameters=parameters,
|
||||
parameters_json_schema=tool_runtime.get_llm_parameters_json_schema(),
|
||||
parameters_json_schema=self._plugin_parameters_json_schema(tool_runtime, parameters),
|
||||
)
|
||||
|
||||
def _to_core_backend_tool_config(
|
||||
@@ -456,6 +459,41 @@ class WorkflowAgentDifyToolsBuilder:
|
||||
description = tool_runtime.entity.description.llm
|
||||
return description
|
||||
|
||||
@staticmethod
|
||||
def _plugin_parameters_json_schema(
|
||||
tool_runtime: Tool,
|
||||
parameters: list[DifyPluginToolParameter],
|
||||
) -> dict[str, Any]:
|
||||
schema = tool_runtime.get_llm_parameters_json_schema()
|
||||
properties = schema.setdefault("properties", {})
|
||||
required = schema.setdefault("required", [])
|
||||
if not isinstance(properties, dict) or not isinstance(required, list):
|
||||
raise WorkflowAgentDifyToolsBuildError(
|
||||
"agent_tool_declaration_invalid",
|
||||
f"Dify Plugin Tool {tool_runtime.entity.identity.name!r} has invalid parameter schema.",
|
||||
)
|
||||
|
||||
for parameter in parameters:
|
||||
if parameter.form is not DifyPluginToolParameterForm.LLM:
|
||||
continue
|
||||
if parameter.type is DifyPluginToolParameterType.FILE:
|
||||
properties[parameter.name] = _plugin_file_input_schema(parameter.llm_description or "")
|
||||
elif parameter.type in {
|
||||
DifyPluginToolParameterType.FILES,
|
||||
DifyPluginToolParameterType.SYSTEM_FILES,
|
||||
}:
|
||||
properties[parameter.name] = {
|
||||
"type": "array",
|
||||
"items": _plugin_file_input_schema(parameter.llm_description or ""),
|
||||
"description": parameter.llm_description or "",
|
||||
}
|
||||
else:
|
||||
continue
|
||||
|
||||
if parameter.required and parameter.name not in required:
|
||||
required.append(parameter.name)
|
||||
return schema
|
||||
|
||||
@staticmethod
|
||||
def _runtime_parameters(
|
||||
tool_runtime: Tool,
|
||||
@@ -498,3 +536,44 @@ class WorkflowAgentDifyToolsBuilder:
|
||||
),
|
||||
)
|
||||
return normalized
|
||||
|
||||
|
||||
def _is_plugin_provider_id(provider_id: str | None) -> bool:
|
||||
if not provider_id:
|
||||
return False
|
||||
parts = provider_id.split("/")
|
||||
return len(parts) == 3 and all(parts)
|
||||
|
||||
|
||||
def _plugin_file_input_schema(description: str) -> dict[str, Any]:
|
||||
return {
|
||||
"description": description,
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "HTTP(S) URL or sandbox-local file path.",
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
"required": ["transfer_method", "url"],
|
||||
"properties": {
|
||||
"transfer_method": {"type": "string", "enum": ["remote_url"]},
|
||||
"url": {"type": "string", "minLength": 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
"required": ["transfer_method", "reference"],
|
||||
"properties": {
|
||||
"transfer_method": {
|
||||
"type": "string",
|
||||
"enum": ["local_file", "tool_file", "datasource_file"],
|
||||
},
|
||||
"reference": {"type": "string", "minLength": 1},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -333,6 +333,8 @@ class WorkflowAgentOutputAdapter:
|
||||
session_snapshot = None
|
||||
if isinstance(event, AgentBackendRunSucceededInternalEvent | AgentBackendDeferredToolCallInternalEvent):
|
||||
session_snapshot = event.session_snapshot
|
||||
if event.usage is not None:
|
||||
agent_backend["usage"] = dict(event.usage)
|
||||
if session_snapshot is not None:
|
||||
agent_backend["session_snapshot"] = {
|
||||
"layer_count": len(session_snapshot.layers),
|
||||
|
||||
@@ -206,17 +206,14 @@ class WorkflowAgentRuntimeRequestBuilder:
|
||||
"cli_tool_count": len(agent_soul.tools.cli_tools),
|
||||
}
|
||||
|
||||
config_layer_config: DifyConfigLayerConfig | None = None
|
||||
soul_prompt_resolver = build_soul_mention_resolver(agent_soul)
|
||||
if dify_config.AGENT_DRIVE_MANIFEST_ENABLED:
|
||||
config_layer_config, config_warnings = build_config_layer_config(
|
||||
agent_soul,
|
||||
agent_id=context.agent.id,
|
||||
config_version_id=context.snapshot.id,
|
||||
config_version_kind="snapshot",
|
||||
)
|
||||
append_runtime_warnings(metadata, config_warnings)
|
||||
soul_prompt_resolver = build_config_aware_soul_mention_resolver(agent_soul)
|
||||
config_layer_config, config_warnings = build_config_layer_config(
|
||||
agent_soul,
|
||||
agent_id=context.agent.id,
|
||||
config_version_id=context.snapshot.id,
|
||||
config_version_kind="snapshot",
|
||||
)
|
||||
append_runtime_warnings(metadata, config_warnings)
|
||||
soul_prompt_resolver = build_config_aware_soul_mention_resolver(agent_soul)
|
||||
soul_prompt = expand_prompt_mentions(agent_soul.prompt.system_prompt, soul_prompt_resolver).strip()
|
||||
knowledge_config = build_knowledge_layer_config(agent_soul)
|
||||
|
||||
@@ -717,7 +714,7 @@ def build_shell_layer_config(agent_soul: AgentSoulConfig) -> DifyShellLayerConfi
|
||||
for tool in (_shell_cli_tool(item) for item in agent_soul.tools.cli_tools if _cli_tool_enabled(item))
|
||||
if tool is not None
|
||||
],
|
||||
env=[env for env in (_shell_env_var(item) for item in agent_soul.env.variables) if env is not None],
|
||||
env=_shell_env_vars(agent_soul.env.variables, agent_soul.env.secret_refs),
|
||||
secret_refs=[
|
||||
secret for secret in (_shell_secret_ref(item) for item in agent_soul.env.secret_refs) if secret is not None
|
||||
],
|
||||
@@ -864,8 +861,13 @@ def build_config_layer_config(
|
||||
agent_id: str | None = None,
|
||||
config_version_id: str | None = None,
|
||||
config_version_kind: Literal["snapshot", "draft", "build_draft"] = "snapshot",
|
||||
) -> tuple[DifyConfigLayerConfig | None, list[dict[str, str]]]:
|
||||
"""Derive prompt-mentioned eager-pull names from Agent Soul."""
|
||||
) -> tuple[DifyConfigLayerConfig, list[dict[str, str]]]:
|
||||
"""Build the always-present Agent config layer from Agent Soul state.
|
||||
|
||||
The ``dify.config`` layer must exist for every Agent v2 runtime request so
|
||||
the backend can expose the config CLI/help surface even when the current
|
||||
Agent Soul has no config assets, note, or prompt mentions.
|
||||
"""
|
||||
|
||||
ordered_mentions = list(
|
||||
dict.fromkeys(
|
||||
@@ -874,14 +876,6 @@ def build_config_layer_config(
|
||||
if mention.kind in {MentionKind.SKILL, MentionKind.FILE} and mention.ref_id
|
||||
)
|
||||
)
|
||||
if (
|
||||
not agent_soul.config_skills
|
||||
and not agent_soul.config_files
|
||||
and not agent_soul.config_note
|
||||
and not ordered_mentions
|
||||
):
|
||||
return None, []
|
||||
|
||||
skill_names = {skill.name for skill in agent_soul.config_skills}
|
||||
file_names = {file_ref.name for file_ref in agent_soul.config_files}
|
||||
warnings: list[dict[str, str]] = []
|
||||
@@ -968,11 +962,7 @@ def _shell_cli_tool(item: object) -> DifyShellCliToolConfig | None:
|
||||
if not commands and not isinstance(name, str):
|
||||
return None
|
||||
tool_env = data.get("env") if isinstance(data.get("env"), Mapping) else {}
|
||||
env = [
|
||||
env_var
|
||||
for env_var in (_shell_env_var(item) for item in _env_entries(tool_env, "variables"))
|
||||
if env_var is not None
|
||||
]
|
||||
env = _shell_env_vars(_env_entries(tool_env, "variables"), _env_entries(tool_env, "secret_refs"))
|
||||
secret_refs = [
|
||||
secret_ref
|
||||
for secret_ref in (_shell_secret_ref(item) for item in _env_entries(tool_env, "secret_refs"))
|
||||
@@ -995,6 +985,12 @@ def _env_entries(env: object, key: str) -> list[object]:
|
||||
return entries
|
||||
|
||||
|
||||
def _shell_env_vars(variables: Sequence[object], secret_refs: Sequence[object]) -> list[DifyShellEnvVarConfig]:
|
||||
env_vars = [_shell_env_var(item) for item in variables]
|
||||
secret_env_vars = [_shell_env_var(item) for item in secret_refs if _has_secret_value(item)]
|
||||
return [env for env in [*env_vars, *secret_env_vars] if env is not None]
|
||||
|
||||
|
||||
def _shell_env_var(item: object) -> DifyShellEnvVarConfig | None:
|
||||
data = _plain_mapping(item)
|
||||
name = _name_from_mapping(data)
|
||||
@@ -1011,13 +1007,15 @@ def _shell_secret_ref(item: object) -> DifyShellSecretRefConfig | None:
|
||||
name = _name_from_mapping(data)
|
||||
if name is None:
|
||||
return None
|
||||
ref = (
|
||||
data.get("ref")
|
||||
or data.get("value")
|
||||
or data.get("id")
|
||||
or data.get("credential_id")
|
||||
or data.get("provider_credential_id")
|
||||
)
|
||||
# Inline Composer values are passed as env vars because the agent-backend
|
||||
# secret ref schema only accepts short backend-managed reference IDs.
|
||||
if _has_secret_value(item):
|
||||
return None
|
||||
ref = data.get("ref") or data.get("credential_id") or data.get("provider_credential_id")
|
||||
if ref is None:
|
||||
ref = data.get("id")
|
||||
if ref is None:
|
||||
return None
|
||||
return DifyShellSecretRefConfig(name=name, ref=str(ref) if ref is not None else None)
|
||||
|
||||
|
||||
@@ -1029,6 +1027,12 @@ def _plain_mapping(item: object) -> dict[str, Any]:
|
||||
return {}
|
||||
|
||||
|
||||
def _has_secret_value(item: object) -> bool:
|
||||
data = _plain_mapping(item)
|
||||
value = data.get("value")
|
||||
return isinstance(value, str) and bool(value)
|
||||
|
||||
|
||||
def _name_from_mapping(item: Mapping[str, Any]) -> str | None:
|
||||
for key in ("name", "key", "env_name", "variable"):
|
||||
value = item.get(key)
|
||||
|
||||
Reference in New Issue
Block a user