Skip to content

Commit

Permalink
feat: Add API call tool type (#571)
Browse files Browse the repository at this point in the history
- **Add optional description field to agent tools**
- **feat(typespec): Add API call tool definition type**

<!-- ELLIPSIS_HIDDEN -->


----

> [!IMPORTANT]
> Add API call tool type and description field to agent tools, updating
models and types accordingly.
> 
>   - **Behavior**:
> - Adds `ApiCallDef` and `ApiCallDefUpdate` classes in `Tools.py` to
define API call tools.
> - Adds `api_call` field to `CreateToolRequest`, `PatchToolRequest`,
`UpdateToolRequest`, and `Tool` classes in `Tools.py`.
> - Adds `description` field to `CreateToolRequest`, `PatchToolRequest`,
`UpdateToolRequest`, and `Tool` classes in `Tools.py`.
> - Updates `prepare_chat_context.py` and `prepare_execution_input.py`
to include `description` in tool data.
> - Implements `execute_api_call()` in `excecute_api_call.py` to handle
API call execution.
>   - **Types**:
>     - Adds `httpMethod` alias in `scalars.tsp` for valid HTTP methods.
>     - Defines `ApiCallDef` model in `models.tsp` for API call tools.
>   - **Misc**:
> - Updates `create_tools.py` and `list_tools.py` to handle
`description` field in tool records.
> - Adds migration `migrate_1727922523_add_description_to_tools.py` to
add `description` field to tools table.
> - Adds test `workflow: tool call api_call` in
`test_execution_workflow.py` to verify API call tool functionality.
> 
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=julep-ai%2Fjulep&utm_source=github&utm_medium=referral)<sup>
for 326067a. It will automatically
update as commits are pushed.</sup>


<!-- ELLIPSIS_HIDDEN -->

---------

Signed-off-by: Diwank Singh Tomer <[email protected]>
Co-authored-by: HamadaSalhab <[email protected]>
Co-authored-by: vedantsahai18 <[email protected]>
Co-authored-by: creatorrr <[email protected]>
  • Loading branch information
4 people authored Oct 4, 2024
1 parent a6bcbbe commit 1c2fb63
Show file tree
Hide file tree
Showing 19 changed files with 478 additions and 47 deletions.
60 changes: 60 additions & 0 deletions agents-api/agents_api/activities/excecute_api_call.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from typing import Annotated, Any, Optional, TypedDict, Union

import httpx
from beartype import beartype
from pydantic import Field
from temporalio import activity

from ..autogen.openapi_model import ApiCallDef

# from ..clients import integrations
from ..common.protocol.tasks import StepContext
from ..env import testing

# from ..models.tools import get_tool_args_from_metadata


class RequestArgs(TypedDict):
content: Optional[str]
data: Optional[dict[str, Any]]
json_: Optional[dict[str, Any]]
cookies: Optional[dict[str, str]]
params: Optional[Union[str, dict[str, Any]]]


@beartype
async def execute_api_call(
api_call: ApiCallDef,
request_args: RequestArgs,
) -> Any:
try:
async with httpx.AsyncClient() as client:
response = await client.request(
method=api_call.method,
url=str(api_call.url),
headers=api_call.headers,
follow_redirects=api_call.follow_redirects,
**request_args,
)

response_dict = {
"status_code": response.status_code,
"headers": dict(response.headers),
"content": response.content,
"json": response.json(),
}

return response_dict

except BaseException as e:
if activity.in_activity():
activity.logger.error(f"Error in execute_api_call: {e}")

raise


mock_execute_api_call = execute_api_call

execute_api_call = activity.defn(name="execute_api_call")(
execute_api_call if not testing else mock_execute_api_call
)
11 changes: 10 additions & 1 deletion agents-api/agents_api/activities/task_steps/base_evaluate.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

