Remove client.chat resource (#105)

This was always temporary, but we are finally removing the chat resource
from the ``Arcade`` and ``AsyncArcade`` clients.
This commit is contained in:
Sam Partee 2024-10-10 18:03:30 -07:00 committed by GitHub
parent 6b716d6dde
commit ff092ac303
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 30 additions and 38 deletions

View file

@ -8,7 +8,7 @@ from typing import Any, Optional
from urllib.parse import urlencode
import typer
from openai import OpenAIError
from openai import OpenAI, OpenAIError
from rich.console import Console
from rich.markup import escape
from rich.text import Text
@ -256,7 +256,10 @@ def chat(
history.append({"role": "user", "content": user_input})
try:
chat_result = handle_chat_interaction(client, model, history, user_email, stream)
openai_client = OpenAI(api_key=config.api.key, base_url=config.engine_url)
chat_result = handle_chat_interaction(
openai_client, model, history, user_email, stream
)
except OpenAIError as e:
console.print(f"❌ Arcade Chat failed with error: {e!s}", style="bold red")
continue
@ -273,7 +276,7 @@ def chat(
try:
history.pop()
chat_result = handle_chat_interaction(
client, model, history, user_email, stream
openai_client, model, history, user_email, stream
)
except OpenAIError as e:
console.print(f"❌ Arcade Chat failed with error: {e!s}", style="bold red")

View file

@ -4,6 +4,7 @@ from pathlib import Path
from typing import Callable, Union
import typer
from openai import OpenAI
from openai.resources.chat.completions import ChatCompletionChunk, Stream
from openai.types.chat.chat_completion import Choice as ChatCompletionChoice
from openai.types.chat.chat_completion_chunk import Choice as ChatCompletionChunkChoice
@ -247,7 +248,7 @@ class ChatInteractionResult:
def handle_chat_interaction(
client: Arcade, model: str, history: list[dict], user_email: str | None, stream: bool = False
client: OpenAI, model: str, history: list[dict], user_email: str | None, stream: bool = False
) -> ChatInteractionResult:
"""
Handle a single chat-request/chat-response interaction for both streamed and non-streamed responses.

View file

@ -13,7 +13,6 @@ from arcade.client.errors import (
RateLimitError,
UnauthorizedError,
)
from arcade.client.schema import OPENAI_API_VERSION
T = TypeVar("T")
ResponseT = TypeVar("ResponseT")
@ -22,11 +21,15 @@ ResponseT = TypeVar("ResponseT")
class BaseResource(Generic[T]):
"""Base class for all resources."""
_path: str
_path: str = ""
_version: str = "v1"
def __init__(self, client: T) -> None:
self._client = client
self._resource_path = self._client._base_url + self._path # type: ignore[attr-defined]
self._resource_path = urljoin(
self._client._base_url, # type: ignore[attr-defined]
f"{self._version}/{self._path}",
)
class BaseArcadeClient:
@ -37,8 +40,8 @@ class BaseArcadeClient:
base_url: str | None = None,
api_key: str | None = None,
headers: dict[str, str] | None = None,
timeout: float | Timeout = 10.0,
retries: int = 3,
timeout: float | Timeout = 30.0,
retries: int = 1,
):
"""
Initialize the BaseArcadeClient.
@ -70,12 +73,6 @@ class BaseArcadeClient:
"""
return urljoin(self._base_url, path)
def _chat_url(self, base_url: str) -> str:
chat_url = str(base_url)
if not base_url.endswith(OPENAI_API_VERSION):
chat_url = f"{base_url}/{OPENAI_API_VERSION}"
return chat_url
def _handle_http_error(self, e: httpx.HTTPStatusError) -> None:
error_map = {
400: BadRequestError,

View file

@ -1,8 +1,7 @@
import json
from typing import Any, TypeVar, Union
from httpx import Timeout
from openai import AsyncOpenAI, OpenAI
from openai.resources.chat import AsyncChat, Chat
from arcade.client.base import (
AsyncArcadeClient,
@ -120,7 +119,7 @@ class ToolResource(BaseResource[ClientT]):
tool_name: str,
user_id: str,
tool_version: str | None = None,
inputs: dict[str, Any] | None = None,
inputs: dict[str, Any] | str | None = None,
) -> ExecuteToolResponse:
"""
Send a request to execute a tool and return the response.
@ -131,6 +130,12 @@ class ToolResource(BaseResource[ClientT]):
tool_version: The version of the tool to execute (if not provided, the latest version will be used).
inputs: The inputs for the tool.
"""
if not isinstance(inputs, str):
try:
inputs = json.dumps(inputs)
except Exception:
raise ValueError("Inputs must be a valid JSON object or serializable dictionary")
request_data = {
"tool_name": tool_name,
"user_id": user_id,
@ -399,12 +404,6 @@ class Arcade(SyncArcadeClient):
self.auth: AuthResource = AuthResource(self)
self.tools: ToolResource = ToolResource(self)
self.health: HealthResource = HealthResource(self)
chat_url = self._chat_url(self._base_url)
self._openai_client = OpenAI(base_url=chat_url, api_key=self._api_key)
@property
def chat(self) -> Chat:
return self._openai_client.chat
def _execute_request(self, method: str, url: str, **kwargs: Any) -> Any:
"""
@ -422,12 +421,6 @@ class AsyncArcade(AsyncArcadeClient):
self.auth: AsyncAuthResource = AsyncAuthResource(self)
self.tools: AsyncToolResource = AsyncToolResource(self)
self.health: AsyncHealthResource = AsyncHealthResource(self)
chat_url = self._chat_url(self._base_url)
self._openai_client = AsyncOpenAI(base_url=chat_url, api_key=self._api_key)
@property
def chat(self) -> AsyncChat:
return self._openai_client.chat
async def _execute_request(self, method: str, url: str, **kwargs: Any) -> Any:
"""

View file

@ -1,12 +1,9 @@
import os
from enum import Enum
from pydantic import BaseModel, Field
from arcade.core.schema import ToolAuthorizationContext, ToolCallOutput
OPENAI_API_VERSION = os.getenv("OPENAI_API_VERSION", "v1")
class AuthProvider(str, Enum):
google = "google"

View file

@ -16,7 +16,8 @@ except ImportError:
"Use `pip install arcade-ai[evals]` to install the required dependencies for evaluation."
)
from arcade.client.client import AsyncArcade
from openai import AsyncOpenAI
from arcade.sdk.error import WeightError
if TYPE_CHECKING:
@ -520,12 +521,12 @@ class EvalSuite:
)
self.cases.append(new_case)
async def run(self, client: AsyncArcade, model: str) -> dict[str, Any]:
async def run(self, client: AsyncOpenAI, model: str) -> dict[str, Any]:
"""
Run the evaluation suite.
Args:
client: The AsyncArcade client instance.
client: The AsyncOpenAI client instance.
model: The model to evaluate.
Returns:
@ -651,11 +652,11 @@ def tool_eval() -> Callable[[Callable], Callable]:
raise TypeError("Eval function must return an EvalSuite")
suite.max_concurrent = max_concurrency
results = []
async with AsyncArcade(
async with AsyncOpenAI(
api_key=config.api.key,
base_url=config.engine_url,
) as client:
result = await suite.run(client, model) # type: ignore[arg-type]
result = await suite.run(client, model)
results.append(result)
return results