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

[Migration]: upgrade pydantic to v2 with sdk compatibility #1705

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c0be44e
Refactor (sdk/types): improve sdk types to be compatible with pydanti…
aybruhm May 25, 2024
0b30431
Refactor (sdk/types): renamed sdk types classmethod from __schema__ t…
aybruhm May 25, 2024
9c48ee2
Refactor (sdk/agenta_decorator): update function_signature and overri…
aybruhm May 25, 2024
283475f
Style: format code with black@23.12.0
aybruhm May 25, 2024
9191c4c
Build: bump pydantic version to 2.7.1
aybruhm May 25, 2024
d3fd8f3
Merge 'main' branch into 'feature/age-234-migration-upgrade-pydantic-…
aybruhm May 25, 2024
90b8f11
Build: sync poetry.lock after merge with main changes
aybruhm May 25, 2024
8c39923
Build (docker-assests): ensure installation of user application depen…
aybruhm May 25, 2024
f573878
Refactor (examples): added pydantic>=2 to baby_name_generator require…
aybruhm May 25, 2024
754ead3
Refactor (openapi_parser): update getBodySchemaName function to retur…
aybruhm May 25, 2024
b6f6dcc
Minor refactor (openapi_parser): remove redundant debug console state…
aybruhm May 27, 2024
30e1216
Refactor (agenta_decorator): added assertion to ensure length of para…
aybruhm May 27, 2024
4fcbda9
SDK: bump version to pre-release agenta@0.14.14a0
aybruhm May 27, 2024
043073d
Build: bump version to fastapi to @0.111.0 and sdk to @0.14.14a1
aybruhm May 27, 2024
7455759
SDK: bump pre-release version to @0.14.14a1
aybruhm May 27, 2024
5de34ae
SDK: move pre-release version to 0.15.0a0
aybruhm May 27, 2024
7c9b884
Build & CI: improve test compose to have agenta-web service and updat…
aybruhm May 27, 2024
8341a8f
Minor refactor (CI): update fourth step to make use of right backend …
aybruhm May 27, 2024
9c05908
SDK: revert pre-release version back to upstream version 0.14.14
aybruhm May 27, 2024
c89e83b
Merge branch 'main' into feature/age-234-migration-upgrade-pydantic-t…
aybruhm May 31, 2024
9a26c54
Refactor (llm_entrypoint): refactored entrypoint to compatibility wit…
aybruhm May 31, 2024
eb2e944
fix to the openapi parser
mmabrouk Jun 2, 2024
2af3202
format
mmabrouk Jun 2, 2024
62dcc4a
Merge pull request #1736 from Agenta-AI/prfix/pydanticv2
aybruhm Jun 3, 2024
4d3b256
Build (poetry): ensure support for fastapi>=0.100.0 & pydantic>=2
aybruhm Jun 3, 2024
1365783
Merge branch 'main' into feature/age-234-migration-upgrade-pydantic-t…
aybruhm Jun 3, 2024
a3a3825
Refactor (openapi_parser): ensure backward compatibility for pydantic…
aybruhm Jun 4, 2024
3892a10
Minor refactor (app): revert requirements back to its previous state
aybruhm Jun 4, 2024
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
6 changes: 3 additions & 3 deletions .github/workflows/run-frontend-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ jobs:
NEXT_PUBLIC_OPENAI_API_KEY: ${{ secrets.NEXT_PUBLIC_OPENAI_API_KEY }}
run: |
sudo apt install curl -y
docker-compose -f "docker-compose.yml" up -d --build
docker-compose -f "docker-compose.test.yml" up -d --build
mmabrouk marked this conversation as resolved.
Show resolved Hide resolved

- name: Restart Backend Service To Fetch Template(s)
run: docker container restart agenta_backend_1
run: docker container restart agenta-backend-test

- name: Wait for Backend Service
run: curl http://localhost/api/health/
Expand All @@ -51,7 +51,7 @@ jobs:
with:
node-version: 18

- name: Install Frontend Depedencies
- name: Install Frontend Dependencies
run: |
cd agenta-web/ && npm install

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ FROM public.ecr.aws/s2t9a1r1/agentaai/lambda_templates_public:main

COPY requirements.txt ${LAMBDA_TASK_ROOT}
RUN pip install --no-cache-dir --disable-pip-version-check -U agenta
RUN pip install --no-cache-dir --disable-pip-version-check -U -r requirements.txt
RUN pip install --no-cache-dir --disable-pip-version-check -r requirements.txt
mmabrouk marked this conversation as resolved.
Show resolved Hide resolved
RUN pip install --no-cache-dir --disable-pip-version-check mangum
COPY . ${LAMBDA_TASK_ROOT}

