Skip to content

Commit c1a66a6

Browse files
authored
SDK: Fully qualified tool names (#47)
In this PR: - Handle and require fully-qualified tool names `Toolkit.ToolName` in the actor Also, unrelated changes/fixes: - Cleaned up the logic around actor secrets and `$ARCADE_ACTOR_SECRET` - Removes experimental Flask actor for now Note: Must be merged along with ArcadeAI/Engine#87
1 parent 53fa083 commit c1a66a6

File tree

23 files changed

+449
-1195
lines changed

23 files changed

+449
-1195
lines changed

.vscode/launch.json

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,22 @@
66
"type": "python",
77
"request": "launch",
88
"module": "uvicorn",
9-
"args": ["main:app", "--app-dir", "${workspaceFolder}/examples/fastapi/arcade_example_fastapi"],
9+
"args": ["main:app", "--app-dir", "${workspaceFolder}/examples/fastapi/arcade_example_fastapi", "--port", "8002"],
1010
"jinja": true,
1111
"justMyCode": true,
1212
"cwd": "${workspaceFolder}/examples/fastapi/arcade_example_fastapi"
1313
},
1414
{
15-
"name": "Debug `arcade dev`",
15+
"name": "Debug `arcade dev --no-auth`",
1616
"type": "python",
1717
"request": "launch",
1818
"program": "${workspaceFolder}/arcade/run_cli.py",
19-
"args": ["dev"],
19+
"args": ["dev", "--no-auth"],
2020
"console": "integratedTerminal",
2121
"jinja": true,
2222
"justMyCode": true,
2323
"cwd": "${workspaceFolder}"
2424
},
25-
,
2625
{
2726
"name": "Debug `arcade chat -h localhost`",
2827
"type": "python",
@@ -33,6 +32,17 @@
3332
"jinja": true,
3433
"justMyCode": true,
3534
"cwd": "${workspaceFolder}"
35+
},
36+
{
37+
"name": "Debug `arcade evals -d`",
38+
"type": "python",
39+
"request": "launch",
40+
"program": "${workspaceFolder}/arcade/run_cli.py",
41+
"args": ["evals", "-d"],
42+
"console": "integratedTerminal",
43+
"jinja": true,
44+
"justMyCode": true,
45+
"cwd": "${workspaceFolder}"
3646
}
3747
]
3848
}

arcade/arcade/actor/core/base.py

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import logging
2+
import os
13
import time
24
from datetime import datetime
35
from typing import Any, Callable, ClassVar
@@ -17,6 +19,8 @@
1719
ToolDefinition,
1820
)
1921

22+
logger = logging.getLogger(__name__)
23+
2024

2125
class BaseActor(Actor):
2226
"""
@@ -32,25 +36,48 @@ class BaseActor(Actor):
3236
HealthCheckComponent,
3337
)
3438

35-
def __init__(self, secret: str, disable_auth: bool = False) -> None:
39+
def __init__(self, secret: str | None = None, disable_auth: bool = False) -> None:
3640
"""
3741
Initialize the BaseActor with an empty ToolCatalog.
42+
If no secret is provided, the actor will use the ARCADE_ACTOR_SECRET environment variable.
3843
"""
3944
self.catalog = ToolCatalog()
4045
self.disable_auth = disable_auth
41-
self.secret = secret
46+
if disable_auth:
47+
logger.warning(
48+
"Warning: Actor is running without authentication. Not recommended for production."
49+
)
50+
51+
self.secret = self._set_secret(secret, disable_auth)
52+
53+
def _set_secret(self, secret: str | None, disable_auth: bool) -> str:
54+
if disable_auth:
55+
return ""
56+
57+
# If secret is provided, use it
58+
if secret:
59+
return secret
60+
61+
# If secret is not provided, try to get it from environment variables
62+
env_secret = os.environ.get("ARCADE_ACTOR_SECRET")
63+
if env_secret:
64+
return env_secret
65+
66+
raise ValueError(
67+
"No secret provided for actor. Set the ARCADE_ACTOR_SECRET environment variable."
68+
)
4269

4370
def get_catalog(self) -> list[ToolDefinition]:
4471
"""
4572
Get the catalog as a list of ToolDefinitions.
4673
"""
4774
return [tool.definition for tool in self.catalog]
4875

49-
def register_tool(self, tool: Callable) -> None:
76+
def register_tool(self, tool: Callable, toolkit_name: str | None = None) -> None:
5077
"""
5178
Register a tool to the catalog.
5279
"""
53-
self.catalog.add_tool(tool)
80+
self.catalog.add_tool(tool, toolkit_name)
5481

5582
def register_toolkit(self, toolkit: Toolkit) -> None:
5683
"""
@@ -62,12 +89,14 @@ async def call_tool(self, tool_request: ToolCallRequest) -> ToolCallResponse:
6289
"""
6390
Call (invoke) a tool using the ToolExecutor.
6491
"""
65-
tool_name = tool_request.tool.name
66-
tool = self.catalog.get_tool(tool_name)
67-
if not tool:
68-
raise ValueError(f"Tool {tool_name} not found in catalog.")
92+
tool_fqname = tool_request.tool.get_fully_qualified_name()
6993