@beartype
async def base_evaluate(
exprs: str | list[str] | dict[str, str],
exprs: str | list[str] | dict[str, str] | dict[str, dict[str, str]],
values: dict[str, Any] = {},
extra_lambda_strs: dict[str, str] | None = None,
) -> Any | list[Any] | dict[str, Any]:
Expand Down Expand Up @@ -53,9 +53,18 @@ async def base_evaluate(
case list():
return [evaluator.eval(expr) for expr in exprs]

case dict() as d if all(isinstance(v, dict) for v in d.values()):
return {
k: {ik: evaluator.eval(iv) for ik, iv in v.items()}
for k, v in d.items()
}

case dict():
return {k: evaluator.eval(v) for k, v in exprs.items()}

case _:
raise ValueError(f"Invalid expression: {exprs}")

except BaseException as e:
if activity.in_activity():
activity.logger.error(f"Error in base_evaluate: {e}")
Expand Down
2 changes: 1 addition & 1 deletion agents-api/agents_api/autogen/Tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,7 @@ class ToolCallStep(BaseModel):
"""
The tool to run
"""
arguments: dict[str, str] | Literal["_"] = "_"
arguments: dict[str, dict[str, str] | str] | Literal["_"] = "_"
"""
The input parameters for the tool (defaults to last step output)
"""
Expand Down
181 changes: 164 additions & 17 deletions agents-api/agents_api/autogen/Tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,114 @@
from typing import Annotated, Any, Literal
from uuid import UUID

from pydantic import AwareDatetime, BaseModel, ConfigDict, Field
from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field, StrictBool


class ApiCallDef(BaseModel):
"""
API call definition
"""

model_config = ConfigDict(
populate_by_name=True,
)
method: Literal[
"GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE"
]
"""
The HTTP method to use
"""
url: AnyUrl
"""
The URL to call
"""
headers: dict[str, str] | None = None
"""
The headers to send with the request
"""
content: str | None = None
"""
The content as base64 to send with the request
"""
data: dict[str, str] | None = None
"""
The data to send as form data
"""
json_: Annotated[dict[str, Any] | None, Field(None, alias="json")]
"""
JSON body to send with the request
"""
cookies: dict[str, str] | None = None
"""
Cookies
"""
params: str | dict[str, Any] | None = None
"""
The parameters to send with the request
"""
follow_redirects: StrictBool | None = None
"""
Follow redirects
"""


class ApiCallDefUpdate(BaseModel):
"""
API call definition
"""

model_config = ConfigDict(
populate_by_name=True,
)
method: (
Literal[
"GET",
"POST",
"PUT",
"DELETE",
"PATCH",
"HEAD",
"OPTIONS",
"CONNECT",
"TRACE",
]
| None
) = None
"""
The HTTP method to use
"""
url: AnyUrl | None = None
"""
The URL to call
"""
headers: dict[str, str] | None = None
"""
The headers to send with the request
"""
content: str | None = None
"""
The content as base64 to send with the request
"""
data: dict[str, str] | None = None
"""
The data to send as form data
"""
json_: Annotated[dict[str, Any] | None, Field(None, alias="json")]
"""
JSON body to send with the request
"""
cookies: dict[str, str] | None = None
"""
Cookies
"""
params: str | dict[str, Any] | None = None
"""
The parameters to send with the request
"""
follow_redirects: StrictBool | None = None
"""
Follow redirects
"""


class ChosenToolCall(BaseModel):
Expand Down Expand Up @@ -37,12 +144,26 @@ class CreateToolRequest(BaseModel):
"""
Name of the tool (must be unique for this agent and a valid python identifier string )
"""
description: str | None = None
"""
Description of the tool
"""
function: FunctionDef | None = None
"""
The function to call
"""
integration: IntegrationDef | None = None
"""
The integration to call
"""
system: SystemDef | None = None
"""
The system to call
"""
api_call: ApiCallDef | None = None
"""
The API call to make
"""


class FunctionCallOption(BaseModel):
Expand Down Expand Up @@ -104,10 +225,6 @@ class IntegrationDef(BaseModel):
"""
The specific method of the integration to call
"""
description: str | None = None
"""
Optional description of the integration
"""
setup: dict[str, Any] | None = None
"""
The setup parameters the integration accepts
Expand Down Expand Up @@ -146,10 +263,6 @@ class IntegrationDefUpdate(BaseModel):
"""
The specific method of the integration to call
"""
description: str | None = None
"""
Optional description of the integration
"""
setup: dict[str, Any] | None = None
"""
The setup parameters the integration accepts
Expand Down Expand Up @@ -179,12 +292,26 @@ class PatchToolRequest(BaseModel):
"""
Name of the tool (must be unique for this agent and a valid python identifier string )
"""
description: str | None = None
"""
Description of the tool
"""
function: FunctionDef | None = None
"""
The function to call
"""
integration: IntegrationDefUpdate | None = None
"""
The integration to call
"""
system: SystemDefUpdate | None = None
"""
The system to call
"""
api_call: ApiCallDefUpdate | None = None
"""
The API call to make
"""


class SystemDef(BaseModel):
Expand All @@ -199,10 +326,6 @@ class SystemDef(BaseModel):
"""
The name of the system call
"""
description: str | None = None
"""
Optional description of the system call
"""
arguments: dict[str, Any] | None = None
"""
The arguments to pre-apply to the system call
Expand All @@ -221,10 +344,6 @@ class SystemDefUpdate(BaseModel):
"""
The name of the system call
"""
description: str | None = None
"""
Optional description of the system call
"""
arguments: dict[str, Any] | None = None
"""
The arguments to pre-apply to the system call
Expand All @@ -239,12 +358,26 @@ class Tool(BaseModel):
"""
Name of the tool (must be unique for this agent and a valid python identifier string )
"""
description: str | None = None
"""
Description of the tool
"""
function: FunctionDef | None = None
"""
The function to call
"""
integration: IntegrationDef | None = None
"""
The integration to call
"""
system: SystemDef | None = None
"""
The system to call
"""
api_call: ApiCallDef | None = None
"""
The API call to make
"""
created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})]
"""
When this resource was created as UTC date-time
Expand Down Expand Up @@ -279,12 +412,26 @@ class UpdateToolRequest(BaseModel):
"""
Name of the tool (must be unique for this agent and a valid python identifier string )
"""
description: str | None = None
"""
Description of the tool
"""
function: FunctionDef | None = None
"""
The function to call
"""
integration: IntegrationDef | None = None
"""
The integration to call
"""
system: SystemDef | None = None
"""
The system to call
"""
api_call: ApiCallDef | None = None
"""
The API call to make
"""


class ChosenFunctionCall(ChosenToolCall):
Expand Down
3 changes: 2 additions & 1 deletion agents-api/agents_api/models/chat/prepare_chat_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,13 @@ def prepare_chat_context(
participant_type: "agent",
},
*tools { agent_id, tool_id, name, type, spec, updated_at, created_at },
*tools { agent_id, tool_id, name, type, spec, description, updated_at, created_at },
tool = {
"id": tool_id,
"name": name,
"type": type,
"spec": spec,
"description": description,
"updated_at": updated_at,
"created_at": created_at,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ def prepare_execution_input(
"name",
"type",
"spec",
"description",
"created_at",
"updated_at",
)
Expand Down
4 changes: 3 additions & 1 deletion agents-api/agents_api/models/task/create_or_update_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ def create_or_update_task(
data.metadata = data.metadata or {}
data.input_schema = data.input_schema or {}

task_data = task_to_spec(data).model_dump(exclude_none=True, exclude_unset=True)
task_data = task_to_spec(data).model_dump(
exclude_none=True, exclude_unset=True, mode="json"
)
task_data.pop("task_id", None)
task_data["created_at"] = utcnow().timestamp()

Expand Down
Loading

0 comments on commit 1c2fb63

Please sign in to comment.