chore: import upstream snapshot with attribution
tools_continuous_delivery / Private PyPI non-main branch release (push) Has been skipped
tools_continuous_delivery / Private PyPI main branch release (push) Failing after 2m42s
Publish Promptflow Doc / Build (push) Has been cancelled
Publish Promptflow Doc / Deploy (push) Has been cancelled
Flake8 Lint / flake8 (push) Has been cancelled
Spell check CI / Spell_Check (push) Has been cancelled
tools_continuous_delivery / Private PyPI non-main branch release (push) Has been skipped
tools_continuous_delivery / Private PyPI main branch release (push) Failing after 2m42s
Publish Promptflow Doc / Build (push) Has been cancelled
Publish Promptflow Doc / Deploy (push) Has been cancelled
Flake8 Lint / flake8 (push) Has been cancelled
Spell check CI / Spell_Check (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
import inspect
|
||||
from enum import Enum, EnumMeta
|
||||
from typing import Callable, Union, get_args, get_origin
|
||||
from promptflow.contracts.tool import ConnectionType, InputDefinition, ValueType, ToolType
|
||||
from promptflow.contracts.types import PromptTemplate
|
||||
|
||||
|
||||
def value_to_str(val):
|
||||
if val is inspect.Parameter.empty:
|
||||
# For empty case, default field will be skipped when dumping to json
|
||||
return None
|
||||
if val is None:
|
||||
# Dump default: "" in json to avoid UI validation error
|
||||
return ""
|
||||
if isinstance(val, Enum):
|
||||
return val.value
|
||||
return str(val)
|
||||
|
||||
|
||||
def resolve_annotation(anno) -> Union[str, list]:
|
||||
"""Resolve the union annotation to type list."""
|
||||
origin = get_origin(anno)
|
||||
if origin != Union:
|
||||
return anno
|
||||
# Optional[Type] is Union[Type, NoneType], filter NoneType out
|
||||
args = [arg for arg in get_args(anno) if arg != type(None)] # noqa: E721
|
||||
return args[0] if len(args) == 1 else args
|
||||
|
||||
|
||||
def param_to_definition(param, value_type) -> (InputDefinition, bool):
|
||||
default_value = param.default
|
||||
enum = None
|
||||
custom_type = None
|
||||
# Get value type and enum from default if no annotation
|
||||
if default_value is not inspect.Parameter.empty and value_type == inspect.Parameter.empty:
|
||||
value_type = default_value.__class__ if isinstance(default_value, Enum) else type(default_value)
|
||||
# Extract enum for enum class
|
||||
if isinstance(value_type, EnumMeta):
|
||||
enum = [str(option.value) for option in value_type]
|
||||
value_type = str
|
||||
is_connection = False
|
||||
if ConnectionType.is_connection_value(value_type):
|
||||
if ConnectionType.is_custom_strong_type(value_type):
|
||||
typ = ["CustomConnection"]
|
||||
custom_type = [value_type.__name__]
|
||||
else:
|
||||
typ = [value_type.__name__]
|
||||
is_connection = True
|
||||
elif isinstance(value_type, list):
|
||||
if not all(ConnectionType.is_connection_value(t) for t in value_type):
|
||||
typ = [ValueType.OBJECT]
|
||||
else:
|
||||
custom_connection_added = False
|
||||
typ = []
|
||||
custom_type = []
|
||||
for t in value_type:
|
||||
if ConnectionType.is_custom_strong_type(t):
|
||||
if not custom_connection_added:
|
||||
custom_connection_added = True
|
||||
typ.append("CustomConnection")
|
||||
custom_type.append(t.__name__)
|
||||
else:
|
||||
typ.append(t.__name__)
|
||||
is_connection = True
|
||||
else:
|
||||
typ = [ValueType.from_type(value_type)]
|
||||
return InputDefinition(type=typ, default=value_to_str(default_value),
|
||||
description=None, enum=enum, custom_type=custom_type), is_connection
|
||||
|
||||
|
||||
def function_to_interface(f: Callable, tool_type, initialize_inputs=None) -> tuple:
|
||||
sign = inspect.signature(f)
|
||||
all_inputs = {}
|
||||
input_defs = {}
|
||||
connection_types = []
|
||||
# Initialize the counter for prompt template
|
||||
prompt_template_count = 0
|
||||
# Collect all inputs from class and func
|
||||
if initialize_inputs:
|
||||
if any(k for k in initialize_inputs if k in sign.parameters):
|
||||
raise Exception(f'Duplicate inputs found from {f.__name__!r} and "__init__()"!')
|
||||
all_inputs = {**initialize_inputs}
|
||||
all_inputs.update(
|
||||
{
|
||||
k: v
|
||||
for k, v in sign.parameters.items()
|
||||
if k != "self" and v.kind != v.VAR_KEYWORD and v.kind != v.VAR_POSITIONAL # TODO: Handle these cases
|
||||
}
|
||||
)
|
||||
# Resolve inputs to definitions.
|
||||
for k, v in all_inputs.items():
|
||||
# Get value type from annotation
|
||||
value_type = resolve_annotation(v.annotation)
|
||||
if value_type is PromptTemplate:
|
||||
# custom llm tool has prompt template as input, skip it
|
||||
prompt_template_count += 1
|
||||
continue
|
||||
input_def, is_connection = param_to_definition(v, value_type)
|
||||
input_defs[k] = input_def
|
||||
if is_connection:
|
||||
connection_types.append(input_def.type)
|
||||
|
||||
# Check PromptTemplate input:
|
||||
# a. For custom llm tool, there should be exactly one PromptTemplate input
|
||||
# b. For python tool, PromptTemplate input is not supported
|
||||
if tool_type == ToolType.PYTHON and prompt_template_count > 0:
|
||||
raise Exception(f"Input of type 'PromptTemplate' not supported in python tool '{f.__name__}'. ")
|
||||
|
||||
if tool_type == ToolType.CUSTOM_LLM and prompt_template_count == 0:
|
||||
raise Exception(f"No input of type 'PromptTemplate' was found in custom llm tool '{f.__name__}'. ")
|
||||
|
||||
if tool_type == ToolType.CUSTOM_LLM and prompt_template_count > 1:
|
||||
raise Exception(f"Multiple inputs of type 'PromptTemplate' were found in '{f.__name__}'. "
|
||||
"Only one input of this type is expected.")
|
||||
|
||||
outputs = {}
|
||||
# Note: We don't have output definition now
|
||||
# outputs = {"output": OutputDefinition("output", [ValueType.from_type(type(sign.return_annotation))], "", True)}
|
||||
# if is_dataclass(sign.return_annotation):
|
||||
# for f in fields(sign.return_annotation):
|
||||
# outputs[f.name] = OutputDefinition(f.name, [ValueType.from_type(
|
||||
# type(getattr(sign.return_annotation, f.name)))], "", False)
|
||||
return input_defs, outputs, connection_types
|
||||
Reference in New Issue
Block a user