From 9c38da1a42c29da7218dac9696b1d67d9ced2538 Mon Sep 17 00:00:00 2001 From: Vivek Gurudutt K <127002789+VivekGuruduttK28@users.noreply.github.com> Date: Fri, 4 Oct 2024 05:40:54 +0530 Subject: [PATCH 1/3] Enhanced the error messages (#575) This PR is to fix the issue mentioned in #572 I have made the required changes in the files create_agent.py, create_or_update_agent.py and delete_agent.py. I have added descriptions to QueryException, ValidationError and TypeError. ---- > [!IMPORTANT] > Enhanced error messages for exceptions in agent-related files to provide detailed guidance on potential issues. > > - **Error Messages**: > - Enhanced error messages for `QueryException`, `ValidationError`, and `TypeError` in `create_agent.py`, `create_or_update_agent.py`, and `delete_agent.py`. > - `QueryException`: Now includes a detailed message about potential causes related to database query failures. > - `ValidationError`: Provides guidance on checking input data for missing or incorrect fields. > - `TypeError`: Describes type mismatch issues and suggests reviewing input data types. > > This description was created by [Ellipsis](https://www.ellipsis.dev?ref=julep-ai%2Fjulep&utm_source=github&utm_medium=referral) for 049d7eea196b74158e4fc4d83049656c7e7fa12e. It will automatically update as commits are pushed. Co-authored-by: Diwank Singh Tomer --- agents-api/agents_api/models/agent/create_agent.py | 6 +++--- .../agents_api/models/agent/create_or_update_agent.py | 6 +++--- agents-api/agents_api/models/agent/delete_agent.py | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/agents-api/agents_api/models/agent/create_agent.py b/agents-api/agents_api/models/agent/create_agent.py index 73be4ec77..546d0d441 100644 --- a/agents-api/agents_api/models/agent/create_agent.py +++ b/agents-api/agents_api/models/agent/create_agent.py @@ -32,9 +32,9 @@ in str(e): lambda *_: HTTPException( detail="developer not found", status_code=403 ), - QueryException: partialclass(HTTPException, status_code=400), - ValidationError: partialclass(HTTPException, status_code=400), - TypeError: partialclass(HTTPException, status_code=400), + QueryException: partialclass(HTTPException, status_code=400, detail="A database query failed to return the expected results. This might occur if the requested resource doesn't exist or your query parameters are incorrect."), + ValidationError: partialclass(HTTPException, status_code=400, detail="Input validation failed. Please check the provided data for missing or incorrect fields, and ensure it matches the required format."), + TypeError: partialclass(HTTPException, status_code=400, detail="A type mismatch occurred. This likely means the data provided is of an incorrect type (e.g., string instead of integer). Please review the input and try again."), } ) @wrap_in_class( diff --git a/agents-api/agents_api/models/agent/create_or_update_agent.py b/agents-api/agents_api/models/agent/create_or_update_agent.py index 156c8ae61..17e01fccb 100644 --- a/agents-api/agents_api/models/agent/create_or_update_agent.py +++ b/agents-api/agents_api/models/agent/create_or_update_agent.py @@ -27,9 +27,9 @@ @rewrap_exceptions( { - QueryException: partialclass(HTTPException, status_code=400), - ValidationError: partialclass(HTTPException, status_code=400), - TypeError: partialclass(HTTPException, status_code=400), + QueryException: partialclass(HTTPException, status_code=400,detail="A database query failed to return the expected results. This might occur if the requested resource doesn't exist or your query parameters are incorrect."), + ValidationError: partialclass(HTTPException, status_code=400,detail="Input validation failed. Please check the provided data for missing or incorrect fields, and ensure it matches the required format."), + TypeError: partialclass(HTTPException, status_code=400,detail="A type mismatch occurred. This likely means the data provided is of an incorrect type (e.g., string instead of integer). Please review the input and try again."), } ) @wrap_in_class( diff --git a/agents-api/agents_api/models/agent/delete_agent.py b/agents-api/agents_api/models/agent/delete_agent.py index 926007bdf..cb6b16718 100644 --- a/agents-api/agents_api/models/agent/delete_agent.py +++ b/agents-api/agents_api/models/agent/delete_agent.py @@ -32,9 +32,9 @@ in e.resp["display"]: lambda *_: HTTPException( detail="developer not found or doesnt own resource", status_code=404 ), - QueryException: partialclass(HTTPException, status_code=400), - ValidationError: partialclass(HTTPException, status_code=400), - TypeError: partialclass(HTTPException, status_code=400), + QueryException: partialclass(HTTPException, status_code=400,detail="A database query failed to return the expected results. This might occur if the requested resource doesn't exist or your query parameters are incorrect."), + ValidationError: partialclass(HTTPException, status_code=400,detail="Input validation failed. Please check the provided data for missing or incorrect fields, and ensure it matches the required format."), + TypeError: partialclass(HTTPException, status_code=400,detail="A type mismatch occurred. This likely means the data provided is of an incorrect type (e.g., string instead of integer). Please review the input and try again."), } ) @wrap_in_class( From 51f5e65cf7095c09219dbe60080898b530f36dac Mon Sep 17 00:00:00 2001 From: Diwank Singh Tomer Date: Thu, 3 Oct 2024 20:11:31 -0400 Subject: [PATCH 2/3] fix(agents-api): Remove unnecessary 'type' property from tool type definition (#574) Signed-off-by: Diwank Singh Tomer ---- > [!IMPORTANT] > Removed 'type' property from tool classes and added computed 'type' property using a function in `Tools.py`, `openapi_model.py`, and `tasks.py`. > > - **Behavior**: > - Removed `type` property from `CreateToolRequest`, `NamedToolChoice`, `PatchToolRequest`, `Tool`, and `UpdateToolRequest` in `Tools.py`. > - Added computed `type` property to `TaskTool`, `Tool`, `UpdateToolRequest`, and `PatchToolRequest` in `openapi_model.py`. > - Updated `task_to_spec()` in `tasks.py` to use computed `type` property. > - **Misc**: > - Removed `type` property from `Tool` and `NamedToolChoice` in `models.tsp`. > > This description was created by [Ellipsis](https://www.ellipsis.dev?ref=julep-ai%2Fjulep&utm_source=github&utm_medium=referral) for 289e87eff9a220b2c4430959f989cca471e36cf9. It will automatically update as commits are pushed. Signed-off-by: Diwank Singh Tomer --- agents-api/agents_api/autogen/Tools.py | 31 ------ .../agents_api/autogen/openapi_model.py | 97 +++++-------------- .../agents_api/common/protocol/tasks.py | 27 ++++-- typespec/tools/models.tsp | 14 --- 4 files changed, 40 insertions(+), 129 deletions(-) diff --git a/agents-api/agents_api/autogen/Tools.py b/agents-api/agents_api/autogen/Tools.py index c2d4199a2..8227e5759 100644 --- a/agents-api/agents_api/autogen/Tools.py +++ b/agents-api/agents_api/autogen/Tools.py @@ -33,10 +33,6 @@ class CreateToolRequest(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - type: Literal["function", "integration", "system", "api_call"] = "function" - """ - Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) - """ name: Annotated[str, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] """ Name of the tool (must be unique for this agent and a valid python identifier string ) @@ -168,10 +164,6 @@ class NamedToolChoice(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - type: Literal["function", "integration", "system", "api_call"] - """ - Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) - """ function: FunctionCallOption | None = None @@ -183,10 +175,6 @@ class PatchToolRequest(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - type: Literal["function", "integration", "system", "api_call"] = "function" - """ - Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) - """ name: Annotated[str | None, Field(None, max_length=40, pattern="^[^\\W0-9]\\w*$")] """ Name of the tool (must be unique for this agent and a valid python identifier string ) @@ -247,10 +235,6 @@ class Tool(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - type: Literal["function", "integration", "system", "api_call"] = "function" - """ - Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) - """ name: Annotated[str, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] """ Name of the tool (must be unique for this agent and a valid python identifier string ) @@ -291,10 +275,6 @@ class UpdateToolRequest(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - type: Literal["function", "integration", "system", "api_call"] = "function" - """ - Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) - """ name: Annotated[str, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] """ Name of the tool (must be unique for this agent and a valid python identifier string ) @@ -316,14 +296,3 @@ class ChosenFunctionCall(ChosenToolCall): """ The function to call """ - - -class NamedFunctionChoice(NamedToolChoice): - model_config = ConfigDict( - populate_by_name=True, - ) - type: Literal["function"] = "function" - function: FunctionCallOption - """ - The function to call - """ diff --git a/agents-api/agents_api/autogen/openapi_model.py b/agents-api/agents_api/autogen/openapi_model.py index bff2221eb..0d9390816 100644 --- a/agents-api/agents_api/autogen/openapi_model.py +++ b/agents-api/agents_api/autogen/openapi_model.py @@ -77,88 +77,35 @@ class InputChatMLMessage(Message): pass -# Custom types (not generated correctly) -# -------------------------------------- - -ChatMLContent = ( - list[ChatMLTextContentPart | ChatMLImageContentPart] - | Tool - | ChosenToolCall - | str - | ToolResponse - | list[ - list[ChatMLTextContentPart | ChatMLImageContentPart] - | Tool - | ChosenToolCall - | str - | ToolResponse - ] -) - -# Extract ChatMLRole -ChatMLRole = BaseEntry.model_fields["role"].annotation - -# Extract ChatMLSource -ChatMLSource = BaseEntry.model_fields["source"].annotation - -# Extract ExecutionStatus -ExecutionStatus = Execution.model_fields["status"].annotation - -# Extract TransitionType -TransitionType = Transition.model_fields["type"].annotation - -# Assertions to ensure consistency (optional, but recommended for runtime checks) -assert ChatMLRole == BaseEntry.model_fields["role"].annotation -assert ChatMLSource == BaseEntry.model_fields["source"].annotation -assert ExecutionStatus == Execution.model_fields["status"].annotation -assert TransitionType == Transition.model_fields["type"].annotation - - -# Create models -# ------------- +# Patches +# ------- -class CreateTransitionRequest(Transition): - # The following fields are optional in this +def type_property(self: BaseModel) -> str: + return ( + "function" + if self.function + else "integration" + if self.integration + else "system" + if self.system + else "api_call" + if self.api_call + else None + ) - id: UUID | None = None - execution_id: UUID | None = None - created_at: AwareDatetime | None = None - updated_at: AwareDatetime | None = None - metadata: dict[str, Any] | None = None - task_token: str | None = None +# Patch original Tool class to add 'type' property +TaskTool.type = computed_field(property(type_property)) -class CreateEntryRequest(BaseEntry): - timestamp: Annotated[ - float, Field(ge=0.0, default_factory=lambda: utcnow().timestamp()) - ] +# Patch original Tool class to add 'type' property +Tool.type = computed_field(property(type_property)) - @classmethod - def from_model_input( - cls: Type[Self], - model: str, - *, - role: ChatMLRole, - content: ChatMLContent, - name: str | None = None, - source: ChatMLSource, - **kwargs: dict, - ) -> Self: - tokenizer: dict = select_tokenizer(model=model) - token_count = token_counter( - model=model, messages=[{"role": role, "content": content, "name": name}] - ) +# Patch original UpdateToolRequest class to add 'type' property +UpdateToolRequest.type = computed_field(property(type_property)) - return cls( - role=role, - content=content, - name=name, - source=source, - tokenizer=tokenizer["type"], - token_count=token_count, - **kwargs, - ) +# Patch original PatchToolRequest class to add 'type' property +PatchToolRequest.type = computed_field(property(type_property)) # Patch Task Workflow Steps diff --git a/agents-api/agents_api/common/protocol/tasks.py b/agents-api/agents_api/common/protocol/tasks.py index 6892d7098..bbb5c28d3 100644 --- a/agents-api/agents_api/common/protocol/tasks.py +++ b/agents-api/agents_api/common/protocol/tasks.py @@ -218,19 +218,28 @@ def task_to_spec( task: Task | CreateTaskRequest | UpdateTaskRequest | PatchTaskRequest, **model_opts ) -> TaskSpecDef | PartialTaskSpecDef: task_data = task.model_dump(**model_opts) - main = task_data.pop("main") - workflows = [Workflow(name="main", steps=main)] + if "tools" in task_data: + del task_data["tools"] - for k in list(task_data.keys()): - if k in TaskSpec.model_fields.keys(): - continue + tools = [] + for tool in task.tools: + tool_spec = getattr(tool, tool.type) - steps = task_data.pop(k) - workflows.append(Workflow(name=k, steps=steps)) + tools.append( + TaskToolDef( + type=tool.type, + spec=tool_spec.model_dump(), + **tool.model_dump(exclude={"type"}), + ) + ) - tools = task_data.pop("tools", []) - tools = [TaskToolDef(spec=tool.pop(tool["type"]), **tool) for tool in tools] + workflows = [Workflow(name="main", steps=task_data.pop("main"))] + + for key, steps in list(task_data.items()): + if key not in TaskSpec.model_fields: + workflows.append(Workflow(name=key, steps=steps)) + del task_data[key] cls = PartialTaskSpecDef if isinstance(task, PatchTaskRequest) else TaskSpecDef diff --git a/typespec/tools/models.tsp b/typespec/tools/models.tsp index be0fb4f68..8a8cead44 100644 --- a/typespec/tools/models.tsp +++ b/typespec/tools/models.tsp @@ -74,9 +74,6 @@ model SystemDef { // TODO: We should use this model for all tools, not just functions and discriminate on the type model Tool { - /** Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) */ - type: ToolType = ToolType.function; - /** Name of the tool (must be unique for this agent and a valid python identifier string )*/ name: validPythonIdentifier; @@ -95,24 +92,13 @@ model FunctionCallOption { name: string; } -@discriminator("type") model NamedToolChoice { - /** Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) */ - type: ToolType; - function?: FunctionCallOption; integration?: never; // TODO: Implement system?: never; // TODO: Implement api_call?: never; // TODO: Implement } -model NamedFunctionChoice extends NamedToolChoice { - type: ToolType.function; - - /** The function to call */ - function: FunctionCallOption; -} - model ToolResponse { @key id: uuid; From 06813fbe4afde299420d6d285fc4ee3b4d9a6dbd Mon Sep 17 00:00:00 2001 From: JeevaRamanathan <64531160+JeevaRamanathan@users.noreply.github.com> Date: Fri, 4 Oct 2024 05:42:58 +0530 Subject: [PATCH 3/3] Enhanced error message in create, delete agent (#572) Related to #568 Enhanced HTTPException in `create_agent.py` and `delete_agent.py`. As this issue states, we aim to improve error messages throughout the agents-api. I believe this issue can accommodate multiple pull requests. Your suggestions are highly appreciated. ---- > [!IMPORTANT] > Enhanced error messages in `create_agent.py` and `delete_agent.py` for better clarity in `HTTPException` details. > > - **Error Messages**: > - In `create_agent.py`, updated `HTTPException` detail to "Developer not found. Please ensure the provided auth token (which refers to your developer_id) is valid and the developer has the necessary permissions to create an agent." > - In `delete_agent.py`, updated `HTTPException` detail to "The specified developer does not own the requested resource. Please verify the ownership or check if the developer ID is correct." > > This description was created by [Ellipsis](https://www.ellipsis.dev?ref=julep-ai%2Fjulep&utm_source=github&utm_medium=referral) for a085d4193ec0c36ba407f340c77e81834419dd69. It will automatically update as commits are pushed. Co-authored-by: Diwank Singh Tomer --- agents-api/agents_api/models/agent/create_agent.py | 3 ++- agents-api/agents_api/models/agent/delete_agent.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/agents-api/agents_api/models/agent/create_agent.py b/agents-api/agents_api/models/agent/create_agent.py index 546d0d441..c1451bb30 100644 --- a/agents-api/agents_api/models/agent/create_agent.py +++ b/agents-api/agents_api/models/agent/create_agent.py @@ -30,7 +30,8 @@ lambda e: isinstance(e, QueryException) and "asserted to return some results, but returned none" in str(e): lambda *_: HTTPException( - detail="developer not found", status_code=403 + detail="Developer not found. Please ensure the provided auth token (which refers to your developer_id) is valid and the developer has the necessary permissions to create an agent.", + status_code=403 ), QueryException: partialclass(HTTPException, status_code=400, detail="A database query failed to return the expected results. This might occur if the requested resource doesn't exist or your query parameters are incorrect."), ValidationError: partialclass(HTTPException, status_code=400, detail="Input validation failed. Please check the provided data for missing or incorrect fields, and ensure it matches the required format."), diff --git a/agents-api/agents_api/models/agent/delete_agent.py b/agents-api/agents_api/models/agent/delete_agent.py index cb6b16718..d8f710481 100644 --- a/agents-api/agents_api/models/agent/delete_agent.py +++ b/agents-api/agents_api/models/agent/delete_agent.py @@ -30,7 +30,8 @@ lambda e: isinstance(e, QueryException) and "Developer does not own resource" in e.resp["display"]: lambda *_: HTTPException( - detail="developer not found or doesnt own resource", status_code=404 + detail="The specified developer does not own the requested resource. Please verify the ownership or check if the developer ID is correct.", + status_code=404 ), QueryException: partialclass(HTTPException, status_code=400,detail="A database query failed to return the expected results. This might occur if the requested resource doesn't exist or your query parameters are incorrect."), ValidationError: partialclass(HTTPException, status_code=400,detail="Input validation failed. Please check the provided data for missing or incorrect fields, and ensure it matches the required format."),