CLI Improvements (#151)
1. Fixes bug where arcade login doesn't work for localhost - `arcade login -h localhost` will open login page at `http://localhost:8000/...` - Optionally specify the port: `arcade login -h localhost -p 8000` 3. Adds `local` flag to `arcade show` - `-h localhost`, `-h 127.0.0.1`, and `-h 0.0.0.0` shows the tools that are in the local engine's catalog - `--local` show the tools that are in the local environment.
This commit is contained in:
parent
081865733a
commit
c02aee3f14
3 changed files with 89 additions and 24 deletions
|
|
@ -4,7 +4,6 @@ import threading
|
|||
import uuid
|
||||
import webbrowser
|
||||
from typing import Any, Optional
|
||||
from urllib.parse import urlencode
|
||||
|
||||
import typer
|
||||
from arcadepy import Arcade
|
||||
|
|
@ -26,7 +25,8 @@ from arcade.cli.display import (
|
|||
from arcade.cli.launcher import start_servers
|
||||
from arcade.cli.utils import (
|
||||
OrderCommands,
|
||||
compute_base_url,
|
||||
compute_engine_base_url,
|
||||
compute_login_url,
|
||||
create_cli_catalog,
|
||||
delete_deprecated_config_file,
|
||||
get_eval_files,
|
||||
|
|
@ -58,6 +58,12 @@ def login(
|
|||
"--host",
|
||||
help="The Arcade Cloud host to log in to.",
|
||||
),
|
||||
port: Optional[int] = typer.Option(
|
||||
None,
|
||||
"-p",
|
||||
"--port",
|
||||
help="The port of the Arcade Cloud host (if running locally).",
|
||||
),
|
||||
) -> None:
|
||||
"""
|
||||
Logs the user into Arcade Cloud.
|
||||
|
|
@ -75,9 +81,7 @@ def login(
|
|||
|
||||
try:
|
||||
# Open the browser for user login
|
||||
callback_uri = "http://localhost:9905/callback"
|
||||
params = urlencode({"callback_uri": callback_uri, "state": state})
|
||||
login_url = f"https://{host}/api/v1/auth/cli_login?{params}"
|
||||
login_url = compute_login_url(host, state, port)
|
||||
|
||||
console.print("Opening a browser to log you in...")
|
||||
if not webbrowser.open(login_url):
|
||||
|
|
@ -142,7 +146,13 @@ def show(
|
|||
DEFAULT_ENGINE_HOST,
|
||||
"-h",
|
||||
"--host",
|
||||
help="The Arcade Engine address to send chat requests to.",
|
||||
help="The Arcade Engine address to show the tools/toolkits of.",
|
||||
),
|
||||
local: bool = typer.Option(
|
||||
False,
|
||||
"--local",
|
||||
"-l",
|
||||
help="Show the local environment's catalog instead of an Arcade Engine's catalog.",
|
||||
),
|
||||
port: Optional[int] = typer.Option(
|
||||
None,
|
||||
|
|
@ -165,9 +175,8 @@ def show(
|
|||
"""
|
||||
Show the available toolkits or detailed information about a specific tool.
|
||||
"""
|
||||
local_hosts = ["localhost", "127.0.0.1", "0.0.0.0"] # noqa: S104
|
||||
try:
|
||||
if host in local_hosts:
|
||||
if local:
|
||||
catalog = create_cli_catalog(toolkit=toolkit)
|
||||
tools = [t.definition for t in list(catalog)]
|
||||
else:
|
||||
|
|
@ -243,7 +252,7 @@ def chat(
|
|||
)
|
||||
|
||||
config = validate_and_get_config()
|
||||
base_url = compute_base_url(force_tls, force_no_tls, host, port)
|
||||
base_url = compute_engine_base_url(force_tls, force_no_tls, host, port)
|
||||
|
||||
client = Arcade(api_key=config.api.key, base_url=base_url)
|
||||
user_email = config.user.email if config.user else None
|
||||
|
|
@ -358,7 +367,7 @@ def evals(
|
|||
execute any functions decorated with @tool_eval, and display the results.
|
||||
"""
|
||||
config = validate_and_get_config()
|
||||
base_url = compute_base_url(force_tls, force_no_tls, host, port)
|
||||
base_url = compute_engine_base_url(force_tls, force_no_tls, host, port)
|
||||
|
||||
models_list = models.split(",") # Use 'models_list' to avoid shadowing
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import webbrowser
|
|||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Union, cast
|
||||
from urllib.parse import urlparse
|
||||
from urllib.parse import urlencode, urlparse
|
||||
|
||||
import idna
|
||||
import typer
|
||||
|
|
@ -69,7 +69,7 @@ def create_cli_catalog(
|
|||
return catalog
|
||||
|
||||
|
||||
def compute_base_url(
|
||||
def compute_engine_base_url(
|
||||
force_tls: bool,
|
||||
force_no_tls: bool,
|
||||
host: str,
|
||||
|
|
@ -78,6 +78,8 @@ def compute_base_url(
|
|||
"""
|
||||
Compute the base URL for the Arcade Engine from the provided overrides.
|
||||
|
||||
Treats 127.0.0.1 and 0.0.0.0 as aliases for localhost.
|
||||
|
||||
force_no_tls takes precedence over force_tls. For example, if both are set to True,
|
||||
the resulting URL will use http.
|
||||
|
||||
|
|
@ -102,6 +104,9 @@ def compute_base_url(
|
|||
Returns:
|
||||
str: The fully constructed URL for the Arcade Engine.
|
||||
"""
|
||||
# "Use 127.0.0.1" and "0.0.0.0" as aliases for "localhost"
|
||||
host = "localhost" if host in ["127.0.0.1", "0.0.0.0"] else host # noqa: S104
|
||||
|
||||
# Determine TLS setting based on input flags
|
||||
if force_no_tls:
|
||||
is_tls = False
|
||||
|
|
@ -150,6 +155,25 @@ def compute_base_url(
|
|||
return f"{protocol}://{encoded_host}"
|
||||
|
||||
|
||||
def compute_login_url(host: str, state: str, port: int | None) -> str:
|
||||
"""
|
||||
Compute the full URL for the CLI login endpoint.
|
||||
"""
|
||||
callback_uri = "http://localhost:9905/callback"
|
||||
params = urlencode({"callback_uri": callback_uri, "state": state})
|
||||
|
||||
port = port if port else 8000
|
||||
|
||||
login_base_url = (
|
||||
f"http://localhost:{port}"
|
||||
if host in ["localhost", "127.0.0.1", "0.0.0.0"] # noqa: S104
|
||||
else f"https://{host}"
|
||||
)
|
||||
endpoint = "/api/v1/auth/cli_login"
|
||||
|
||||
return f"{login_base_url}{endpoint}?{params}"
|
||||
|
||||
|
||||
def get_tools_from_engine(
|
||||
host: str,
|
||||
port: int | None = None,
|
||||
|
|
@ -158,7 +182,7 @@ def get_tools_from_engine(
|
|||
toolkit: str | None = None,
|
||||
) -> list[ToolDefinition]:
|
||||
config = validate_and_get_config()
|
||||
base_url = compute_base_url(force_tls, force_no_tls, host, port)
|
||||
base_url = compute_engine_base_url(force_tls, force_no_tls, host, port)
|
||||
client = Arcade(api_key=config.api.key, base_url=base_url)
|
||||
|
||||
tools = []
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import pytest
|
||||
|
||||
from arcade.cli.utils import compute_base_url
|
||||
from arcade.cli.utils import compute_engine_base_url, compute_login_url
|
||||
|
||||
DEFAULT_HOST = "api.arcade-ai.com"
|
||||
DEFAULT_CLOUD_HOST = "cloud.arcade-ai.com"
|
||||
DEFAULT_ENGINE_HOST = "api.arcade-ai.com"
|
||||
LOCALHOST = "localhost"
|
||||
DEFAULT_PORT = None
|
||||
DEFAULT_FORCE_TLS = False
|
||||
|
|
@ -14,7 +15,7 @@ DEFAULT_FORCE_NO_TLS = False
|
|||
[
|
||||
pytest.param(
|
||||
{
|
||||
"host_input": DEFAULT_HOST,
|
||||
"host_input": DEFAULT_ENGINE_HOST,
|
||||
"port_input": DEFAULT_PORT,
|
||||
"force_tls": DEFAULT_FORCE_TLS,
|
||||
"force_no_tls": DEFAULT_FORCE_NO_TLS,
|
||||
|
|
@ -34,7 +35,7 @@ DEFAULT_FORCE_NO_TLS = False
|
|||
),
|
||||
pytest.param(
|
||||
{
|
||||
"host_input": DEFAULT_HOST,
|
||||
"host_input": DEFAULT_ENGINE_HOST,
|
||||
"port_input": 9099,
|
||||
"force_tls": DEFAULT_FORCE_TLS,
|
||||
"force_no_tls": DEFAULT_FORCE_NO_TLS,
|
||||
|
|
@ -54,7 +55,7 @@ DEFAULT_FORCE_NO_TLS = False
|
|||
),
|
||||
pytest.param(
|
||||
{
|
||||
"host_input": DEFAULT_HOST,
|
||||
"host_input": DEFAULT_ENGINE_HOST,
|
||||
"port_input": DEFAULT_PORT,
|
||||
"force_tls": True,
|
||||
"force_no_tls": DEFAULT_FORCE_NO_TLS,
|
||||
|
|
@ -74,7 +75,7 @@ DEFAULT_FORCE_NO_TLS = False
|
|||
),
|
||||
pytest.param(
|
||||
{
|
||||
"host_input": DEFAULT_HOST,
|
||||
"host_input": DEFAULT_ENGINE_HOST,
|
||||
"port_input": 9099,
|
||||
"force_tls": True,
|
||||
"force_no_tls": DEFAULT_FORCE_NO_TLS,
|
||||
|
|
@ -94,7 +95,7 @@ DEFAULT_FORCE_NO_TLS = False
|
|||
),
|
||||
pytest.param(
|
||||
{
|
||||
"host_input": DEFAULT_HOST,
|
||||
"host_input": DEFAULT_ENGINE_HOST,
|
||||
"port_input": DEFAULT_PORT,
|
||||
"force_tls": DEFAULT_FORCE_TLS,
|
||||
"force_no_tls": True,
|
||||
|
|
@ -114,7 +115,7 @@ DEFAULT_FORCE_NO_TLS = False
|
|||
),
|
||||
pytest.param(
|
||||
{
|
||||
"host_input": DEFAULT_HOST,
|
||||
"host_input": DEFAULT_ENGINE_HOST,
|
||||
"port_input": 9099,
|
||||
"force_tls": DEFAULT_FORCE_TLS,
|
||||
"force_no_tls": True,
|
||||
|
|
@ -134,7 +135,7 @@ DEFAULT_FORCE_NO_TLS = False
|
|||
),
|
||||
pytest.param(
|
||||
{
|
||||
"host_input": DEFAULT_HOST,
|
||||
"host_input": DEFAULT_ENGINE_HOST,
|
||||
"port_input": DEFAULT_PORT,
|
||||
"force_tls": True,
|
||||
"force_no_tls": True,
|
||||
|
|
@ -154,7 +155,7 @@ DEFAULT_FORCE_NO_TLS = False
|
|||
),
|
||||
pytest.param(
|
||||
{
|
||||
"host_input": DEFAULT_HOST,
|
||||
"host_input": DEFAULT_ENGINE_HOST,
|
||||
"port_input": 9099,
|
||||
"force_tls": True,
|
||||
"force_no_tls": True,
|
||||
|
|
@ -185,7 +186,7 @@ DEFAULT_FORCE_NO_TLS = False
|
|||
],
|
||||
)
|
||||
def test_compute_base_url(inputs: dict, expected_output: str):
|
||||
base_url = compute_base_url(
|
||||
base_url = compute_engine_base_url(
|
||||
inputs["force_tls"],
|
||||
inputs["force_no_tls"],
|
||||
inputs["host_input"],
|
||||
|
|
@ -193,3 +194,34 @@ def test_compute_base_url(inputs: dict, expected_output: str):
|
|||
)
|
||||
|
||||
assert base_url == expected_output
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"inputs, expected_output",
|
||||
[
|
||||
pytest.param(
|
||||
{"host_input": DEFAULT_CLOUD_HOST, "port_input": DEFAULT_PORT, "state": "123"},
|
||||
"https://cloud.arcade-ai.com/api/v1/auth/cli_login?callback_uri=http%3A%2F%2Flocalhost%3A9905%2Fcallback&state=123",
|
||||
id="default",
|
||||
),
|
||||
pytest.param(
|
||||
{"host_input": "localhost", "port_input": 9099, "state": "123"},
|
||||
"http://localhost:9099/api/v1/auth/cli_login?callback_uri=http%3A%2F%2Flocalhost%3A9905%2Fcallback&state=123",
|
||||
id="localhost with custom port",
|
||||
),
|
||||
pytest.param(
|
||||
{"host_input": "localhost", "port_input": DEFAULT_PORT, "state": "123"},
|
||||
"http://localhost:8000/api/v1/auth/cli_login?callback_uri=http%3A%2F%2Flocalhost%3A9905%2Fcallback&state=123",
|
||||
id="localhost",
|
||||
),
|
||||
pytest.param(
|
||||
{"host_input": DEFAULT_CLOUD_HOST, "port_input": 8000, "state": "123"},
|
||||
"https://cloud.arcade-ai.com/api/v1/auth/cli_login?callback_uri=http%3A%2F%2Flocalhost%3A9905%2Fcallback&state=123",
|
||||
id="cloud host with an ignored custom port",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_compute_login_url(inputs: dict, expected_output: str):
|
||||
login_url = compute_login_url(inputs["host_input"], inputs["state"], inputs["port_input"])
|
||||
|
||||
assert login_url == expected_output
|
||||
|
|
|
|||
Loading…
Reference in a new issue