Skip to content

Commit

Permalink
feat: AWEL flow supports dynamic parameters (eosphoros-ai#1251)
Browse files Browse the repository at this point in the history
  • Loading branch information
fangyinc authored and Hopshine committed Sep 10, 2024
1 parent f16280e commit ea72980
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 35 deletions.
4 changes: 3 additions & 1 deletion dbgpt/app/scene/chat_knowledge/v1/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,9 @@ def parse_source_view(self, chunks_with_score: List):
}
)
references_list = list(references_dict.values())
references_ele.set("references", json.dumps(references_list))
references_ele.set(
"references", json.dumps(references_list, ensure_ascii=False)
)
html = ET.tostring(references_ele, encoding="utf-8")
reference = html.decode("utf-8")
return reference.replace("\\n", "")
Expand Down
8 changes: 7 additions & 1 deletion dbgpt/core/awel/flow/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
This module contains the classes and functions to build AWEL DAGs from serialized data.
"""

from ..util.parameter_util import ( # noqa: F401
BaseDynamicOptions,
FunctionDynamicOptions,
OptionValue,
)
from .base import ( # noqa: F401
IOField,
OperatorCategory,
OperatorType,
OptionValue,
Parameter,
ResourceCategory,
ResourceMetadata,
Expand All @@ -29,4 +33,6 @@
"ResourceType",
"OperatorType",
"IOField",
"BaseDynamicOptions",
"FunctionDynamicOptions",
]
39 changes: 22 additions & 17 deletions dbgpt/core/awel/flow/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typing import Any, Dict, List, Optional, Type, TypeVar, Union, cast

from dbgpt._private.pydantic import BaseModel, Field, ValidationError, root_validator
from dbgpt.core.awel.util.parameter_util import BaseDynamicOptions, OptionValue
from dbgpt.core.interface.serialization import Serializable

from .exceptions import FlowMetadataException, FlowParameterMetadataException
Expand Down Expand Up @@ -205,18 +206,6 @@ class ResourceType(str, Enum):
CLASS = "class"


class OptionValue(Serializable, BaseModel):
"""The option value of the parameter."""

label: str = Field(..., description="The label of the option")
name: str = Field(..., description="The name of the option")
value: Any = Field(..., description="The value of the option")

def to_dict(self) -> Dict:
"""Convert current metadata to json dict."""
return self.dict()


class ParameterType(str, Enum):
"""The type of the parameter."""

Expand Down Expand Up @@ -317,7 +306,7 @@ class Parameter(TypeMetadata, Serializable):
description: Optional[str] = Field(
None, description="The description of the parameter"
)
options: Optional[List[OptionValue]] = Field(
options: Optional[Union[BaseDynamicOptions, List[OptionValue]]] = Field(
None, description="The options of the parameter"
)
value: Optional[Any] = Field(
Expand Down Expand Up @@ -379,7 +368,7 @@ def build_from(
default: Optional[Union[DefaultParameterType, _MISSING_TYPE]] = _MISSING_VALUE,
placeholder: Optional[DefaultParameterType] = None,
description: Optional[str] = None,
options: Optional[List[OptionValue]] = None,
options: Optional[Union[BaseDynamicOptions, List[OptionValue]]] = None,
resource_type: ResourceType = ResourceType.INSTANCE,
):
"""Build the parameter from the type."""
Expand Down Expand Up @@ -435,7 +424,15 @@ def build_from_ui(cls, data: Dict) -> "Parameter":

def to_dict(self) -> Dict:
"""Convert current metadata to json dict."""
return self.dict()
dict_value = self.dict(exclude={"options"})
if not self.options:
dict_value["options"] = None
elif isinstance(self.options, BaseDynamicOptions):
values = self.options.option_values()
dict_value["options"] = [value.to_dict() for value in values]
else:
dict_value["options"] = [value.to_dict() for value in self.options]
return dict_value

def to_runnable_parameter(
self,
Expand Down Expand Up @@ -685,6 +682,14 @@ def get_origin_id(self) -> str:
split_ids = self.id.split("_")
return "_".join(split_ids[:-1])

def to_dict(self) -> Dict:
"""Convert current metadata to json dict."""
dict_value = self.dict(exclude={"parameters"})
dict_value["parameters"] = [
parameter.to_dict() for parameter in self.parameters
]
return dict_value


class ResourceMetadata(BaseMetadata, TypeMetadata):
"""The metadata of the resource."""
Expand Down Expand Up @@ -939,9 +944,9 @@ def get_registry_item(self, key: str) -> Optional[_RegistryItem]:
"""Get the registry item by the key."""
return self._registry.get(key)

def metadata_list(self) -> List[Union[ViewMetadata, ResourceMetadata]]:
def metadata_list(self):
"""Get the metadata list."""
return [item.metadata for item in self._registry.values()]
return [item.metadata.to_dict() for item in self._registry.values()]


_OPERATOR_REGISTRY: FlowRegistry = FlowRegistry()
Expand Down
70 changes: 70 additions & 0 deletions dbgpt/core/awel/util/parameter_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""The parameter utility."""