Expand Down
137 changes: 110 additions & 27 deletions agenta-cli/agenta/sdk/decorators/llm_entrypoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,15 +203,22 @@ async def execute_function(self, func: Callable[..., Any], *args, **func_params)
if isinstance(result, str):
return FuncResponse(message=result, latency=round(latency, 4)) # type: ignore
if isinstance(result, int) or isinstance(result, float):
return FuncResponse(message=str(result), latency=round(latency, 4))
return FuncResponse(
message=str(result),
usage=None,
cost=None,
latency=round(latency, 4),
)
if result is None:
return FuncResponse(
message="Function executed successfully, but did return None. \n Are you sure you did not forget to return a value?",
usage=None,
cost=None,
latency=round(latency, 4),
)
except Exception as e:
self.handle_exception(e)
return FuncResponse(message="Unexpected error occurred when calling the @entrypoing decorated function", latency=0) # type: ignore
return FuncResponse(message="Unexpected error occurred when calling the @entrypoint decorated function", latency=0) # type: ignore

def handle_exception(self, e: Exception):
"""Handle exceptions."""
Expand All @@ -237,7 +244,7 @@ def update_wrapper_signature(

wrapper_signature = inspect.signature(wrapper)
wrapper_signature = wrapper_signature.replace(parameters=updated_params)
wrapper.__signature__ = wrapper_signature
wrapper.__signature__ = wrapper_signature # type: ignore

def update_function_signature(
self,
Expand All @@ -248,7 +255,7 @@ def update_function_signature(
) -> None:
"""Update the function signature to include new parameters."""

updated_params = []
updated_params: List[inspect.Parameter] = []
self.add_config_params_to_parser(updated_params, config_params)
self.add_func_params_to_parser(updated_params, func_signature, ingestible_files)
self.update_wrapper_signature(wrapper, updated_params)
Expand All @@ -260,7 +267,8 @@ def update_deployed_function_signature(
ingestible_files: Dict[str, inspect.Parameter],
) -> None:
"""Update the function signature to include new parameters."""
updated_params = []

updated_params: List[inspect.Parameter] = []
self.add_func_params_to_parser(updated_params, func_signature, ingestible_files)
for param in [
"config",
Expand All @@ -281,12 +289,19 @@ def add_config_params_to_parser(
) -> None:
"""Add configuration parameters to function signature."""
for name, param in config_params.items():
assert (
len(param.__class__.__bases__) == 1
), f"Inherited standard type of {param.__class__} needs to be one."
updated_params.append(
inspect.Parameter(
name,
inspect.Parameter.KEYWORD_ONLY,
default=Body(param),
annotation=Optional[type(param)],
annotation=param.__class__.__bases__[
0
], # determines and get the base (parent/inheritance) type of the sdk-type at run-time. \
# E.g __class__ is ag.MessagesInput() and accessing it parent type will return (<class 'list'>,), \
# thus, why we are accessing the first item.
)
)

Expand All @@ -303,12 +318,19 @@ def add_func_params_to_parser(
inspect.Parameter(name, param.kind, annotation=UploadFile)
)
else:
assert (
len(param.default.__class__.__bases__) == 1
), f"Inherited standard type of {param.default.__class__} needs to be one."
updated_params.append(
inspect.Parameter(
name,
inspect.Parameter.KEYWORD_ONLY,
default=Body(..., embed=True),
annotation=param.annotation,
annotation=param.default.__class__.__bases__[
0
], # determines and get the base (parent/inheritance) type of the sdk-type at run-time. \
# E.g __class__ is ag.MessagesInput() and accessing it parent type will return (<class 'list'>,), \
# thus, why we are accessing the first item.
)
)

Expand Down Expand Up @@ -358,7 +380,7 @@ def handle_terminal_run(
f"--{name}",
type=str,
default=param.default,
choices=param.choices,
choices=param.choices, # type: ignore
)
else:
parser.add_argument(
Expand Down Expand Up @@ -420,7 +442,9 @@ def override_schema(
params (dict(param_name, param_val)): The dictionary of the parameters for the function
"""

def find_in_schema(schema: dict, param_name: str, xparam: str):
def find_in_schema(
schema_type_properties: dict, schema: dict, param_name: str, xparam: str
):
"""Finds a parameter in the schema based on its name and x-parameter value"""
for _, value in schema.items():
value_title_lower = str(value.get("title")).lower()
Expand All @@ -432,9 +456,17 @@ def find_in_schema(schema: dict, param_name: str, xparam: str):

if (
isinstance(value, dict)
and value.get("x-parameter") == xparam
and schema_type_properties.get("x-parameter") == xparam
and value_title == param_name
):
# this will update the default type schema with the properties gotten
# from the schema type (param_val) __schema_properties__ classmethod
for type_key, type_value in schema_type_properties.items():
# BEFORE:
# value = {'temperature': {'title': 'Temperature'}}
value[type_key] = type_value
# AFTER:
# value = {'temperature': { "type": "number", "title": "Temperature", "x-parameter": "float" }}
return value

schema_to_override = openapi_schema["components"]["schemas"][
Expand All @@ -443,17 +475,26 @@ def find_in_schema(schema: dict, param_name: str, xparam: str):
for param_name, param_val in params.items():
if isinstance(param_val, GroupedMultipleChoiceParam):
subschema = find_in_schema(
schema_to_override, param_name, "grouped_choice"
param_val.__schema_type_properties__(),
schema_to_override,
param_name,
"grouped_choice",
)
assert (
subschema
), f"GroupedMultipleChoiceParam '{param_name}' is in the parameters but could not be found in the openapi.json"
subschema["choices"] = param_val.choices
subschema["default"] = param_val.default
subschema["choices"] = param_val.choices # type: ignore
subschema["default"] = param_val.default # type: ignore

if isinstance(param_val, MultipleChoiceParam):
subschema = find_in_schema(schema_to_override, param_name, "choice")
subschema = find_in_schema(
param_val.__schema_type_properties__(),
schema_to_override,
param_name,
"choice",
)
default = str(param_val)
param_choices = param_val.choices
param_choices = param_val.choices # type: ignore
choices = (
[default] + param_choices
if param_val not in param_choices
Expand All @@ -463,37 +504,79 @@ def find_in_schema(schema: dict, param_name: str, xparam: str):
subschema["default"] = (
default if default in param_choices else choices[0]
)

if isinstance(param_val, FloatParam):
subschema = find_in_schema(schema_to_override, param_name, "float")
subschema["minimum"] = param_val.minval
subschema["maximum"] = param_val.maxval
subschema = find_in_schema(
param_val.__schema_type_properties__(),
schema_to_override,
param_name,
"float",
)
subschema["minimum"] = param_val.minval # type: ignore
subschema["maximum"] = param_val.maxval # type: ignore
subschema["default"] = param_val

if isinstance(param_val, IntParam):
subschema = find_in_schema(schema_to_override, param_name, "int")
subschema["minimum"] = param_val.minval
subschema["maximum"] = param_val.maxval
subschema = find_in_schema(
param_val.__schema_type_properties__(),
schema_to_override,
param_name,
"int",
)
subschema["minimum"] = param_val.minval # type: ignore
subschema["maximum"] = param_val.maxval # type: ignore
subschema["default"] = param_val

if (
isinstance(param_val, inspect.Parameter)
and param_val.annotation is DictInput
):
subschema = find_in_schema(schema_to_override, param_name, "dict")
subschema = find_in_schema(
param_val.annotation.__schema_type_properties__(),
schema_to_override,
param_name,
"dict",
)
subschema["default"] = param_val.default["default_keys"]

if isinstance(param_val, TextParam):
subschema = find_in_schema(schema_to_override, param_name, "text")
subschema = find_in_schema(
param_val.__schema_type_properties__(),
schema_to_override,
param_name,
"text",
)
subschema["default"] = param_val

if (
isinstance(param_val, inspect.Parameter)
and param_val.annotation is MessagesInput
):
subschema = find_in_schema(schema_to_override, param_name, "messages")
subschema = find_in_schema(
param_val.annotation.__schema_type_properties__(),
schema_to_override,
param_name,
"messages",
)
subschema["default"] = param_val.default

if (
isinstance(param_val, inspect.Parameter)
and param_val.annotation is FileInputURL
):
subschema = find_in_schema(schema_to_override, param_name, "file_url")
subschema = find_in_schema(
param_val.annotation.__schema_type_properties__(),
schema_to_override,
param_name,
"file_url",
)
subschema["default"] = "https://example.com"

if isinstance(param_val, BinaryParam):
subschema = find_in_schema(schema_to_override, param_name, "bool")
subschema["default"] = param_val.default
subschema = find_in_schema(
param_val.__schema_type_properties__(),
schema_to_override,
param_name,
"bool",
)
subschema["default"] = param_val.default # type: ignore
Loading
Loading