70-
materialized_tool = self.catalog[tool_name]
94+
try:
95+
materialized_tool = self.catalog.get_tool(tool_fqname)
96+
except KeyError:
97+
raise ValueError(
98+
f"Tool {tool_fqname} not found in catalog with toolkit version {tool_request.tool.version}."
99+
)
71100

72101
start_time = time.time()
73102

@@ -95,7 +124,7 @@ def health_check(self) -> dict[str, Any]:
95124
"""
96125
Provide a health check that serves as a heartbeat of actor health.
97126
"""
98-
return {"status": "ok", "tool_count": len(self.catalog.tools.keys())}
127+
return {"status": "ok", "tool_count": len(self.catalog)}
99128

100129
def register_routes(self, router: Router) -> None:
101130
"""

arcade/arcade/actor/fastapi/actor.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ class FastAPIActor(BaseActor):
1818
An Arcade Actor that is hosted inside a FastAPI app.
1919
"""
2020

21-
def __init__(self, app: FastAPI, *, secret: str, disable_auth: bool = False) -> None:
21+
def __init__(
22+
self, app: FastAPI, secret: str | None = None, *, disable_auth: bool = False
23+
) -> None:
2224
"""
23-
Initialize the FastAPIActor with a FastAPI app
24-
instance and an empty ToolCatalog.
25+
Initialize the FastAPIActor with a FastAPI app instance.
26+
If no secret is provided, the actor will use the ARCADE_ACTOR_SECRET environment variable.
2527
"""
2628
super().__init__(secret, disable_auth)
2729
self.app = app

arcade/arcade/actor/flask/__init__.py

Lines changed: 0 additions & 7 deletions
This file was deleted.

arcade/arcade/actor/flask/actor.py

Lines changed: 0 additions & 75 deletions
This file was deleted.

arcade/arcade/cli/main.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -295,13 +295,6 @@ def dev(
295295
Starts the actor with host, port, and reload options. Uses
296296
Uvicorn as ASGI actor. Parameters allow runtime configuration.
297297
"""
298-
299-
if disable_auth:
300-
console.print(
301-
"⚠️ Actor authentication is disabled. Not recommended for production.",
302-
style="bold yellow",
303-
)
304-
305298
from arcade.cli.serve import serve_default_actor
306299

307300
try:

arcade/arcade/cli/serve.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ def serve_default_actor(
4949
else:
5050
logger.info("Serving the following toolkits:")
5151
for toolkit in toolkits:
52-
logger.info(f" - {toolkit.name} ({toolkit.package_name})")
52+
logger.info(f" - {toolkit.name} ({toolkit.package_name}): {len(toolkit.tools)} tools")
5353

5454
actor_secret = os.environ.get("ARCADE_ACTOR_SECRET")
55-
if not actor_secret:
55+
if not disable_auth and not actor_secret:
5656
logger.warning(
5757
"Warning: ARCADE_ACTOR_SECRET environment variable is not set. Using 'dev' as the actor secret.",
5858
)

0 commit comments

Comments
 (0)