Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: AWEL flow supports dynamic parameters #1251

Merged
merged 2 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading