feat: Add flow2.0 examples

This commit is contained in:
Fangyin Cheng
2024-08-05 18:08:02 +08:00
parent 2387aaf34a
commit 79fbee7275
7 changed files with 786 additions and 123 deletions

View File

@@ -19,6 +19,7 @@ from dbgpt.core.awel.util.parameter_util import BaseDynamicOptions, OptionValue
from dbgpt.core.interface.serialization import Serializable
from .exceptions import FlowMetadataException, FlowParameterMetadataException
from .ui import UIComponent
_TYPE_REGISTRY: Dict[str, Type] = {}
@@ -136,6 +137,7 @@ _OPERATOR_CATEGORY_DETAIL = {
"agent": _CategoryDetail("Agent", "The agent operator"),
"rag": _CategoryDetail("RAG", "The RAG operator"),
"experimental": _CategoryDetail("EXPERIMENTAL", "EXPERIMENTAL operator"),
"example": _CategoryDetail("Example", "Example operator"),
}
@@ -151,6 +153,7 @@ class OperatorCategory(str, Enum):
AGENT = "agent"
RAG = "rag"
EXPERIMENTAL = "experimental"
EXAMPLE = "example"
def label(self) -> str:
"""Get the label of the category."""
@@ -193,6 +196,7 @@ _RESOURCE_CATEGORY_DETAIL = {
"embeddings": _CategoryDetail("Embeddings", "The embeddings resource"),
"rag": _CategoryDetail("RAG", "The resource"),
"vector_store": _CategoryDetail("Vector Store", "The vector store resource"),
"example": _CategoryDetail("Example", "The example resource"),
}
@@ -209,6 +213,7 @@ class ResourceCategory(str, Enum):
EMBEDDINGS = "embeddings"
RAG = "rag"
VECTOR_STORE = "vector_store"
EXAMPLE = "example"
def label(self) -> str:
"""Get the label of the category."""
@@ -343,6 +348,9 @@ class Parameter(TypeMetadata, Serializable):
alias: Optional[List[str]] = Field(
None, description="The alias of the parameter(Compatible with old version)"
)
ui: Optional[UIComponent] = Field(
None, description="The UI component of the parameter"
)
@model_validator(mode="before")
@classmethod
@@ -398,6 +406,7 @@ class Parameter(TypeMetadata, Serializable):
label: str,
name: str,
type: Type,
is_list: bool = False,
optional: bool = False,
default: Optional[Union[DefaultParameterType, _MISSING_TYPE]] = _MISSING_VALUE,
placeholder: Optional[DefaultParameterType] = None,
@@ -405,6 +414,7 @@ class Parameter(TypeMetadata, Serializable):
options: Optional[Union[BaseDynamicOptions, List[OptionValue]]] = None,
resource_type: ResourceType = ResourceType.INSTANCE,
alias: Optional[List[str]] = None,
ui: Optional[UIComponent] = None,
):
"""Build the parameter from the type."""
type_name = type.__qualname__
@@ -419,6 +429,7 @@ class Parameter(TypeMetadata, Serializable):
name=name,
type_name=type_name,
type_cls=type_cls,
is_list=is_list,
category=category.value,
resource_type=resource_type,
optional=optional,
@@ -427,6 +438,7 @@ class Parameter(TypeMetadata, Serializable):
description=description or label,
options=options,
alias=alias,
ui=ui,
)
@classmethod
@@ -456,11 +468,12 @@ class Parameter(TypeMetadata, Serializable):
description=data["description"],
options=data["options"],
value=data["value"],
ui=data.get("ui"),
)
def to_dict(self) -> Dict:
"""Convert current metadata to json dict."""
dict_value = model_to_dict(self, exclude={"options", "alias"})
dict_value = model_to_dict(self, exclude={"options", "alias", "ui"})
if not self.options:
dict_value["options"] = None
elif isinstance(self.options, BaseDynamicOptions):
@@ -468,6 +481,9 @@ class Parameter(TypeMetadata, Serializable):
dict_value["options"] = [value.to_dict() for value in values]
else:
dict_value["options"] = [value.to_dict() for value in self.options]
if self.ui:
dict_value["ui"] = self.ui.to_dict()
return dict_value
def get_dict_options(self) -> Optional[List[Dict]]:

