mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-18 02:25:52 +08:00
feat: add fc agent mode support
This commit is contained in:
parent
fb7b2c8ff3
commit
b56d2b739b
@ -420,10 +420,11 @@ POSITION_PROVIDER_EXCLUDES=
|
||||
|
||||
# Plugin configuration
|
||||
PLUGIN_API_KEY=lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1
|
||||
PLUGIN_DAEMON_URL=http://127.0.0.1:5002
|
||||
PLUGIN_API_URL=http://127.0.0.1:5002
|
||||
PLUGIN_REMOTE_INSTALL_PORT=5003
|
||||
PLUGIN_REMOTE_INSTALL_HOST=localhost
|
||||
PLUGIN_MAX_PACKAGE_SIZE=15728640
|
||||
INNER_API_KEY=QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1
|
||||
INNER_API_KEY_FOR_PLUGIN=QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1
|
||||
|
||||
# Marketplace configuration
|
||||
|
@ -14,12 +14,10 @@ class PluginAgentStrategy(BaseAgentStrategy):
|
||||
"""
|
||||
|
||||
tenant_id: str
|
||||
plugin_unique_identifier: str
|
||||
declaration: AgentStrategyEntity
|
||||
|
||||
def __init__(self, tenant_id: str, plugin_unique_identifier: str, declaration: AgentStrategyEntity):
|
||||
def __init__(self, tenant_id: str, declaration: AgentStrategyEntity):
|
||||
self.tenant_id = tenant_id
|
||||
self.plugin_unique_identifier = plugin_unique_identifier
|
||||
self.declaration = declaration
|
||||
|
||||
def get_parameters(self) -> Sequence[AgentStrategyParameter]:
|
||||
|
@ -255,10 +255,9 @@ class WorkflowCycleManage:
|
||||
for workflow_node_execution in running_workflow_node_executions:
|
||||
workflow_node_execution.status = WorkflowNodeExecutionStatus.FAILED.value
|
||||
workflow_node_execution.error = error
|
||||
workflow_node_execution.finished_at = datetime.now(UTC).replace(tzinfo=None)
|
||||
workflow_node_execution.elapsed_time = (
|
||||
workflow_node_execution.finished_at - workflow_node_execution.created_at
|
||||
).total_seconds()
|
||||
finish_at = datetime.now(UTC).replace(tzinfo=None)
|
||||
workflow_node_execution.finished_at = finish_at
|
||||
workflow_node_execution.elapsed_time = (finish_at - workflow_node_execution.created_at).total_seconds()
|
||||
|
||||
if trace_manager:
|
||||
trace_manager.add_trace_task(
|
||||
|
@ -136,7 +136,8 @@ def cast_parameter_value(typ: enum.StrEnum, value: Any, /):
|
||||
return value
|
||||
case _:
|
||||
return str(value)
|
||||
|
||||
except ValueError:
|
||||
raise
|
||||
except Exception:
|
||||
raise ValueError(f"The tool parameter value {value} is not in correct type of {as_normal_type(typ)}.")
|
||||
|
||||
|
@ -92,7 +92,7 @@ class PluginAgentManager(BasePluginManager):
|
||||
|
||||
response = self._request_with_plugin_daemon_response_stream(
|
||||
"POST",
|
||||
f"plugin/{tenant_id}/dispatch/agent/invoke",
|
||||
f"plugin/{tenant_id}/dispatch/agent_strategy/invoke",
|
||||
AgentInvokeMessage,
|
||||
data={
|
||||
"user_id": user_id,
|
||||
|
@ -1,15 +1,23 @@
|
||||
from collections.abc import Generator, Sequence
|
||||
from ast import literal_eval
|
||||
from collections.abc import Generator, Mapping, Sequence
|
||||
from typing import Any, cast
|
||||
|
||||
from core.agent.entities import AgentToolEntity
|
||||
from core.agent.plugin_entities import AgentStrategyParameter
|
||||
from core.model_manager import ModelManager
|
||||
from core.model_runtime.entities.model_entities import ModelType
|
||||
from core.plugin.manager.exc import PluginDaemonClientSideError
|
||||
from core.tools.entities.tool_entities import ToolProviderType
|
||||
from core.tools.tool_manager import ToolManager
|
||||
from core.workflow.entities.node_entities import NodeRunResult
|
||||
from core.workflow.entities.variable_pool import VariablePool
|
||||
from core.workflow.enums import SystemVariableKey
|
||||
from core.workflow.nodes.agent.entities import AgentNodeData
|
||||
from core.workflow.nodes.base.entities import BaseNodeData
|
||||
from core.workflow.nodes.enums import NodeType
|
||||
from core.workflow.nodes.event.event import RunCompletedEvent
|
||||
from core.workflow.nodes.tool.tool_node import ToolNode
|
||||
from core.workflow.utils.variable_template_parser import VariableTemplateParser
|
||||
from factories.agent_factory import get_plugin_agent_strategy
|
||||
from models.workflow import WorkflowNodeExecutionStatus
|
||||
|
||||
@ -31,7 +39,6 @@ class AgentNode(ToolNode):
|
||||
try:
|
||||
strategy = get_plugin_agent_strategy(
|
||||
tenant_id=self.tenant_id,
|
||||
plugin_unique_identifier=node_data.plugin_unique_identifier,
|
||||
agent_strategy_provider_name=node_data.agent_strategy_provider_name,
|
||||
agent_strategy_name=node_data.agent_strategy_name,
|
||||
)
|
||||
@ -48,12 +55,12 @@ class AgentNode(ToolNode):
|
||||
agent_parameters = strategy.get_parameters()
|
||||
|
||||
# get parameters
|
||||
parameters = self._generate_parameters(
|
||||
parameters = self._generate_agent_parameters(
|
||||
agent_parameters=agent_parameters,
|
||||
variable_pool=self.graph_runtime_state.variable_pool,
|
||||
node_data=node_data,
|
||||
)
|
||||
parameters_for_log = self._generate_parameters(
|
||||
parameters_for_log = self._generate_agent_parameters(
|
||||
agent_parameters=agent_parameters,
|
||||
variable_pool=self.graph_runtime_state.variable_pool,
|
||||
node_data=node_data,
|
||||
@ -78,6 +85,7 @@ class AgentNode(ToolNode):
|
||||
error=f"Failed to invoke agent: {str(e)}",
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
# convert tool messages
|
||||
@ -91,7 +99,7 @@ class AgentNode(ToolNode):
|
||||
)
|
||||
)
|
||||
|
||||
def _generate_parameters(
|
||||
def _generate_agent_parameters(
|
||||
self,
|
||||
*,
|
||||
agent_parameters: Sequence[AgentStrategyParameter],
|
||||
@ -130,6 +138,86 @@ class AgentNode(ToolNode):
|
||||
parameter_value = segment_group.log if for_log else segment_group.text
|
||||
else:
|
||||
raise ValueError(f"Unknown agent input type '{agent_input.type}'")
|
||||
result[parameter_name] = parameter_value
|
||||
value = parameter_value.strip()
|
||||
if (parameter_value.startswith("{") and parameter_value.endswith("}")) or (
|
||||
parameter_value.startswith("[") and parameter_value.endswith("]")
|
||||
):
|
||||
value = literal_eval(parameter_value) # transform string to python object
|
||||
if parameter.type == "array[tools]":
|
||||
value = cast(list[dict[str, Any]], value)
|
||||
value = [tool for tool in value if tool.get("enabled", False)]
|
||||
|
||||
if not for_log:
|
||||
if parameter.type == "array[tools]":
|
||||
value = cast(list[dict[str, Any]], value)
|
||||
tool_value = []
|
||||
for tool in value:
|
||||
entity = AgentToolEntity(
|
||||
provider_id=tool.get("provider_name", ""),
|
||||
provider_type=ToolProviderType.BUILT_IN,
|
||||
tool_name=tool.get("tool_name", ""),
|
||||
tool_parameters=tool.get("parameters", {}),
|
||||
plugin_unique_identifier=tool.get("plugin_unique_identifier", None),
|
||||
)
|
||||
|
||||
extra = tool.get("extra", {})
|
||||
|
||||
tool_runtime = ToolManager.get_agent_tool_runtime(
|
||||
self.tenant_id, self.app_id, entity, self.invoke_from
|
||||
)
|
||||
if tool_runtime.entity.description:
|
||||
tool_runtime.entity.description.llm = (
|
||||
extra.get("descrption", "") or tool_runtime.entity.description.llm
|
||||
)
|
||||
|
||||
tool_value.append(tool_runtime.entity.model_dump(mode="json"))
|
||||
value = tool_value
|
||||
if parameter.type == "model-selector":
|
||||
value = cast(dict[str, Any], value)
|
||||
model_instance = ModelManager().get_model_instance(
|
||||
tenant_id=self.tenant_id,
|
||||
provider=value.get("provider", ""),
|
||||
model_type=ModelType(value.get("model_type", "")),
|
||||
model=value.get("model", ""),
|
||||
)
|
||||
models = model_instance.model_type_instance.plugin_model_provider.declaration.models
|
||||
finded_model = next((model for model in models if model.model == value.get("model", "")), None)
|
||||
|
||||
value["entity"] = finded_model.model_dump(mode="json") if finded_model else None
|
||||
|
||||
result[parameter_name] = value
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def _extract_variable_selector_to_variable_mapping(
|
||||
cls,
|
||||
*,
|
||||
graph_config: Mapping[str, Any],
|
||||
node_id: str,
|
||||
node_data: BaseNodeData,
|
||||
) -> Mapping[str, Sequence[str]]:
|
||||
"""
|
||||
Extract variable selector to variable mapping
|
||||
:param graph_config: graph config
|
||||
:param node_id: node id
|
||||
:param node_data: node data
|
||||
:return:
|
||||
"""
|
||||
node_data = cast(AgentNodeData, node_data)
|
||||
result = {}
|
||||
for parameter_name in node_data.agent_parameters:
|
||||
input = node_data.agent_parameters[parameter_name]
|
||||
if input.type == "mixed":
|
||||
assert isinstance(input.value, str)
|
||||
selectors = VariableTemplateParser(input.value).extract_variable_selectors()
|
||||
for selector in selectors:
|
||||
result[selector.variable] = selector.value_selector
|
||||
elif input.type == "variable":
|
||||
result[parameter_name] = input.value
|
||||
elif input.type == "constant":
|
||||
pass
|
||||
|
||||
result = {node_id + "." + key: value for key, value in result.items()}
|
||||
|
||||
return result
|
||||
|
@ -1,81 +1,18 @@
|
||||
from typing import Any, Literal, Union
|
||||
|
||||
from pydantic import BaseModel, ValidationInfo, field_validator
|
||||
from pydantic import BaseModel
|
||||
|
||||
from core.tools.entities.tool_entities import ToolSelector
|
||||
from core.workflow.nodes.base.entities import BaseNodeData
|
||||
|
||||
|
||||
class AgentEntity(BaseModel):
|
||||
class AgentNodeData(BaseNodeData):
|
||||
agent_strategy_provider_name: str # redundancy
|
||||
agent_strategy_name: str
|
||||
agent_strategy_label: str # redundancy
|
||||
agent_configurations: dict[str, Any]
|
||||
plugin_unique_identifier: str
|
||||
|
||||
@field_validator("agent_configurations", mode="before")
|
||||
@classmethod
|
||||
def validate_agent_configurations(cls, value, values: ValidationInfo):
|
||||
if not isinstance(value, dict):
|
||||
raise ValueError("agent_configurations must be a dictionary")
|
||||
|
||||
for key in values.data.get("agent_configurations", {}):
|
||||
value = values.data.get("agent_configurations", {}).get(key)
|
||||
if isinstance(value, dict):
|
||||
# convert dict to ToolSelector
|
||||
return ToolSelector(**value)
|
||||
elif isinstance(value, ToolSelector):
|
||||
return value
|
||||
elif isinstance(value, list):
|
||||
# convert list[ToolSelector] to ToolSelector
|
||||
if all(isinstance(val, dict) for val in value):
|
||||
return [ToolSelector(**val) for val in value]
|
||||
elif all(isinstance(val, ToolSelector) for val in value):
|
||||
return value
|
||||
else:
|
||||
raise ValueError("value must be a list of ToolSelector")
|
||||
else:
|
||||
raise ValueError("value must be a dictionary or ToolSelector")
|
||||
|
||||
return value
|
||||
|
||||
|
||||
class AgentNodeData(BaseNodeData, AgentEntity):
|
||||
class AgentInput(BaseModel):
|
||||
# TODO: check this type
|
||||
value: Union[list[str], list[ToolSelector], Any]
|
||||
type: Literal["mixed", "variable", "constant"]
|
||||
|
||||
@field_validator("type", mode="before")
|
||||
@classmethod
|
||||
def check_type(cls, value, validation_info: ValidationInfo):
|
||||
typ = value
|
||||
value = validation_info.data.get("value")
|
||||
if typ == "mixed" and not isinstance(value, str):
|
||||
raise ValueError("value must be a string")
|
||||
elif typ == "variable":
|
||||
if not isinstance(value, list):
|
||||
raise ValueError("value must be a list")
|
||||
for val in value:
|
||||
if not isinstance(val, str):
|
||||
raise ValueError("value must be a list of strings")
|
||||
elif typ == "constant":
|
||||
if isinstance(value, list):
|
||||
# convert dict to ToolSelector
|
||||
if all(isinstance(val, dict) for val in value) or all(
|
||||
isinstance(val, ToolSelector) for val in value
|
||||
):
|
||||
return value
|
||||
else:
|
||||
raise ValueError("value must be a list of ToolSelector")
|
||||
elif isinstance(value, dict):
|
||||
# convert dict to ToolSelector
|
||||
return ToolSelector(**value)
|
||||
elif isinstance(value, ToolSelector):
|
||||
return value
|
||||
else:
|
||||
raise ValueError("value must be a list of ToolSelector")
|
||||
|
||||
return typ
|
||||
|
||||
agent_parameters: dict[str, AgentInput]
|
||||
|
@ -3,13 +3,13 @@ from core.plugin.manager.agent import PluginAgentManager
|
||||
|
||||
|
||||
def get_plugin_agent_strategy(
|
||||
tenant_id: str, plugin_unique_identifier: str, agent_strategy_provider_name: str, agent_strategy_name: str
|
||||
tenant_id: str, agent_strategy_provider_name: str, agent_strategy_name: str
|
||||
) -> PluginAgentStrategy:
|
||||
# TODO: use contexts to cache the agent provider
|
||||
manager = PluginAgentManager()
|
||||
agent_provider = manager.fetch_agent_strategy_provider(tenant_id, agent_strategy_provider_name)
|
||||
for agent_strategy in agent_provider.declaration.strategies:
|
||||
if agent_strategy.identity.name == agent_strategy_name:
|
||||
return PluginAgentStrategy(tenant_id, plugin_unique_identifier, agent_strategy)
|
||||
return PluginAgentStrategy(tenant_id, agent_strategy)
|
||||
|
||||
raise ValueError(f"Agent strategy {agent_strategy_name} not found")
|
||||
|
Loading…
x
Reference in New Issue
Block a user