import inspect
from abc import ABC, abstractmethod
from typing import Any, Callable, Dict, List

from dbgpt._private.pydantic import BaseModel, Field, root_validator
from dbgpt.core.interface.serialization import Serializable

_DEFAULT_DYNAMIC_REGISTRY = {}


class OptionValue(Serializable, BaseModel):
"""The option value of the parameter."""

label: str = Field(..., description="The label of the option")
name: str = Field(..., description="The name of the option")
value: Any = Field(..., description="The value of the option")

def to_dict(self) -> Dict:
"""Convert current metadata to json dict."""
return self.dict()


class BaseDynamicOptions(Serializable, BaseModel, ABC):
"""The base dynamic options."""

@abstractmethod
def option_values(self) -> List[OptionValue]:
"""Return the option values of the parameter."""


class FunctionDynamicOptions(BaseDynamicOptions):
"""The function dynamic options."""

func: Callable[[], List[OptionValue]] = Field(
..., description="The function to generate the dynamic options"
)
func_id: str = Field(
..., description="The unique id of the function to generate the dynamic options"
)

def option_values(self) -> List[OptionValue]:
"""Return the option values of the parameter."""
return self.func()

@root_validator(pre=True)
def pre_fill(cls, values: Dict[str, Any]) -> Dict[str, Any]:
"""Pre fill the function id."""
func = values.get("func")
if func is None:
raise ValueError(
"The function to generate the dynamic options is required."
)
func_id = _generate_unique_id(func)
values["func_id"] = func_id
_DEFAULT_DYNAMIC_REGISTRY[func_id] = func
return values

def to_dict(self) -> Dict:
"""Convert current metadata to json dict."""
return {"func_id": self.func_id}


def _generate_unique_id(func: Callable) -> str:
if func.__name__ == "<lambda>":
func_id = f"lambda_{inspect.getfile(func)}_{inspect.getsourcelines(func)}"
else:
func_id = f"{func.__module__}.{func.__name__}"
return func_id
19 changes: 9 additions & 10 deletions dbgpt/serve/agent/team/layout/agent_operator_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,16 @@

from dbgpt._private.pydantic import root_validator
from dbgpt.agent.agents.agents_manage import agent_manage
from dbgpt.agent.agents.base_agent_new import ConversableAgent
from dbgpt.agent.agents.llm.llm import LLMConfig, LLMStrategyType
from dbgpt.agent.resource.resource_api import AgentResource, ResourceType
from dbgpt.core import LLMClient
from dbgpt.core.awel.flow import (
IOField,
OperatorCategory,
OperatorType,
FunctionDynamicOptions,
OptionValue,
Parameter,
ResourceCategory,
ViewMetadata,
register_resource,
)
from dbgpt.core.interface.operators.prompt_operator import CommonChatPromptTemplate


@register_resource(
Expand Down Expand Up @@ -115,6 +110,13 @@ def pre_fill(cls, values: Dict[str, Any]) -> Dict[str, Any]:
return values


def _agent_resource_option_values() -> List[OptionValue]:
return [
OptionValue(label=item["name"], name=item["name"], value=item["name"])
for item in agent_manage.list_agents()
]


@register_resource(
label="Awel Layout Agent",
name="agent_operator_agent",
Expand All @@ -126,10 +128,7 @@ def pre_fill(cls, values: Dict[str, Any]) -> Dict[str, Any]:
name="agent_profile",
type=str,
description="Which agent want use.",
options=[
OptionValue(label=item["name"], name=item["name"], value=item["name"])
for item in agent_manage.list_agents()
],
options=FunctionDynamicOptions(func=_agent_resource_option_values),
),
Parameter.build_from(
label="Role Name",
Expand Down
17 changes: 11 additions & 6 deletions dbgpt/serve/rag/operators/knowledge_space.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
)
from dbgpt.core.awel import JoinOperator, MapOperator
from dbgpt.core.awel.flow import (
FunctionDynamicOptions,
IOField,
OperatorCategory,
OperatorType,
Expand All @@ -29,6 +30,15 @@
from dbgpt.util.function_utils import rearrange_args_by_type


def _load_space_name() -> List[OptionValue]:
return [
OptionValue(label=space.name, name=space.name, value=space.name)
for space in knowledge_space_service.get_knowledge_space(
KnowledgeSpaceRequest()
)
]


class SpaceRetrieverOperator(MapOperator[IN, OUT]):
"""knowledge space retriever operator."""

Expand All @@ -51,12 +61,7 @@ class SpaceRetrieverOperator(MapOperator[IN, OUT]):
"Space Name",
"space_name",
str,
options=[
OptionValue(label=space.name, name=space.name, value=space.name)
for space in knowledge_space_service.get_knowledge_space(
KnowledgeSpaceRequest()
)
],
options=FunctionDynamicOptions(func=_load_space_name),
optional=False,
default=None,
description="space name.",
Expand Down

0 comments on commit ea72980

Please sign in to comment.