View File

@@ -1,8 +1,9 @@
"""UI components for AWEL flow."""
from typing import Any, Dict, List, Literal, Optional
from typing import Any, Dict, List, Literal, Optional, Union
from dbgpt._private.pydantic import BaseModel, Field
from dbgpt._private.pydantic import BaseModel, Field, model_to_dict
from dbgpt.core.interface.serialization import Serializable
from .exceptions import FlowUIComponentException
@@ -36,37 +37,6 @@ class RefreshableMixin(BaseModel):
)
class UIComponent(RefreshableMixin, BaseModel):
"""UI component."""
class UIRange(BaseModel):
"""UI range."""
min: int | float | str | None = Field(None, description="Minimum value")
max: int | float | str | None = Field(None, description="Maximum value")
step: int | float | str | None = Field(None, description="Step value")
format: str | None = Field(None, description="Format")
ui_type: _UI_TYPE = Field(..., description="UI component type")
disabled: bool = Field(
False,
description="Whether the component is disabled",
)
def check_parameter(self, parameter_dict: Dict[str, Any]):
"""Check parameter.
Raises:
FlowUIParameterException: If the parameter is invalid.
"""
def _check_options(self, options: Dict[str, Any]):
"""Check options."""
if not options:
raise FlowUIComponentException("options is required", self.ui_type)
class StatusMixin(BaseModel):
"""Status mixin."""
@@ -76,40 +46,6 @@ class StatusMixin(BaseModel):
)
class RangeMixin(BaseModel):
"""Range mixin."""
ui_range: Optional[UIComponent.UIRange] = Field(
None,
description="Range for the component",
)
class InputMixin(BaseModel):
"""Input mixin."""
class Count(BaseModel):
"""Count."""
show: Optional[bool] = Field(
None,
description="Whether to show count",
)
max: Optional[int] = Field(
None,
description="The maximum count",
)
exceed_strategy: Optional[Literal["cut", "warning"]] = Field(
None,
description="The strategy when the count exceeds",
)
count: Optional[Count] = Field(
None,
description="Count configuration",
)
class PanelEditorMixin(BaseModel):
"""Edit the content in the panel."""
@@ -126,19 +62,62 @@ class PanelEditorMixin(BaseModel):
)
editor: Optional[Editor] = Field(
None,
default_factory=lambda: PanelEditorMixin.Editor(width=800, height=400),
description="The editor configuration",
)
class UICascader(StatusMixin, UIComponent):
class UIComponent(RefreshableMixin, Serializable, BaseModel):
"""UI component."""
class UIAttribute(StatusMixin, BaseModel):
"""Base UI attribute."""
disabled: bool = Field(
False,
description="Whether the component is disabled",
)
ui_type: _UI_TYPE = Field(..., description="UI component type")
attr: Optional[UIAttribute] = Field(
None,
description="The attributes of the component",
)
def check_parameter(self, parameter_dict: Dict[str, Any]):
"""Check parameter.
Raises:
FlowUIParameterException: If the parameter is invalid.
"""
def _check_options(self, options: Dict[str, Any]):
"""Check options."""
if not options:
raise FlowUIComponentException("options is required", self.ui_type)
def to_dict(self) -> Dict:
"""Convert current metadata to json dict."""
return model_to_dict(self)
class UICascader(UIComponent):
"""Cascader component."""
class UIAttribute(UIComponent.UIAttribute):
"""Cascader attribute."""
show_search: bool = Field(
False,
description="Whether to show search input",
)
ui_type: Literal["cascader"] = Field("cascader", frozen=True)
show_search: bool = Field(
False,
description="Whether to show search input",
attr: Optional[UIAttribute] = Field(
None,
description="The attributes of the component",
)
def check_parameter(self, parameter_dict: Dict[str, Any]):
@@ -163,53 +142,81 @@ class UICheckbox(UIComponent):
self._check_options(parameter_dict.get("options", {}))
class UIDatePicker(StatusMixin, RangeMixin, UIComponent):
class UIDatePicker(UIComponent):
"""Date picker component."""
class UIAttribute(UIComponent.UIAttribute):
"""Date picker attribute."""
placement: Optional[
Literal["topLeft", "topRight", "bottomLeft", "bottomRight"]
] = Field(
None,
description="The position of the picker panel, None means bottomLeft",
)
ui_type: Literal["date_picker"] = Field("date_picker", frozen=True)
placement: Optional[
Literal["topLeft", "topRight", "bottomLeft", "bottomRight"]
] = Field(
attr: Optional[UIAttribute] = Field(
None,
description="The position of the picker panel, None means bottomLeft",
description="The attributes of the component",
)
class UIInput(StatusMixin, InputMixin, UIComponent):
class UIInput(UIComponent):
"""Input component."""
class UIAttribute(UIComponent.UIAttribute):
"""Input attribute."""
prefix: Optional[str] = Field(
None,
description="The prefix, icon or text",
examples=["$", "icon:UserOutlined"],
)
suffix: Optional[str] = Field(
None,
description="The suffix, icon or text",
examples=["$", "icon:SearchOutlined"],
)
show_count: Optional[bool] = Field(
None,
description="Whether to show count",
)
maxlength: Optional[int] = Field(
None,
description="The maximum length of the input",
)
ui_type: Literal["input"] = Field("input", frozen=True)
prefix: Optional[str] = Field(
attr: Optional[UIAttribute] = Field(
None,
description="The prefix, icon or text",
examples=["$", "icon:UserOutlined"],
)
suffix: Optional[str] = Field(
None,
description="The suffix, icon or text",
examples=["$", "icon:SearchOutlined"],
description="The attributes of the component",
)
class UITextArea(PanelEditorMixin, UIInput):
"""Text area component."""
class AutoSize(BaseModel):
"""Auto size configuration."""
min_rows: Optional[int] = Field(
None,
description="The minimum number of rows",
)
max_rows: Optional[int] = Field(
None,
description="The maximum number of rows",
)
ui_type: Literal["text_area"] = Field("text_area", frozen=True) # type: ignore
auto_size: Optional[bool] = Field(
autosize: Optional[Union[bool, AutoSize]] = Field(
None,
description="Whether the height of the textarea automatically adjusts based "
"on the content",
)
min_rows: Optional[int] = Field(
None,
description="The minimum number of rows",
)
max_rows: Optional[int] = Field(
None,
description="The maximum number of rows",
)
class UIAutoComplete(UIInput):
@@ -220,44 +227,73 @@ class UIAutoComplete(UIInput):
)
class UISlider(RangeMixin, UIComponent):
class UISlider(UIComponent):
"""Slider component."""
class UIAttribute(UIComponent.UIAttribute):
"""Slider attribute."""
min: Optional[int | float] = Field(
None,
description="The minimum value",
)
max: Optional[int | float] = Field(
None,
description="The maximum value",
)
step: Optional[int | float] = Field(
None,
description="The step of the slider",
)
ui_type: Literal["slider"] = Field("slider", frozen=True)
show_input: bool = Field(
False, description="Whether to display the value in a input component"
)
attr: Optional[UIAttribute] = Field(
None,
description="The attributes of the component",
)
class UITimePicker(StatusMixin, UIComponent):
class UITimePicker(UIComponent):
"""Time picker component."""
class UIAttribute(UIComponent.UIAttribute):
"""Time picker attribute."""
format: Optional[str] = Field(
None,
description="The format of the time",
examples=["HH:mm:ss", "HH:mm"],
)
hour_step: Optional[int] = Field(
None,
description="The step of the hour input",
)
minute_step: Optional[int] = Field(
None,
description="The step of the minute input",
)
second_step: Optional[int] = Field(
None,
description="The step of the second input",
)
ui_type: Literal["time_picker"] = Field("time_picker", frozen=True)
format: Optional[str] = Field(
attr: Optional[UIAttribute] = Field(
None,
description="The format of the time",
examples=["HH:mm:ss", "HH:mm"],
)
hour_step: Optional[int] = Field(
None,
description="The step of the hour input",
)
minute_step: Optional[int] = Field(
None,
description="The step of the minute input",
)
second_step: Optional[int] = Field(
None,
description="The step of the second input",
description="The attributes of the component",
)
class UITreeSelect(StatusMixin, UIComponent):
class UITreeSelect(UICascader):
"""Tree select component."""
ui_type: Literal["tree_select"] = Field("tree_select", frozen=True)
ui_type: Literal["tree_select"] = Field("tree_select", frozen=True) # type: ignore
def check_parameter(self, parameter_dict: Dict[str, Any]):
"""Check parameter."""
@@ -271,19 +307,24 @@ class UITreeSelect(StatusMixin, UIComponent):
)
class UIUpload(StatusMixin, UIComponent):
class UIUpload(UIComponent):
"""Upload component."""
class UIAttribute(UIComponent.UIAttribute):
"""Upload attribute."""
max_count: Optional[int] = Field(
None,
description="The maximum number of files that can be uploaded",
)
ui_type: Literal["upload"] = Field("upload", frozen=True)
max_file_size: Optional[int] = Field(
None,
description="The maximum size of the file, in bytes",
)
max_count: Optional[int] = Field(
None,
description="The maximum number of files that can be uploaded",
)
file_types: Optional[List[str]] = Field(
None,
description="The file types that can be accepted",
@@ -346,3 +387,13 @@ class UICodeEditor(UITextArea):
"python",
description="The language of the code",
)
class DefaultUITextArea(UITextArea):
"""Default text area component."""
autosize: Union[bool, UITextArea.AutoSize] = Field(
default_factory=lambda: UITextArea.AutoSize(min_rows=2, max_rows=40),
description="Whether the height of the textarea automatically adjusts based "
"on the content",
)

View File

@@ -2,7 +2,7 @@
import inspect
from abc import ABC, abstractmethod
from typing import Any, Callable, Dict, List
from typing import Any, Callable, Dict, List, Optional
from dbgpt._private.pydantic import BaseModel, Field, model_validator
from dbgpt.core.interface.serialization import Serializable
@@ -16,6 +16,9 @@ class OptionValue(Serializable, BaseModel):
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")
children: Optional[List["OptionValue"]] = Field(
None, description="The children of the option"
)
def to_dict(self) -> Dict:
"""Convert current metadata to json dict."""

View File

@@ -24,6 +24,7 @@ from dbgpt.core.awel.flow import (
OperatorType,
Parameter,
ViewMetadata,
ui,
)
from dbgpt.core.interface.llm import (
LLMClient,
@@ -69,6 +70,10 @@ class RequestBuilderOperator(MapOperator[RequestInput, ModelRequest]):
optional=True,
default=None,
description=_("The temperature of the model request."),
ui=ui.UISlider(
show_input=True,
attr=ui.UISlider.UIAttribute(min=0.0, max=2.0, step=0.1),
),
),
Parameter.build_from(
_("Max New Tokens"),

View File

@@ -1,4 +1,5 @@
"""The prompt operator."""
from abc import ABC
from typing import Any, Dict, List, Optional, Union
@@ -18,6 +19,7 @@ from dbgpt.core.awel.flow import (
ResourceCategory,
ViewMetadata,
register_resource,
ui,
)
from dbgpt.core.interface.message import BaseMessage
from dbgpt.core.interface.operators.llm_operator import BaseLLM
@@ -48,6 +50,7 @@ from dbgpt.util.i18n_utils import _
optional=True,
default="You are a helpful AI Assistant.",
description=_("The system message."),
ui=ui.DefaultUITextArea(),
),
Parameter.build_from(
label=_("Message placeholder"),
@@ -65,6 +68,7 @@ from dbgpt.util.i18n_utils import _
default="{user_input}",
placeholder="{user_input}",
description=_("The human message."),
ui=ui.DefaultUITextArea(),
),
],
)

View File

@@ -209,7 +209,7 @@ async def query_page(
@router.get("/nodes", dependencies=[Depends(check_api_key)])
async def get_nodes() -> Result[List[Union[ViewMetadata, ResourceMetadata]]]:
async def get_nodes():
"""Get the operator or resource nodes
Returns:
@@ -218,7 +218,8 @@ async def get_nodes() -> Result[List[Union[ViewMetadata, ResourceMetadata]]]:
"""
from dbgpt.core.awel.flow.base import _OPERATOR_REGISTRY
return Result.succ(_OPERATOR_REGISTRY.metadata_list())
metadata_list = _OPERATOR_REGISTRY.metadata_list()
return Result.succ(metadata_list)
def init_endpoints(system_app: SystemApp) -> None: