Add examples (#136)
## PR Description This PR adds 7 examples. * `call_a_tool_directly_with_auth.py` - Simple example that uses Arcade client to execute a tool that lists Gmail emails * `call_a_tool_directly.py` - Simple example that uses Arcade client to execute a tool that adds two numbers together * `call_a_tool_with_llm.py` - Simple example that uses the LLM api to star the arcade-ai repository * `get_auth_token.py` - Simple example that gets a Google auth token and then calls the Google API * `call_multiple_tools_directly_with_auth.py` - A more involved example that directly calls multiple spotify tools sequentially * `call_multiple_tools_with_llm.py` - A more involved example that uses an llm to call multiple spotify tools sequentially * `simple_chatbot.py` - Simple chatbot that uses arcade tools and has history --------- Co-authored-by: Nate Barbettini <nathanaelb@gmail.com>
This commit is contained in:
parent
6d1bc6c084
commit
081865733a
12 changed files with 546 additions and 60 deletions
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
|
|
@ -8,6 +8,11 @@
|
||||||
"python.testing.unittestEnabled": false,
|
"python.testing.unittestEnabled": false,
|
||||||
"python.testing.pytestEnabled": true,
|
"python.testing.pytestEnabled": true,
|
||||||
"[python]": {
|
"[python]": {
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll": "explicit",
|
||||||
|
"source.organizeImports": "explicit"
|
||||||
|
},
|
||||||
"editor.defaultFormatter": "charliermarsh.ruff"
|
"editor.defaultFormatter": "charliermarsh.ruff"
|
||||||
},
|
},
|
||||||
"[jsonc]": {
|
"[jsonc]": {
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
from arcadepy import Arcade
|
|
||||||
|
|
||||||
client = Arcade(
|
|
||||||
base_url="http://localhost:9099",
|
|
||||||
)
|
|
||||||
|
|
||||||
user_id = "you@example.com"
|
|
||||||
|
|
||||||
# Start the authorization process
|
|
||||||
auth_response = client.tools.authorize(
|
|
||||||
tool_name="Google.ListEmails",
|
|
||||||
user_id=user_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
if auth_response.status != "completed":
|
|
||||||
print(f"Click this link to authorize: {auth_response.authorization_url}")
|
|
||||||
input("After you have authorized, press Enter to continue...")
|
|
||||||
|
|
||||||
inputs = {"n_emails": 5}
|
|
||||||
|
|
||||||
response = client.tools.execute(
|
|
||||||
tool_name="Google.ListEmails",
|
|
||||||
inputs=inputs,
|
|
||||||
user_id=user_id,
|
|
||||||
)
|
|
||||||
print(response)
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
from arcadepy import Arcade
|
|
||||||
from arcadepy.types.auth_authorize_params import AuthRequirement, AuthRequirementOauth2
|
|
||||||
from google.oauth2.credentials import Credentials
|
|
||||||
from googleapiclient.discovery import build
|
|
||||||
|
|
||||||
client = Arcade(
|
|
||||||
base_url="http://localhost:9099",
|
|
||||||
)
|
|
||||||
|
|
||||||
user_id = "you@example.com"
|
|
||||||
|
|
||||||
# Start the authorization process
|
|
||||||
auth_response = client.auth.authorize(
|
|
||||||
auth_requirement=AuthRequirement(
|
|
||||||
provider_id="google",
|
|
||||||
oauth2=AuthRequirementOauth2(
|
|
||||||
scopes=["https://www.googleapis.com/auth/gmail.readonly"],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
user_id=user_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
if auth_response.status != "completed":
|
|
||||||
print(f"Click this link to authorize: {auth_response.authorization_url}")
|
|
||||||
input("After you have authorized, press Enter to continue...")
|
|
||||||
|
|
||||||
# Use the token from the authorization response
|
|
||||||
creds = Credentials(auth_response.context.token)
|
|
||||||
service = build("gmail", "v1", credentials=creds)
|
|
||||||
|
|
||||||
# Now you can use the Google API
|
|
||||||
results = service.users().labels().list(userId="me").execute()
|
|
||||||
labels = results.get("labels", [])
|
|
||||||
print("Labels:", labels)
|
|
||||||
40
examples/call_a_tool_directly.py
Normal file
40
examples/call_a_tool_directly.py
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
"""
|
||||||
|
This example demonstrates how to directly call a tool that does not require authorization.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from arcadepy import Arcade # pip install arcade-py
|
||||||
|
|
||||||
|
|
||||||
|
def call_non_auth_tool(client: Arcade, user_id: str) -> None:
|
||||||
|
"""Directly call a prebuilt tool that does not require authorization.
|
||||||
|
|
||||||
|
In this example, we are
|
||||||
|
1. Preparing the inputs to the Math.Add tool
|
||||||
|
2. Executing the tool
|
||||||
|
3. Printing the output of the tool's execution, i.e., the result of adding 9001 and 42
|
||||||
|
|
||||||
|
This is a simple example of calling a non-auth tool. Next, try writing your own non-auth tool for your own use case.
|
||||||
|
"""
|
||||||
|
# Prepare the inputs to the tool as a dictionary where keys are the names of the parameters expected by the tool and the values are the actual values to pass to the tool
|
||||||
|
inputs = {"a": 9001, "b": 42}
|
||||||
|
|
||||||
|
# Execute the tool
|
||||||
|
response = client.tools.execute(
|
||||||
|
tool_name="Math.Add",
|
||||||
|
inputs=inputs,
|
||||||
|
user_id=user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Print the output of the tool execution
|
||||||
|
print(response.output.value)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
cloud_host = "https://api.arcade-ai.com"
|
||||||
|
|
||||||
|
client = Arcade(
|
||||||
|
base_url=cloud_host, # Alternatively, use http://localhost:9099 if you are running Arcade Engine locally, or any base_url if you're hosting elsewhere
|
||||||
|
)
|
||||||
|
|
||||||
|
user_id = "you@example.com"
|
||||||
|
call_non_auth_tool(client, user_id)
|
||||||
51
examples/call_a_tool_directly_with_auth.py
Normal file
51
examples/call_a_tool_directly_with_auth.py
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
"""
|
||||||
|
This example demonstrates how to directly call a tool that requires authorization.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from arcadepy import Arcade # pip install arcade-py
|
||||||
|
|
||||||
|
|
||||||
|
def call_auth_tool(client: Arcade, user_id: str) -> None:
|
||||||
|
"""Directly call a prebuilt tool that requires authorization.
|
||||||
|
|
||||||
|
In this example, we are
|
||||||
|
1. Authorizing Arcade to read emails from the user's Gmail account with the user's permission to do so
|
||||||
|
2. Reading 5 emails from the user's Gmail account
|
||||||
|
3. Printing the emails
|
||||||
|
|
||||||
|
Try altering this example to call a tool that requires a different authorization.
|
||||||
|
"""
|
||||||
|
# Start the authorization process
|
||||||
|
auth_response = client.tools.authorize(
|
||||||
|
tool_name="Google.ListEmails",
|
||||||
|
user_id=user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
# If not already authorized, then wait for the user to authorize the permissions required by the tool
|
||||||
|
if auth_response.status != "completed":
|
||||||
|
print(f"Click this link to authorize: {auth_response.authorization_url}")
|
||||||
|
|
||||||
|
# Wait for the user to complete the auth flow, if necessary
|
||||||
|
client.auth.wait_for_completion(auth_response)
|
||||||
|
|
||||||
|
# Prepare the inputs to the tool as a dictionary where keys are the names of the parameters expected by the tool and the values are the actual values to pass to the tool
|
||||||
|
inputs = {"n_emails": 5}
|
||||||
|
|
||||||
|
# Execute the tool
|
||||||
|
response = client.tools.execute(
|
||||||
|
tool_name="Google.ListEmails",
|
||||||
|
inputs=inputs,
|
||||||
|
user_id=user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Print the output of the tool execution.
|
||||||
|
print(response)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
client = Arcade(
|
||||||
|
base_url="https://api.arcade-ai.com", # Alternatively, use http://localhost:9099 if you are running Arcade Engine locally, or any base_url if you're hosting elsewhere
|
||||||
|
)
|
||||||
|
|
||||||
|
user_id = "you@example.com"
|
||||||
|
call_auth_tool(client, user_id)
|
||||||
45
examples/call_a_tool_with_llm.py
Normal file
45
examples/call_a_tool_with_llm.py
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
"""
|
||||||
|
This example shows how to call a tool that requires authorization with an LLM using the OpenAI Python client.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from openai import OpenAI
|
||||||
|
|
||||||
|
|
||||||
|
def call_tool_with_openai(client: OpenAI) -> dict:
|
||||||
|
response = client.chat.completions.create(
|
||||||
|
messages=[
|
||||||
|
{"role": "user", "content": "Star the ArcadeAI/arcade-ai repository."},
|
||||||
|
],
|
||||||
|
model="gpt-4o-mini", # TODO: Try "claude-3-5-sonnet-20240620" or other models from our supported model providers. Checkout out our docs for a full list: https://docs.arcade-ai.com/integrations
|
||||||
|
user="you@example.com",
|
||||||
|
tools=["Github.SetStarred"],
|
||||||
|
tool_choice="generate", # TODO: Try "execute" and note any differences
|
||||||
|
)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
arcade_api_key = os.environ.get(
|
||||||
|
"ARCADE_API_KEY"
|
||||||
|
) # If you forget your Arcade API key, it is stored at ~/.arcade/credentials.yaml on `arcade login`
|
||||||
|
cloud_host = "https://api.arcade-ai.com" + "/v1"
|
||||||
|
|
||||||
|
openai_client = OpenAI(
|
||||||
|
api_key=arcade_api_key,
|
||||||
|
base_url=cloud_host, # Alternatively, use http://localhost:9099/v1 if you are running Arcade Engine locally
|
||||||
|
)
|
||||||
|
|
||||||
|
chat_result = call_tool_with_openai(openai_client)
|
||||||
|
# If the tool call requires authorization, then wait for the user to authorize and then call the tool again
|
||||||
|
if (
|
||||||
|
chat_result.choices[0].tool_authorizations
|
||||||
|
and chat_result.choices[0].tool_authorizations[0].get("status") == "pending"
|
||||||
|
):
|
||||||
|
print(chat_result.choices[0].message.content)
|
||||||
|
input("After you have authorized, press Enter to continue...")
|
||||||
|
chat_result = call_tool_with_openai(openai_client)
|
||||||
|
|
||||||
|
print(chat_result.choices[0].message.content)
|
||||||
178
examples/call_multiple_tools_directly_with_auth.py
Normal file
178
examples/call_multiple_tools_directly_with_auth.py
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
"""Example script demonstrating how to call multiple tools directly with authentication.
|
||||||
|
|
||||||
|
For this example, we are using the prebuilt Spotify toolkit to start playing similar songs to the currently playing song.
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
1. Get the currently playing song
|
||||||
|
2. Get audio features for the currently playing song
|
||||||
|
3. Get song recommendations that are similar to the currently playing song
|
||||||
|
4. Start playing the recommended songs
|
||||||
|
5. Inform the user which recommended song is now playing
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from arcadepy import Arcade # pip install arcade-py
|
||||||
|
|
||||||
|
|
||||||
|
# Need to click on a link for every provider
|
||||||
|
def get_permissions(client: Arcade, provider_to_scopes: dict, user_id: str) -> None:
|
||||||
|
"""Prompt the user to authorize necessary permissions for each provider."""
|
||||||
|
for provider, scopes in provider_to_scopes.items():
|
||||||
|
auth_response = client.auth.start(
|
||||||
|
user_id=user_id,
|
||||||
|
provider=provider,
|
||||||
|
scopes=scopes,
|
||||||
|
)
|
||||||
|
|
||||||
|
if auth_response.status != "completed":
|
||||||
|
print(f"Click this link to authorize: {auth_response.authorization_url}")
|
||||||
|
input("After you have authorized, press Enter to continue...")
|
||||||
|
|
||||||
|
|
||||||
|
def call_tool(client: Arcade, tool_name: str, user_id: str, inputs: Optional[dict] = None) -> Any:
|
||||||
|
"""Call a single tool."""
|
||||||
|
if inputs is None:
|
||||||
|
inputs = {}
|
||||||
|
|
||||||
|
response = client.tools.execute(
|
||||||
|
tool_name=tool_name,
|
||||||
|
inputs=inputs,
|
||||||
|
user_id=user_id,
|
||||||
|
)
|
||||||
|
return response.output.value
|
||||||
|
|
||||||
|
|
||||||
|
def recommend_similar_songs(
|
||||||
|
client: Arcade, provider_to_scopes: dict, tools: list[str], user_id: str, user_country_code: str
|
||||||
|
) -> None:
|
||||||
|
"""Execute the sequence of tools to get recommendations and start playback."""
|
||||||
|
get_permissions(client, provider_to_scopes, user_id)
|
||||||
|
|
||||||
|
recommendation_params = {"seed_genres": []}
|
||||||
|
|
||||||
|
(
|
||||||
|
get_currently_playing_tool,
|
||||||
|
get_tracks_audio_features_tool,
|
||||||
|
get_recommendations_tool,
|
||||||
|
start_tracks_playback_tool,
|
||||||
|
) = tools
|
||||||
|
|
||||||
|
# Step 1: Get the currently playing song
|
||||||
|
while True:
|
||||||
|
response = call_tool(client, get_currently_playing_tool, user_id)
|
||||||
|
|
||||||
|
if response["is_playing"]:
|
||||||
|
break
|
||||||
|
|
||||||
|
print("Nothing is playing right now. Press Enter once you start playing a song...")
|
||||||
|
input()
|
||||||
|
|
||||||
|
# Step 1.5: Use the previous tool output to construct the inputs for the following tool calls
|
||||||
|
current_track_name = response["track_name"]
|
||||||
|
current_track_artists = response["track_artists"]
|
||||||
|
current_track_id = response["track_id"]
|
||||||
|
current_track_spotify_url = response["track_spotify_url"]
|
||||||
|
current_track_artists_ids = response["track_artists_ids"]
|
||||||
|
print(
|
||||||
|
f"\nYou are currently listening to '{current_track_name}' by {', '.join(current_track_artists)}"
|
||||||
|
)
|
||||||
|
recommendation_params.update({
|
||||||
|
"seed_tracks": [current_track_id],
|
||||||
|
"seed_artists": current_track_artists_ids,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Step 2:Get audio features for the currently playing song
|
||||||
|
response = call_tool(
|
||||||
|
client,
|
||||||
|
get_tracks_audio_features_tool,
|
||||||
|
user_id,
|
||||||
|
inputs={"track_ids": [current_track_id]},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Step 2.5: Use the previous tool output to construct the inputs for the following tool calls
|
||||||
|
audio_features = response["audio_features"][0]
|
||||||
|
recommendation_params.update({
|
||||||
|
"target_acousticness": float(audio_features["acousticness"]),
|
||||||
|
"target_danceability": float(audio_features["danceability"]),
|
||||||
|
"target_energy": float(audio_features["energy"]),
|
||||||
|
"target_instrumentalness": float(audio_features["instrumentalness"]),
|
||||||
|
"target_key": int(audio_features["key"]),
|
||||||
|
"target_liveness": float(audio_features["liveness"]),
|
||||||
|
"target_loudness": float(audio_features["loudness"]),
|
||||||
|
"target_mode": int(audio_features["mode"]),
|
||||||
|
"target_speechiness": float(audio_features["speechiness"]),
|
||||||
|
"target_tempo": float(audio_features["tempo"]),
|
||||||
|
"target_time_signature": int(audio_features["time_signature"]),
|
||||||
|
"target_valence": float(audio_features["valence"]),
|
||||||
|
})
|
||||||
|
|
||||||
|
# Step 3: Get song recommendations that are similar to the currently playingsong
|
||||||
|
print(
|
||||||
|
f"Getting recommendations similar to '{current_track_name}' by {', '.join(current_track_artists)} - {current_track_spotify_url}"
|
||||||
|
)
|
||||||
|
response = call_tool(client, get_recommendations_tool, user_id, inputs=recommendation_params)
|
||||||
|
|
||||||
|
# Step 3.5: Use the previous tool output to construct the inputs for the following tool calls
|
||||||
|
# Filter out remixes and the same song from the recommendations
|
||||||
|
tracks = [
|
||||||
|
track for track in response["tracks"] if not track["name"].startswith(current_track_name)
|
||||||
|
]
|
||||||
|
track_ids = [
|
||||||
|
track["id"] for track in tracks if (user_country_code in track["available_markets"])
|
||||||
|
]
|
||||||
|
track_names = [track["name"] for track in tracks]
|
||||||
|
tracks_artists = [[artist["name"] for artist in track["artists"]] for track in tracks]
|
||||||
|
track_spotify_urls = [track["external_urls"]["spotify"] for track in tracks]
|
||||||
|
|
||||||
|
if not track_ids:
|
||||||
|
print("I couldn't find any similar songs that are available in your country.")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("\nHere are some recommendations:")
|
||||||
|
for track_name, track_artists, track_spotify_url in zip(
|
||||||
|
track_names, tracks_artists, track_spotify_urls
|
||||||
|
):
|
||||||
|
print(f"\t{track_name} by {', '.join(track_artists)} - {track_spotify_url}")
|
||||||
|
|
||||||
|
# Step 4: Start playing the recommended songs
|
||||||
|
response = call_tool(
|
||||||
|
client,
|
||||||
|
start_tracks_playback_tool,
|
||||||
|
user_id,
|
||||||
|
inputs={"track_ids": track_ids},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Step 5: Inform the user which recommended song is now playing
|
||||||
|
response = call_tool(client, get_currently_playing_tool, user_id)
|
||||||
|
print(
|
||||||
|
f"\nNow playing: {response['track_name']} by {', '.join(response['track_artists'])} - {response['track_spotify_url']}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
client = Arcade(base_url="https://api.arcade-ai.com")
|
||||||
|
|
||||||
|
# Necessary scopes for the tools we are calling:
|
||||||
|
provider_to_scopes = {
|
||||||
|
"spotify": [
|
||||||
|
"user-read-currently-playing",
|
||||||
|
"user-read-playback-state",
|
||||||
|
"user-modify-playback-state",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
tools = [
|
||||||
|
"Spotify.GetCurrentlyPlaying", # Get info about the current song
|
||||||
|
"Spotify.GetTracksAudioFeatures", # Get audio features for the current song
|
||||||
|
"Spotify.GetRecommendations", # Get recommendations similar to the current song
|
||||||
|
"Spotify.StartTracksPlaybackById", # Start playing the recommended songs
|
||||||
|
]
|
||||||
|
|
||||||
|
user_id = "you@example.com"
|
||||||
|
user_country_code = "US"
|
||||||
|
|
||||||
|
while True:
|
||||||
|
recommend_similar_songs(client, provider_to_scopes, tools, user_id, user_country_code)
|
||||||
|
print("\nPress Enter to get more recommendations...")
|
||||||
|
input()
|
||||||
91
examples/call_multiple_tools_with_llm.py
Normal file
91
examples/call_multiple_tools_with_llm.py
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
"""
|
||||||
|
Example script demonstrating how to call multiple tools (sequentially) using an LLM with authentication.
|
||||||
|
|
||||||
|
For this example, we are using the prebuilt Spotify toolkit to start playing similar songs to the currently playing song.
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
1. Get the currently playing song
|
||||||
|
2. Get audio features for the currently playing song
|
||||||
|
3. Get song recommendations that are similar to the currently playing song
|
||||||
|
4. Start playing the recommended songs
|
||||||
|
5. Inform the user which recommended song is now playing
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from openai import OpenAI
|
||||||
|
|
||||||
|
|
||||||
|
def call_tool(client: OpenAI, user_id: str, tool: str, message: dict, history: list[dict]) -> str:
|
||||||
|
"""Make a tool call with a specific tool and message."""
|
||||||
|
response = client.chat.completions.create(
|
||||||
|
messages=[
|
||||||
|
*history,
|
||||||
|
message,
|
||||||
|
],
|
||||||
|
model="gpt-4o",
|
||||||
|
user=user_id,
|
||||||
|
tools=[tool],
|
||||||
|
tool_choice="generate",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def call_tools_with_llm(client: OpenAI, user_id: str, user_country_code: str) -> list[dict]:
|
||||||
|
"""Use an LLM to execute the sequence of tools to get recommendations and start playback."""
|
||||||
|
tools = [
|
||||||
|
"Spotify.GetCurrentlyPlaying",
|
||||||
|
"Spotify.GetTracksAudioFeatures",
|
||||||
|
"Spotify.GetRecommendations",
|
||||||
|
"Spotify.StartTracksPlaybackById",
|
||||||
|
"Spotify.GetCurrentlyPlaying",
|
||||||
|
]
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
{"role": "user", "content": "Get the currently playing song."},
|
||||||
|
{"role": "user", "content": "Retrieve its audio features."},
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "Get song recommendations similar to it. "
|
||||||
|
"Do not include the currently playing song in the recommendations "
|
||||||
|
"or any remixed versions of the song. "
|
||||||
|
"Also only include tracks that are available in the user's country. "
|
||||||
|
f"The current user resides in {user_country_code}.",
|
||||||
|
},
|
||||||
|
{"role": "user", "content": "Start playing the recommended songs. Just one tool call."},
|
||||||
|
{"role": "user", "content": "Get the currently playing song."},
|
||||||
|
]
|
||||||
|
|
||||||
|
history = []
|
||||||
|
for i in range(len(messages)):
|
||||||
|
response = call_tool(client, user_id, tools[i], messages[i], history)
|
||||||
|
print("\n\n", response.choices[0].message.content)
|
||||||
|
if (
|
||||||
|
response.choices[0].tool_authorizations
|
||||||
|
and response.choices[0].tool_authorizations[0].get("status") == "pending"
|
||||||
|
):
|
||||||
|
input("\nPress Enter once you have authorized...")
|
||||||
|
response = call_tool(client, user_id, tools[i], messages[i], history)
|
||||||
|
history.append(messages[i])
|
||||||
|
history.append({"role": "assistant", "content": response.choices[0].message.content})
|
||||||
|
|
||||||
|
return history
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
arcade_api_key = os.environ.get("ARCADE_API_KEY")
|
||||||
|
cloud_host = "https://api.arcade-ai.com/v1"
|
||||||
|
|
||||||
|
openai_client = OpenAI(
|
||||||
|
api_key=arcade_api_key,
|
||||||
|
base_url=cloud_host,
|
||||||
|
)
|
||||||
|
|
||||||
|
user_id = "you@example.com"
|
||||||
|
user_country_code = "US"
|
||||||
|
|
||||||
|
while True:
|
||||||
|
history = call_tools_with_llm(openai_client, user_id, user_country_code)
|
||||||
|
|
||||||
|
print("\nPress Enter to get more recommendations...")
|
||||||
|
input()
|
||||||
58
examples/get_auth_token.py
Normal file
58
examples/get_auth_token.py
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
"""
|
||||||
|
This example demonstrates how to get an authorization token for a user and then use it to make a request to the Google API on behalf of the user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from arcadepy import Arcade
|
||||||
|
from google.oauth2.credentials import Credentials # pip install google-auth
|
||||||
|
from googleapiclient.discovery import build # pip install google-api-python-client
|
||||||
|
|
||||||
|
|
||||||
|
def get_auth_token(client: Arcade, user_id: str) -> str:
|
||||||
|
"""Get an authorization token for a user.
|
||||||
|
|
||||||
|
In this example, we are
|
||||||
|
1. Starting the authorization process for the Gmail Readonly scope
|
||||||
|
2. Waiting for the user to authorize the scope
|
||||||
|
3. Getting the authorization token
|
||||||
|
4. Using the authorization token to make a request to the Google API on behalf of the user
|
||||||
|
"""
|
||||||
|
# Start the authorization process
|
||||||
|
auth_response = client.auth.start(
|
||||||
|
user_id, "google", scopes=["https://www.googleapis.com/auth/gmail.readonly"]
|
||||||
|
)
|
||||||
|
|
||||||
|
if auth_response.status != "completed":
|
||||||
|
print(f"Click this link to authorize: {auth_response.authorization_url}")
|
||||||
|
auth_response = client.auth.wait_for_completion(auth_response)
|
||||||
|
|
||||||
|
return auth_response.context.token
|
||||||
|
|
||||||
|
|
||||||
|
def use_auth_token(token: str) -> None:
|
||||||
|
"""Use an authorization token to make a request to the Google API on behalf of a user.
|
||||||
|
|
||||||
|
In this example, we are
|
||||||
|
1. Using the authorization token that we got from the authorization process to make a request to the Google API
|
||||||
|
client.auth.wait_for_completion(auth_response)
|
||||||
|
"""
|
||||||
|
# Use the token from the authorization response
|
||||||
|
creds = Credentials(token)
|
||||||
|
service = build("gmail", "v1", credentials=creds)
|
||||||
|
|
||||||
|
# Now you can use the Google API
|
||||||
|
results = service.users().labels().list(userId="me").execute()
|
||||||
|
labels = results.get("labels", [])
|
||||||
|
print("Labels:", labels)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
cloud_host = "https://api.arcade-ai.com"
|
||||||
|
|
||||||
|
client = Arcade(
|
||||||
|
base_url=cloud_host, # Alternatively, use http://localhost:9099 if you are running Arcade locally, or any base_url if you're hosting elsewhere
|
||||||
|
)
|
||||||
|
|
||||||
|
user_id = "you@example.com"
|
||||||
|
|
||||||
|
token = get_auth_token(client, user_id)
|
||||||
|
use_auth_token(token)
|
||||||
74
examples/simple_chatbot.py
Normal file
74
examples/simple_chatbot.py
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
"""
|
||||||
|
Example script demonstrating how to build a simple chatbot with Arcade AI.
|
||||||
|
|
||||||
|
For this example, we are using the prebuilt Google Docs toolkit to create and edit documents.
|
||||||
|
|
||||||
|
Try asking questions like:
|
||||||
|
- "Create a document with the title 'My New Document' and content 'Hello, World!'"
|
||||||
|
- "List my 2 most recently modified documents and tell me the title, document id, and document URL of each one and summarize them."
|
||||||
|
- "Edit the second document from the list you just returned and add the text 'Hello, World!' to the end of it."
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from openai import OpenAI
|
||||||
|
|
||||||
|
|
||||||
|
def chat(openai_client: OpenAI, tool_names: list[str], user_id: str) -> None:
|
||||||
|
history = []
|
||||||
|
|
||||||
|
print("Hello! How can I help you today?")
|
||||||
|
while True:
|
||||||
|
message = {"role": "user", "content": input(">")}
|
||||||
|
history.append(message)
|
||||||
|
chat_result = call_tool_with_openai(openai_client, tool_names, user_id, history)
|
||||||
|
|
||||||
|
# If the tool call requires authorization, then wait for the user to authorize and then call the tool again
|
||||||
|
if (
|
||||||
|
chat_result.choices[0].tool_authorizations
|
||||||
|
and chat_result.choices[0].tool_authorizations[0].get("status") == "pending"
|
||||||
|
):
|
||||||
|
print("\n" + chat_result.choices[0].message.content)
|
||||||
|
input("\nAfter you have authorized, press Enter to continue...")
|
||||||
|
chat_result = call_tool_with_openai(openai_client, tool_names, user_id, history)
|
||||||
|
|
||||||
|
history.append({"role": "assistant", "content": chat_result.choices[0].message.content})
|
||||||
|
|
||||||
|
print(chat_result.choices[0].message.content)
|
||||||
|
|
||||||
|
|
||||||
|
def call_tool_with_openai(
|
||||||
|
client: OpenAI, tool_names: list[str], user_id: str, messages: list[dict]
|
||||||
|
) -> dict:
|
||||||
|
response = client.chat.completions.create(
|
||||||
|
messages=messages,
|
||||||
|
model="gpt-4o-mini",
|
||||||
|
user=user_id,
|
||||||
|
tools=tool_names,
|
||||||
|
tool_choice="generate",
|
||||||
|
)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
arcade_api_key = os.environ.get(
|
||||||
|
"ARCADE_API_KEY"
|
||||||
|
) # If you forget your Arcade API key, it is stored at ~/.arcade/credentials.yaml on `arcade login`
|
||||||
|
local_host = "http://localhost:9099/v1"
|
||||||
|
user_id = "user@example.com"
|
||||||
|
|
||||||
|
openai_client = OpenAI(
|
||||||
|
api_key=arcade_api_key,
|
||||||
|
base_url=local_host,
|
||||||
|
)
|
||||||
|
|
||||||
|
tool_names = [
|
||||||
|
"Google.GetDocumentById",
|
||||||
|
"Google.InsertTextAtEndOfDocument",
|
||||||
|
"Google.CreateBlankDocument",
|
||||||
|
"Google.CreateDocumentFromText",
|
||||||
|
"Google.ListDocuments",
|
||||||
|
]
|
||||||
|
|
||||||
|
chat(openai_client, tool_names, user_id)
|
||||||
|
|
@ -18,7 +18,9 @@ class PlaybackState:
|
||||||
album_spotify_url: Optional[str] = None
|
album_spotify_url: Optional[str] = None
|
||||||
track_id: Optional[str] = None
|
track_id: Optional[str] = None
|
||||||
track_name: Optional[str] = None
|
track_name: Optional[str] = None
|
||||||
|
track_spotify_url: Optional[str] = None
|
||||||
track_artists: list[str] = field(default_factory=list)
|
track_artists: list[str] = field(default_factory=list)
|
||||||
|
track_artists_ids: list[str] = field(default_factory=list)
|
||||||
show_id: Optional[str] = None
|
show_id: Optional[str] = None
|
||||||
show_spotify_url: Optional[str] = None
|
show_spotify_url: Optional[str] = None
|
||||||
episode_name: Optional[str] = None
|
episode_name: Optional[str] = None
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,9 @@ def convert_to_playback_state(data: dict) -> PlaybackState:
|
||||||
playback_state.album_spotify_url = album.get("external_urls", {}).get("spotify")
|
playback_state.album_spotify_url = album.get("external_urls", {}).get("spotify")
|
||||||
playback_state.track_name = item.get("name")
|
playback_state.track_name = item.get("name")
|
||||||
playback_state.track_id = item.get("id")
|
playback_state.track_id = item.get("id")
|
||||||
|
playback_state.track_spotify_url = item.get("external_urls").get("spotify")
|
||||||
playback_state.track_artists = [artist.get("name") for artist in item.get("artists", [])]
|
playback_state.track_artists = [artist.get("name") for artist in item.get("artists", [])]
|
||||||
|
playback_state.track_artists_ids = [artist.get("id") for artist in item.get("artists", [])]
|
||||||
elif data.get("currently_playing_type") == "episode":
|
elif data.get("currently_playing_type") == "episode":
|
||||||
item = data.get("item", {})
|
item = data.get("item", {})
|
||||||
show = item.get("show", {})
|
show = item.get("show", {})
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue