From a95702dcac5b0705706bb32cce780956031a840d Mon Sep 17 00:00:00 2001 From: Akshit Arora <46507859+aarora0211@users.noreply.github.com> Date: Wed, 19 Mar 2025 20:59:26 -0400 Subject: [PATCH 01/19] TLedger Plugin --- plugins/tLedger/README.md | 129 ++++++++++ plugins/tLedger/__init__.py | 0 plugins/tLedger/examples/.env.example | 8 + plugins/tLedger/examples/chat_agent.py | 134 +++++++++++ plugins/tLedger/examples/example-worker.py | 67 ++++++ plugins/tLedger/examples/example_agent.py | 188 +++++++++++++++ plugins/tLedger/pyproject.toml | 34 +++ .../tledger_plugin_gamesdk/__init__.py | 0 .../t54_account_details_plugin.py | 77 ++++++ .../t54_payments_plugin.py | 222 ++++++++++++++++++ .../tledger_plugin_gamesdk/tLedger_models.py | 41 ++++ 11 files changed, 900 insertions(+) create mode 100644 plugins/tLedger/README.md create mode 100644 plugins/tLedger/__init__.py create mode 100644 plugins/tLedger/examples/.env.example create mode 100644 plugins/tLedger/examples/chat_agent.py create mode 100644 plugins/tLedger/examples/example-worker.py create mode 100644 plugins/tLedger/examples/example_agent.py create mode 100644 plugins/tLedger/pyproject.toml create mode 100644 plugins/tLedger/tledger_plugin_gamesdk/__init__.py create mode 100644 plugins/tLedger/tledger_plugin_gamesdk/t54_account_details_plugin.py create mode 100644 plugins/tLedger/tledger_plugin_gamesdk/t54_payments_plugin.py create mode 100644 plugins/tLedger/tledger_plugin_gamesdk/tLedger_models.py diff --git a/plugins/tLedger/README.md b/plugins/tLedger/README.md new file mode 100644 index 00000000..8652d27f --- /dev/null +++ b/plugins/tLedger/README.md @@ -0,0 +1,129 @@ +# TLedger Plugin for GAME SDK + +## Features + +- get_agent_details - Get the details of your agent, including the TLedger agent_id and the balances of the agent's wallets +- create_payment - Create a payment request for a specific amount and currency +- get_payment_by_id - Get the details of a payment request by its ID +- get_payments - Get a list of all payments + +## Admin Setup for doing agent to agent payments using TLedger Plugin + +You are required to set up your account, project, agent profile, and keys in the TLedger platform to use the TLedger plugin for GAME SDK. + +To make testing easier, there are two agents already set up in the TLedger sandbox environment. You can use these agents to test the TLedger plugin. The agent details are as follows: + +Agent 1: +- Agent ID: agt_59b17650-a689-4649-91fa-4bf5d0db56ad +- key: ewSZjNQGPLle-vn5dMZoLOGUljEB6fbmox31o7KLKuI +- secret: iqB7-iETCVBE0UV_0HfRAwCHkVXO9_4cCPJYmTIyUpHauHlVP4Hk5xSsCquRqBO_2_eQ6OK_Zu7P1X4LU7hSHg + +Agent 2: +- Agent ID: agt_3db52291-a9f8-4f04-a180-adb6e50ef5b0 +- key: j06KtBcRRbmrEAqIVSiXZc3DPAJSqymDimo__ERD0oQ +- secret: h13ERQG797cYMeNeRLvwDF_3-DBt4o-kp0fL-bFHKstTUTS5xsLUFgDEUZG2GsoEKINxeSVusbAQxc24mHm1eQ + +If you want to set your own TLedger agents, the following steps will guide you through the setup process. + +TLedger Sandbox URL = https://tledger-sandbox-69bd94a49289.herokuapp.com/ + +### TLedger Account Setup + +1. You first need to register your user on the TLedger platform + +POST /api/v1/users/signup +payload = { + "email": "user@example.com", + "password": "password", + "full_name": "full name" +} + +2. Login to your account and obtain the JWT token + +POST /api/v1/login/access-token +payload = { + "username": "user@example.com", + "password": "password" +} +headers = { + "Content-Type": "application/x-www-form-urlencoded" +} + +### TLedger Project Setup + +1. Create a project + +POST /api/v1/projects +payload = { + "user_profile_id": "usr_123", # user_profile_id from the user profile + "network": "solana", + "description": "Solana Launch Pad", + "name": "Twitter Project", + "daily_limit": 100 +} + +### TLedger Agent Profile Setup + +1. Create an agent profile + +POST /api/v1/agent_profiles +payload = { + "project_id": "{{project_id}}", + "name": "My Agent", + "description": "Twitter KOL Agent" +} + +### TLedger Key Setup for Agent + +1. You need to create an API key for the agent to use the TLedger APIs + +POST /api/v1/api_key/generate-api-key +{ + "scopes": ["payments:read", "payments:write", "payments:agent:read"], + "agent_id": "agt_123456", + "created_by": "Name" +} + +For a complete list of TLedger APIs, please feel free to look at the public documentation at: https://docs.t54.ai/ + +### Setup and Configuration + +Import and initialize the plugin to use in your worker: + +```python +import os +from plugins.tLedger.tledger_plugin_gamesdk.t54_payments_plugin import T54PaymentsPlugin +from plugins.tLedger.tledger_plugin_gamesdk.t54_account_details_plugin import T54AccountDetailsPlugin + +account_details_plugin = T54AccountDetailsPlugin( + api_key=os.environ.get("TLEDGER_API_KEY"), + api_secret=os.environ.get("TLEDGER_API_SECRET"), + api_url=os.environ.get("TLEDGER_API_URL") +) + +payments_plugin = T54PaymentsPlugin( + api_key=os.environ.get("TLEDGER_API_KEY"), + api_secret=os.environ.get("TLEDGER_API_SECRET"), + api_url = os.environ.get("TLEDGER_API_URL") +) +``` + +**Basic worker example:** + +```python + +def get_state_fn(function_result: FunctionResult, current_state: dict) -> dict: + +tledger_worker = Worker( + api_key=os.environ.get("GAME_API_KEY"), + description="Worker specialized in doing payments on Tledger", + get_state_fn=get_state_fn, + action_space=[ + account_details_plugin.functions.get("get_agent_profile_details"), + payments_plugin.functions.get("create_payment"), + payments_plugin.functions.get("get_payment_by_id"), + ], +) + +tledger_worker.run("Get TLedger account details") +``` diff --git a/plugins/tLedger/__init__.py b/plugins/tLedger/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/plugins/tLedger/examples/.env.example b/plugins/tLedger/examples/.env.example new file mode 100644 index 00000000..daf15bff --- /dev/null +++ b/plugins/tLedger/examples/.env.example @@ -0,0 +1,8 @@ +TLEDGER_API_KEY=ewSZjNQGPLle-vn5dMZoLOGUljEB6fbmox31o7KLKuI +TLEDGER_API_SECRET=iqB7-iETCVBE0UV_0HfRAwCHkVXO9_4cCPJYmTIyUpHauHlVP4Hk5xSsCquRqBO_2_eQ6OK_Zu7P1X4LU7hSHg +TLEDGER_API_URL=https://tledger-sandbox-69bd94a49289.herokuapp.com/api/v1/ +SENDER_TLEDGER_API_KEY=ewSZjNQGPLle-vn5dMZoLOGUljEB6fbmox31o7KLKuI +SENDER_TLEDGER_API_SECRET=iqB7-iETCVBE0UV_0HfRAwCHkVXO9_4cCPJYmTIyUpHauHlVP4Hk5xSsCquRqBO_2_eQ6OK_Zu7P1X4LU7hSHg +RECEIVER_TLEDGER_API_KEY=j06KtBcRRbmrEAqIVSiXZc3DPAJSqymDimo__ERD0oQ +RECEIVER_TLEDGER_API_SECRET=h13ERQG797cYMeNeRLvwDF_3-DBt4o-kp0fL-bFHKstTUTS5xsLUFgDEUZG2GsoEKINxeSVusbAQxc24mHm1eQ +GAME_API_KEY=apt-0804014406b3b03866c996ec1689abea \ No newline at end of file diff --git a/plugins/tLedger/examples/chat_agent.py b/plugins/tLedger/examples/chat_agent.py new file mode 100644 index 00000000..271305bc --- /dev/null +++ b/plugins/tLedger/examples/chat_agent.py @@ -0,0 +1,134 @@ +from typing import Any, Callable, Dict, List, Optional, Tuple +from src.game_sdk.game.custom_types import ActionResponse, FunctionCallResponse, FunctionResult, GameChatResponse, Function, AgentMessage, ChatResponse +from src.game_sdk.game.api_v2 import GAMEClientV2 +from dotenv import load_dotenv +from pathlib import Path + +# Load environment variables from .env file +env_path = Path(__file__).parent / '.env' +load_dotenv(dotenv_path=env_path) + +class Chat: + def __init__( + self, + conversation_id: str, + client: GAMEClientV2, + action_space: Optional[List[Function]] = None, + get_state_fn: Optional[Callable[[], Dict[str, Any]]] = None, + ): + self.chat_id = conversation_id + self.client = client + self.action_space = ( + {f.fn_name: f for f in action_space} if action_space else None + ) + self.get_state_fn = get_state_fn + + def next(self, message: str) -> ChatResponse: + + convo_response = self._update_conversation(message) + + # execute functions/actions if present + if convo_response.function_call: + if not self.action_space: + raise Exception("No functions provided") + + fn_name = convo_response.function_call.fn_name + + fn_to_call = self.action_space.get(fn_name) + if not fn_to_call: + raise Exception( + f"Function {fn_name}, returned by the agent, not found in action space" + ) + + result = fn_to_call.execute( + **{ + "fn_id": convo_response.function_call.id, + "args": convo_response.function_call.args, + } + ) + response_message = self._report_function_result(result) + function_call_response = FunctionCallResponse( + fn_name=fn_name, + fn_args=convo_response.function_call.args, + result=result, + ) + else: + response_message = convo_response.message or "" + function_call_response = None + + return ChatResponse( + message=response_message, + is_finished=convo_response.is_finished, + function_call=function_call_response, + ) + + def end(self, message: Optional[str] = None): + self.client.end_chat( + self.chat_id, + { + "message": message, + }, + ) + + def _update_conversation(self, message: str) -> GameChatResponse: + data = { + "message": message, + "state": self.get_state_fn().__str__() if self.get_state_fn else None, + "functions": ( + [f.get_function_def() for f in self.action_space.values()] + if self.action_space + else None + ), + } + #print(f"Data: {data}") + result = self.client.update_chat(self.chat_id, data) + return GameChatResponse.model_validate(result) + + def _report_function_result(self, result: FunctionResult) -> str: + data = { + "fn_id": result.action_id, + "result": ( + f"{result.action_status.value}: {result.feedback_message}" + if result.feedback_message + else result.action_status.value + ), + } + response = self.client.report_function(self.chat_id, data) + + message = response.get("message") + if not message: + raise Exception("Agent did not return a message for the function report.") + return message + + +class ChatAgent: + def __init__( + self, + api_key: str, + prompt: str, + ): + self._api_key = api_key + self.prompt = prompt + + if api_key.startswith("apt-"): + self.client = GAMEClientV2(api_key) + else: + raise Exception("Please use V2 API key to use ChatAgent") + + def create_chat( + self, + partner_id: str, + partner_name: str, + action_space: Optional[List[Function]] = None, + get_state_fn: Optional[Callable[[], Dict[str, Any]]] = None, + ) -> Chat: + + chat_id = self.client.create_chat( + { + "prompt": self.prompt, + "partner_id": partner_id, + "partner_name": partner_name, + }, + ) + + return Chat(chat_id, self.client, action_space, get_state_fn) diff --git a/plugins/tLedger/examples/example-worker.py b/plugins/tLedger/examples/example-worker.py new file mode 100644 index 00000000..c97b4455 --- /dev/null +++ b/plugins/tLedger/examples/example-worker.py @@ -0,0 +1,67 @@ +import os +import sys +from game_sdk.game.worker import Worker +from game_sdk.game.custom_types import FunctionResult +from dotenv import load_dotenv +from pathlib import Path + +# Load environment variables from .env file +env_path = Path(__file__).parent / '.env.example' +load_dotenv(dotenv_path=env_path) + +# Add the project directory to the PYTHONPATH +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..'))) + +from plugins.tLedger.tledger_plugin_gamesdk.t54_payments_plugin import T54PaymentsPlugin +from plugins.tLedger.tledger_plugin_gamesdk.t54_account_details_plugin import T54AccountDetailsPlugin + +def get_state_fn(function_result: FunctionResult, current_state: dict) -> dict: + """ + Update state based on the function results + """ + init_state = {} + + if current_state is None: + return init_state + + # Update state with the function result info + current_state.update(function_result.info) + + return current_state + + +account_details_plugin = T54AccountDetailsPlugin( + api_key=os.environ.get("TLEDGER_API_KEY"), + api_secret=os.environ.get("TLEDGER_API_SECRET"), + api_url=os.environ.get("TLEDGER_API_URL") +) + +payments_plugin = T54PaymentsPlugin( + api_key=os.environ.get("TLEDGER_API_KEY"), + api_secret=os.environ.get("TLEDGER_API_SECRET"), + api_url = os.environ.get("TLEDGER_API_URL") +) + +# Create worker +tledger_worker = Worker( + api_key=os.environ.get("GAME_API_KEY"), + description="Worker specialized in doing payments on Tledger", + get_state_fn=get_state_fn, + action_space=[ + account_details_plugin.functions.get("get_agent_profile_details"), + payments_plugin.functions.get("create_payment"), + payments_plugin.functions.get("get_payment_by_id"), + ], +) + +# # Run example query +queries = [ + "Get TLedger account details", + "Create payment of 1 SOL using the TLedger account details. The receiving agent's ID is 'agt_7111d3ea-ec7a-4b5d-ba82-d50b14ffa89f', the payment amount is 1, the settlement network is 'solana', the currency is 'sol', and the conversation ID is 'conv1'", + "Get payment by ID. Retrieve the payment ID using the previous query", +] + +for query in queries: + print("-" * 100) + print(f"Query: {query}") + tledger_worker.run(query) diff --git a/plugins/tLedger/examples/example_agent.py b/plugins/tLedger/examples/example_agent.py new file mode 100644 index 00000000..45dc9533 --- /dev/null +++ b/plugins/tLedger/examples/example_agent.py @@ -0,0 +1,188 @@ +import os +import sys +import time +import warnings +from typing import Any, Tuple +from dotenv import load_dotenv +from pathlib import Path + +import requests +# Add the project directory to the PYTHONPATH +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..'))) +from typing_extensions import Dict + +from chat_agent import ChatAgent +from game_sdk.game.custom_types import Argument, Function, FunctionResultStatus, ChatResponse, FunctionResult + +from urllib3.exceptions import NotOpenSSLWarning + + +from plugins.tLedger.tledger_plugin_gamesdk.t54_payments_plugin import T54PaymentsPlugin +from plugins.tLedger.tledger_plugin_gamesdk.t54_account_details_plugin import T54AccountDetailsPlugin + +from colorama import Fore, Style + +# Load environment variables from .env file +env_path = Path(__file__).parent / '.env.example' +load_dotenv(dotenv_path=env_path) + +# Define color variables +AGENT_COLOR = Fore.GREEN # Agent's messages in green +AGENT_ACTION_COLOR = Fore.BLUE # Agent's action messages in green +USER_COLOR = Fore.CYAN # User's messages in cyan +RESET = Style.RESET_ALL # Reset color after each print + + +# ACTION SPACE + +def post_twitter(object: str, **kwargs) -> Tuple[FunctionResultStatus, str, dict]: + """ + Specialized function to throw fruit objects. + + This function demonstrates type-specific actions in the environment. + + Args: + object (str): Name of the fruit to throw. + **kwargs: Additional arguments that might be passed. + + Returns: + Tuple[FunctionResultStatus, str, dict]: Status, feedback message, and state info. + + Example: + status, msg, info = throw_fruit("apple") + """ + return FunctionResultStatus.DONE, f"Successfully posted on twitter!", {} + +sender_account_details_plugin = T54AccountDetailsPlugin( + api_key=os.environ.get("SENDER_TLEDGER_API_KEY"), + api_secret=os.environ.get("SENDER_TLEDGER_API_SECRET"), + api_url=os.environ.get("TLEDGER_API_URL") +) + +sender_payments_plugin = T54PaymentsPlugin( + api_key=os.environ.get("SENDER_TLEDGER_API_KEY"), + api_secret=os.environ.get("SENDER_TLEDGER_API_SECRET"), + api_url = os.environ.get("TLEDGER_API_URL") +) + +receiver_account_details_plugin = T54AccountDetailsPlugin( + api_key=os.environ.get("RECEIVER_TLEDGER_API_KEY"), + api_secret=os.environ.get("RECEIVER_TLEDGER_API_SECRET"), + api_url=os.environ.get("TLEDGER_API_URL") +) + +action_space = [ + sender_account_details_plugin.functions.get("get_agent_profile_details"), + sender_payments_plugin.functions.get("create_payment"), + sender_payments_plugin.functions.get("get_payment_by_id"), +] + +aixbt_action_space = [ + Function( + fn_name="post_twitter", + fn_description="Make post on twitter", + args=[Argument(name="object", type="string", description="Make post on twitter")], + executable=post_twitter + ), + receiver_account_details_plugin.functions.get("get_agent_profile_details") +] + +api_key = os.environ.get("GAME_API_KEY") +if not api_key: + raise ValueError("GAME_API_KEY is not set") + + +# CREATE AGENT +autonomous_agent = ChatAgent( + prompt="You are an agent trying to promote your utility token $PYMT for peer to peer instant settlement on twitter and pay the influencers in any crypto they wish to receive only after they complete the work. Ask the influencer agent_id for its agent_id. Your settlement_network is solana, currency is SOL, and payment_amount is 10", + api_key=api_key +) + +aixbt = ChatAgent( + prompt="You are an influencer on twitter who charges 10 $SOL token to promote an asset by making twitter post. You accept payment only through tLedger Payment Platform via tLedger Agent Id. You can call tLedger to get your agent_details", + api_key=api_key +) + +aixbt_state = {} + +autonomous_agent_state = {} + + +def get_aixbt_state() -> Dict[str, Any]: + return aixbt_state + +def get_autonomous_agent_state() -> Dict[str, Any]: + return autonomous_agent_state + + +aixbt_chat = aixbt.create_chat( + partner_id="Autonomous Agent", + partner_name="Autonomous Agent", + action_space=aixbt_action_space + +) + +autonomous_agent_chat = autonomous_agent.create_chat( + partner_id="Aixbt", + partner_name="Aixbt", + action_space=action_space +) + +chat_continue = True +initialize_conversation: bool = True + + +def update_agent_state(current_state: dict, function_result: FunctionResult) -> Dict[str, Any]: + info = function_result.info + # if not info["payment_id"]: + # current_state["payment_id"] = info["payment_id"] + return current_state + + +while chat_continue: + + warnings.simplefilter("ignore", category=UserWarning) + + meme_agent_turn: bool + + # print(f"Pymt Agent State: {autonomous_agent_chat.get_state_fn()}") + # print(f"Aixbt Agent state: {aixbt_chat.get_state_fn()}") +# chat_response: ChatResponse + if initialize_conversation: + chat_response = autonomous_agent_chat.next("Hi") + if chat_response.message: + print(f"{AGENT_COLOR}Autonomous Response{RESET}: {chat_response.message}") + initialize_conversation = False + meme_agent_turn = False + continue + + + time.sleep(5) # Wait for 5 seconds before retrying + if meme_agent_turn: + updated_response = autonomous_agent_chat.next(chat_response.message) + if updated_response.function_call: + print(f"{AGENT_ACTION_COLOR}Autonomous Agent Action{RESET}: {updated_response.function_call.result.feedback_message}") + #update_agent_state(autonomous_agent_chat.get_state_fn(), updated_response.function_call.result) + + if updated_response.message: + print(f"{AGENT_COLOR}Autonomous Agent Response{RESET}: {updated_response.message}") + meme_agent_turn = False + else: + + updated_response = aixbt_chat.next(chat_response.message) + # if updated_response.function_call: + # print(f"Function call: {updated_response.function_call}") + + if updated_response.message: + print(f"{USER_COLOR}Aixbt Response{RESET}: {updated_response.message}") + meme_agent_turn = True + + chat_response = updated_response + + + + if chat_response.is_finished: + chat_continue = False + break + +print("Chat ended") \ No newline at end of file diff --git a/plugins/tLedger/pyproject.toml b/plugins/tLedger/pyproject.toml new file mode 100644 index 00000000..78ed9f0a --- /dev/null +++ b/plugins/tLedger/pyproject.toml @@ -0,0 +1,34 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "t54_plugin_gamesdk" +version = "0.1.0" +authors = [{ name = "Prateek Tiwari", email = "ptiwari@t54.ai" }, { name = "Akshit Arora", email = "aarora@t54.ai" }] +description = "TLedger SDK for GAME by Virtuals" +requires-python = ">=3.8" +classifiers = [ + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Topic :: Software Development :: Libraries :: Python Modules", +] +dependencies = [ + "aiohttp>=3.11.11", + "game-sdk>=0.1.1" +] + +[tool.hatch.build.targets.wheel] +packages = ["tledger_gamesdk"] + +[project.urls] +"Homepage" = "https://github.com/game-by-virtuals/game-python/plugins/tLedger" +"Bug Tracker" = "https://github.com/game-by-virtuals/game-python" + diff --git a/plugins/tLedger/tledger_plugin_gamesdk/__init__.py b/plugins/tLedger/tledger_plugin_gamesdk/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/plugins/tLedger/tledger_plugin_gamesdk/t54_account_details_plugin.py b/plugins/tLedger/tledger_plugin_gamesdk/t54_account_details_plugin.py new file mode 100644 index 00000000..9bdc4abb --- /dev/null +++ b/plugins/tLedger/tledger_plugin_gamesdk/t54_account_details_plugin.py @@ -0,0 +1,77 @@ +import os +from typing import Dict, Any + +import requests + +from src.game_sdk.game.custom_types import Function, Argument, FunctionResultStatus + +BASE_URL = "https://tledger-sandbox-69bd94a49289.herokuapp.com/api/v1/" + +class T54AccountDetailsPlugin: + + + def __init__( + self, + api_key: str = os.environ.get("TLEDGER_API_KEY"), + api_secret: str = os.environ.get("TLEDGER_API_SECRET"), + api_url: str = BASE_URL, + ): + self.api_key = api_key + self.api_url = api_url + self.api_secret = api_secret + + # Available client functions + self.functions: Dict[str, Function] = { + "get_agent_profile_details": Function( + fn_name="get_agent_profile_details", + fn_description="Get agent profile details", + hint="This function is used to get the agent profile details for a given agent", + executable=self.get_agent_profile_details, + args=[], + ), + } + + def get_agent_profile_details(self, **kwargs) -> tuple[FunctionResultStatus, str, dict[str, str]] | tuple[ + FunctionResultStatus, str, dict[str, Any]]: + """ Get agent profile details for a given agent + + Returns: + + """ + + # Prepare headers for the request + headers = { + "X-API-Key": self.api_key, + "X-API-Secret": self.api_secret, + "Content-Type": "application/json", + } + + try: + + url = f"{self.api_url}agent_details" + + # Make the API request + response = requests.get(url, headers=headers) + response.raise_for_status() + + # Extract the image URL from the response + response_data = response.json() + agent_profile_details = response_data + + return ( + FunctionResultStatus.DONE, + f"The agent details are: {agent_profile_details}", + { + "agent_details": agent_profile_details, + }, + ) + except Exception as e: + print(f"An error occurred while getting the agent details: {str(e)}") + return ( + FunctionResultStatus.FAILED, + f"An error occurred while getting the agent details: {str(e)}", + { + "tledger_url": url, + }, + ) + diff --git a/plugins/tLedger/tledger_plugin_gamesdk/t54_payments_plugin.py b/plugins/tLedger/tledger_plugin_gamesdk/t54_payments_plugin.py new file mode 100644 index 00000000..c035cab0 --- /dev/null +++ b/plugins/tLedger/tledger_plugin_gamesdk/t54_payments_plugin.py @@ -0,0 +1,222 @@ +import os +from typing import Dict, Any + +import requests + +from src.game_sdk.game.custom_types import Function, Argument, FunctionResultStatus + +BASE_URL = "https://tledger-sandbox-69bd94a49289.herokuapp.com/api/v1/" + + +class T54PaymentsPlugin: + + + def __init__( + self, + api_key: str, + api_secret: str, + api_url: str, + ): + self.api_key = api_key + self.api_url = api_url + self.api_secret = api_secret + + # Available client functions + self.functions: Dict[str, Function] = { + "create_payment": Function( + fn_name="create_payment", + fn_description="Create payment", + hint="This function is used to get the agent profile details for a given agent", + executable=self.create_payment, + args=[ + Argument( + name="request_id", + description="Unique ID for requesting the payment to ensure idempotence", + type="string", + ), + Argument( + name="receiving_agent_id", + description="The Unique ID of the receiving agent.", + type="string", + ), + Argument( + name="payment_amount", + description="Amount to be paid", + type="float", + ), + Argument( + name="settlement_network", + description="network used for settlement", + type="string", + ), + Argument( + name="currency", + description="Currency used in the crypto bridge", + type="string", + ), + Argument( + name="conversation_id", + description="Conversation ID to correlate the agent conversation", + type="string", + ), + ], + ), + "get_payment_by_id": Function( + fn_name="get_payment_by_id", + fn_description="Get payment", + hint="This function is used to get the payment by payment id", + executable=self.get_payment_by_id, + args=[ + Argument( + name="payment_id", + description="Unique ID for the payment", + type="string", + ) + ], + ), + "get_payments": Function( + fn_name="get_payments", + fn_description="Get payments", + hint="This function is used to get all the payments", + executable=self.get_payments, + args=[], + ), + } + + def create_payment(self, request_id: str, receiving_agent_id: str, payment_amount: float, settlement_network: str, currency: str, conversation_id: str, **kwargs) -> \ + tuple[FunctionResultStatus, str, dict[str, Any]] | tuple[FunctionResultStatus, str, dict[Any, Any]]: + """Generate image based on prompt. + + Returns: + str URL of image (need to save since temporal) + """ + # API endpoint for image generation + # Prepare headers for the request + headers = { + "X-API-Key": self.api_key, + "X-API-Secret": self.api_secret, + "Content-Type": "application/json", + } + + # Prepare request payload + payload = { + "model": "black-forest-labs/FLUX.1-schnell-Free", + "request_id": request_id, + "receiving_agent_id": receiving_agent_id, + "payment_amount": payment_amount, + "settlement_network": settlement_network, + "currency": currency, + "conversation_id": conversation_id, + } + + try: + + url = self.api_url + "payment" + + # Make the API request + response = requests.post(url, headers=headers, json=payload) + response.raise_for_status() + + # Extract the image URL from the response + response_data = response.json() + + return ( + FunctionResultStatus.DONE, + f"The create payment response is: {response_data}", + { + "response": response_data, + }, + ) + except Exception as e: + print(f"An error occurred while generating image: {str(e)}") + return ( + FunctionResultStatus.FAILED, + f"An error occurred while while generating image: {str(e)}", + { + }, + ) + + def get_payment_by_id(self, payment_id: str, **kwargs) -> \ + tuple[FunctionResultStatus, str, dict[str, Any]] | tuple[FunctionResultStatus, str, dict[Any, Any]]: + """Generate image based on prompt. + + Returns: + str URL of image (need to save since temporal) + """ + # API endpoint for image generation + # Prepare headers for the request + headers = { + "X-API-Key": self.api_key, + "X-API-Secret": self.api_secret, + "Content-Type": "application/json", + } + + get_url = self.api_url + f"payment/{payment_id}" + + try: + # Make the API request + response = requests.get(get_url, headers=headers) + response.raise_for_status() + + # Extract the image URL from the response + response_data = response.json() + + return ( + FunctionResultStatus.DONE, + f"The get payment response is: {response_data}", + { + "response": response_data, + }, + ) + except Exception as e: + print(f"An error occurred getting payment: {str(e)}") + return ( + FunctionResultStatus.FAILED, + f"An error occurred while while getting payment: {str(e)}", + { + "payment_id": payment_id, + "get_url": get_url, + }, + ) + + def get_payments(self, **kwargs) -> \ + tuple[FunctionResultStatus, str, dict[str, Any]] | tuple[FunctionResultStatus, str, dict[Any, Any]]: + """Generate image based on prompt. + + Returns: + str URL of image (need to save since temporal) + """ + # API endpoint for image generation + # Prepare headers for the request + headers = { + "X-API-Key": self.api_key, + "X-API-Secret": self.api_secret, + "Content-Type": "application/json", + } + + get_url = self.api_url + "payments" + + try: + # Make the API request + response = requests.get(get_url, headers=headers) + response.raise_for_status() + + # Extract the image URL from the response + response_data = response.json() + + return ( + FunctionResultStatus.DONE, + f"The get payments response is: {response_data}", + { + "response": response_data, + }, + ) + except Exception as e: + print(f"An error occurred while getting payments: {str(e)}") + return ( + FunctionResultStatus.FAILED, + f"An error occurred while getting payments: {str(e)}", + { + "get_url": get_url, + }, + ) diff --git a/plugins/tLedger/tledger_plugin_gamesdk/tLedger_models.py b/plugins/tLedger/tledger_plugin_gamesdk/tLedger_models.py new file mode 100644 index 00000000..515cc3c8 --- /dev/null +++ b/plugins/tLedger/tledger_plugin_gamesdk/tLedger_models.py @@ -0,0 +1,41 @@ +from datetime import datetime +from typing import Optional + +from pydantic import BaseModel +from sqlmodel import Field + +class PaymentResponse(BaseModel): + object: str = "payment" + id: str = Field(..., description="Unique ID for the payment.") + status: str = Field(..., description="Current status of the payment.") + payment_amount: float = Field(..., gt=0, description="The amount for the payment, must be positive.") + sending_agent_id: str = Field(..., description="Unique ID of the sending agent.") + sending_agent_name: str = Field(..., description="Name of the sending agent.") + receiving_agent_id: str = Field(..., description="Unique ID of the receiving agent.") + receiving_agent_name: str = Field(..., description="Name of the receiving agent.") + settlement_network: str = Field(..., description="network used for settlement.") + currency: str = Field(..., description="Currency used in the crypto bridge (e.g., USDT, USDC, BTC, ETH).") + transaction_fee: float = Field(..., ge=0, description="Transaction fee for the payment, must be non-negative.") + conversation_id: str = Field(..., description="Identifier for the conversation linked to the payment.") + transaction_hash: Optional[str] = Field(..., description="Transaction hash for the payment on the network") + created_at: datetime = Field(..., description="The entity create timestamp") + updated_at: datetime = Field(..., description="The entity update timestamp") + +class AssetAccountBase(BaseModel): + object: str = "account" + id: str = Field(..., description="Unique identifier for the asset account.") + balance: float = Field(0.0, ge=0, description="Current balance, must be non-negative.") + asset: str = Field(..., max_length=50, description="Asset of the virtual currency.") + created_at: datetime = Field(..., description="The entity create timestamp") + updated_at: datetime = Field(..., description="The entity update timestamp") + network: str = "Solana" + +class AssetAccountRead(AssetAccountBase): + wallet_address: str = Field(default=None, max_length=255, description="network address for agent wallet") + +class AgentDataPlaneResponse(BaseModel): + object: str = "agent_details" + id: str = Field(description="Unique identifier for the agent") + agent_type: str = Field() + account: list[AssetAccountRead] = Field(..., description="Agent account balance information") + From da933c77e8f087dcf176e7fa72c185fbc79b17ed Mon Sep 17 00:00:00 2001 From: Akshit Arora <46507859+aarora0211@users.noreply.github.com> Date: Thu, 20 Mar 2025 23:43:32 -0400 Subject: [PATCH 02/19] Script for admin setup --- plugins/tLedger/.env.setup | 5 + plugins/tLedger/examples/example-worker.py | 2 +- plugins/tLedger/examples/example_agent.py | 15 ++- plugins/tLedger/setup.py | 108 +++++++++++++++++++++ 4 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 plugins/tLedger/.env.setup create mode 100644 plugins/tLedger/setup.py diff --git a/plugins/tLedger/.env.setup b/plugins/tLedger/.env.setup new file mode 100644 index 00000000..b605d0db --- /dev/null +++ b/plugins/tLedger/.env.setup @@ -0,0 +1,5 @@ +# .env.setup + +EMAIL=user@example.com +PASSWORD=password +FULL_NAME=Dummy User \ No newline at end of file diff --git a/plugins/tLedger/examples/example-worker.py b/plugins/tLedger/examples/example-worker.py index c97b4455..f99b0152 100644 --- a/plugins/tLedger/examples/example-worker.py +++ b/plugins/tLedger/examples/example-worker.py @@ -57,7 +57,7 @@ def get_state_fn(function_result: FunctionResult, current_state: dict) -> dict: # # Run example query queries = [ "Get TLedger account details", - "Create payment of 1 SOL using the TLedger account details. The receiving agent's ID is 'agt_7111d3ea-ec7a-4b5d-ba82-d50b14ffa89f', the payment amount is 1, the settlement network is 'solana', the currency is 'sol', and the conversation ID is 'conv1'", + "Create payment of 1 SOL using the TLedger account details. The receiving agent's ID is 'agt_3db52291-a9f8-4f04-a180-adb6e50ef5b0', the payment amount is 1, the settlement network is 'solana', the currency is 'sol', and the conversation ID is 'conv1'", "Get payment by ID. Retrieve the payment ID using the previous query", ] diff --git a/plugins/tLedger/examples/example_agent.py b/plugins/tLedger/examples/example_agent.py index 45dc9533..a9d22a23 100644 --- a/plugins/tLedger/examples/example_agent.py +++ b/plugins/tLedger/examples/example_agent.py @@ -35,7 +35,8 @@ # ACTION SPACE -def post_twitter(object: str, **kwargs) -> Tuple[FunctionResultStatus, str, dict]: +def post_twitter(object: str, **kwargs) -> tuple[FunctionResultStatus, str, dict[str, str]] | tuple[ + FunctionResultStatus, str, dict[str, Any]]: """ Specialized function to throw fruit objects. @@ -51,7 +52,13 @@ def post_twitter(object: str, **kwargs) -> Tuple[FunctionResultStatus, str, dict Example: status, msg, info = throw_fruit("apple") """ - return FunctionResultStatus.DONE, f"Successfully posted on twitter!", {} + return ( + FunctionResultStatus.DONE, + f"Successfully posted on twitter!", + { + "post": "Token promoted", + }, + ) sender_account_details_plugin = T54AccountDetailsPlugin( api_key=os.environ.get("SENDER_TLEDGER_API_KEY"), @@ -99,7 +106,7 @@ def post_twitter(object: str, **kwargs) -> Tuple[FunctionResultStatus, str, dict ) aixbt = ChatAgent( - prompt="You are an influencer on twitter who charges 10 $SOL token to promote an asset by making twitter post. You accept payment only through tLedger Payment Platform via tLedger Agent Id. You can call tLedger to get your agent_details", + prompt="You are an influencer on twitter who charges 1 $SOL token to promote an asset by making twitter post. You accept payment only through tLedger Payment Platform via tLedger Agent Id. You can call tLedger to get your agent_details", api_key=api_key ) @@ -141,6 +148,8 @@ def update_agent_state(current_state: dict, function_result: FunctionResult) -> while chat_continue: + time.sleep(10) # Wait for 10 seconds before making request + warnings.simplefilter("ignore", category=UserWarning) meme_agent_turn: bool diff --git a/plugins/tLedger/setup.py b/plugins/tLedger/setup.py new file mode 100644 index 00000000..6d93a27b --- /dev/null +++ b/plugins/tLedger/setup.py @@ -0,0 +1,108 @@ +import os + +import requests +from dotenv import load_dotenv +from pathlib import Path + +# Load environment variables from .env file +env_path = Path(__file__).parent / '.env.setup' +load_dotenv(dotenv_path=env_path) + +BASE_URL = "https://tledger-sandbox-69bd94a49289.herokuapp.com/api/v1" + +def register_user(email: str, password: str, full_name: str) -> dict: + url = f"{BASE_URL}/users/signup" + payload = { + "email": email, + "password": password, + "full_name": full_name + } + response = requests.post(url, json=payload) + response.raise_for_status() + return response.json() + +def login_user(username: str, password: str) -> str: + url = f"{BASE_URL}/login/access-token" + payload = { + "username": username, + "password": password + } + headers = { + "Content-Type": "application/x-www-form-urlencoded" + } + response = requests.post(url, data=payload, headers=headers) + response.raise_for_status() + return response.json()["access_token"] + +def create_project(token, user_profile_id, network, description, name, daily_limit) -> dict: + url = f"{BASE_URL}/projects" + payload = { + "user_profile_id": user_profile_id, + "network": network, + "description": description, + "name": name, + "daily_limit": daily_limit + } + headers = { + "Authorization": f"Bearer {token}" + } + response = requests.post(url, json=payload, headers=headers) + response.raise_for_status() + return response.json() + +def create_agent_profile(token, project_id, name, description) -> dict: + url = f"{BASE_URL}/agent_profiles" + payload = { + "project_id": project_id, + "name": name, + "description": description + } + headers = { + "Authorization": f"Bearer {token}" + } + response = requests.post(url, json=payload, headers=headers) + response.raise_for_status() + return response.json() + +def generate_api_key(token, agent_id, created_by) -> dict: + url = f"{BASE_URL}/api_key/generate-api-key" + payload = { + "scopes": ["payments:read", "payments:write", "payments:agent:read"], + "agent_id": agent_id, + "created_by": created_by + } + headers = { + "Authorization": f"Bearer {token}" + } + response = requests.post(url, json=payload, headers=headers) + response.raise_for_status() + return response.json() + +# Example usage +email = os.environ.get("EMAIL") +password = os.environ.get("PASSWORD") +full_name = os.environ.get("FULL_NAME") + +# Register user +user = register_user(email, password, full_name) + +# Login user and get JWT token +token = login_user(email, password) + +# Create project +project = create_project(token, user["id"], "solana", "Solana Launch Pad", "Twitter Project", 100) +project_id = project["id"] + +# Create agent profile +agent_profile = create_agent_profile(token, project_id, "My Agent", "Twitter KOL Agent") +agent_id = agent_profile["id"] + +# Generate API key for agent +api_key = generate_api_key(token, agent_id, full_name) + +print("Setup complete") +print(f"Project ID: {project_id}") +print(f"Agent ID: {agent_id}") +print(f"API Key: {api_key}") + +print(f"To add funds to your solana wallet, visit https://faucet.solana.com/") \ No newline at end of file From ff1e5970e58389ed36dfb58f6e8d19cf33b9c0a9 Mon Sep 17 00:00:00 2001 From: Akshit Arora <46507859+aarora0211@users.noreply.github.com> Date: Fri, 21 Mar 2025 00:03:24 -0400 Subject: [PATCH 03/19] Add to readme --- plugins/tLedger/README.md | 12 +++++++----- .../{example-worker.py => example_worker.py} | 0 2 files changed, 7 insertions(+), 5 deletions(-) rename plugins/tLedger/examples/{example-worker.py => example_worker.py} (100%) diff --git a/plugins/tLedger/README.md b/plugins/tLedger/README.md index 8652d27f..2a6137a7 100644 --- a/plugins/tLedger/README.md +++ b/plugins/tLedger/README.md @@ -11,7 +11,13 @@ You are required to set up your account, project, agent profile, and keys in the TLedger platform to use the TLedger plugin for GAME SDK. -To make testing easier, there are two agents already set up in the TLedger sandbox environment. You can use these agents to test the TLedger plugin. The agent details are as follows: +To make the setup easy, all you need to do is run the setup.py file in the tLedger plugin folder. This will install the plugin and set up the necessary environment variables for you. +To set up the necessary environment variables, please fill in thw details in the .env.setup file + +```shell +python3 ./setup.py +``` +There are also two agents set for you incase you want to test the plugin. The agent details are as follows: Agent 1: - Agent ID: agt_59b17650-a689-4649-91fa-4bf5d0db56ad @@ -23,10 +29,6 @@ Agent 2: - key: j06KtBcRRbmrEAqIVSiXZc3DPAJSqymDimo__ERD0oQ - secret: h13ERQG797cYMeNeRLvwDF_3-DBt4o-kp0fL-bFHKstTUTS5xsLUFgDEUZG2GsoEKINxeSVusbAQxc24mHm1eQ -If you want to set your own TLedger agents, the following steps will guide you through the setup process. - -TLedger Sandbox URL = https://tledger-sandbox-69bd94a49289.herokuapp.com/ - ### TLedger Account Setup 1. You first need to register your user on the TLedger platform diff --git a/plugins/tLedger/examples/example-worker.py b/plugins/tLedger/examples/example_worker.py similarity index 100% rename from plugins/tLedger/examples/example-worker.py rename to plugins/tLedger/examples/example_worker.py From dfe23cfa03d6cd5ebd51e0f066b2101a30a4fdab Mon Sep 17 00:00:00 2001 From: Akshit Arora <46507859+aarora0211@users.noreply.github.com> Date: Sat, 22 Mar 2025 18:40:01 -0400 Subject: [PATCH 04/19] Changes for setup based on feedback --- plugins/tLedger/examples/.env.example | 5 +--- plugins/tLedger/examples/example_worker.py | 8 +++---- plugins/tLedger/setup.py | 27 ++++++++++++++++------ 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/plugins/tLedger/examples/.env.example b/plugins/tLedger/examples/.env.example index daf15bff..0cdf38d3 100644 --- a/plugins/tLedger/examples/.env.example +++ b/plugins/tLedger/examples/.env.example @@ -1,8 +1,5 @@ -TLEDGER_API_KEY=ewSZjNQGPLle-vn5dMZoLOGUljEB6fbmox31o7KLKuI -TLEDGER_API_SECRET=iqB7-iETCVBE0UV_0HfRAwCHkVXO9_4cCPJYmTIyUpHauHlVP4Hk5xSsCquRqBO_2_eQ6OK_Zu7P1X4LU7hSHg TLEDGER_API_URL=https://tledger-sandbox-69bd94a49289.herokuapp.com/api/v1/ SENDER_TLEDGER_API_KEY=ewSZjNQGPLle-vn5dMZoLOGUljEB6fbmox31o7KLKuI SENDER_TLEDGER_API_SECRET=iqB7-iETCVBE0UV_0HfRAwCHkVXO9_4cCPJYmTIyUpHauHlVP4Hk5xSsCquRqBO_2_eQ6OK_Zu7P1X4LU7hSHg RECEIVER_TLEDGER_API_KEY=j06KtBcRRbmrEAqIVSiXZc3DPAJSqymDimo__ERD0oQ -RECEIVER_TLEDGER_API_SECRET=h13ERQG797cYMeNeRLvwDF_3-DBt4o-kp0fL-bFHKstTUTS5xsLUFgDEUZG2GsoEKINxeSVusbAQxc24mHm1eQ -GAME_API_KEY=apt-0804014406b3b03866c996ec1689abea \ No newline at end of file +RECEIVER_TLEDGER_API_SECRET=h13ERQG797cYMeNeRLvwDF_3-DBt4o-kp0fL-bFHKstTUTS5xsLUFgDEUZG2GsoEKINxeSVusbAQxc24mHm1eQ \ No newline at end of file diff --git a/plugins/tLedger/examples/example_worker.py b/plugins/tLedger/examples/example_worker.py index f99b0152..a07dcca0 100644 --- a/plugins/tLedger/examples/example_worker.py +++ b/plugins/tLedger/examples/example_worker.py @@ -31,14 +31,14 @@ def get_state_fn(function_result: FunctionResult, current_state: dict) -> dict: account_details_plugin = T54AccountDetailsPlugin( - api_key=os.environ.get("TLEDGER_API_KEY"), - api_secret=os.environ.get("TLEDGER_API_SECRET"), + api_key=os.environ.get("SENDER_TLEDGER_API_KEY"), + api_secret=os.environ.get("SENDER_TLEDGER_API_SECRET"), api_url=os.environ.get("TLEDGER_API_URL") ) payments_plugin = T54PaymentsPlugin( - api_key=os.environ.get("TLEDGER_API_KEY"), - api_secret=os.environ.get("TLEDGER_API_SECRET"), + api_key=os.environ.get("SENDER_TLEDGER_API_KEY"), + api_secret=os.environ.get("SENDER_TLEDGER_API_SECRET"), api_url = os.environ.get("TLEDGER_API_URL") ) diff --git a/plugins/tLedger/setup.py b/plugins/tLedger/setup.py index 6d93a27b..f6c29894 100644 --- a/plugins/tLedger/setup.py +++ b/plugins/tLedger/setup.py @@ -83,8 +83,14 @@ def generate_api_key(token, agent_id, created_by) -> dict: password = os.environ.get("PASSWORD") full_name = os.environ.get("FULL_NAME") -# Register user -user = register_user(email, password, full_name) +try: + # Register user + user = register_user(email, password, full_name) +except requests.HTTPError as e: + print(f"User already exists: {e}") + user = { + "id": "user_id" + } # Login user and get JWT token token = login_user(email, password) @@ -94,15 +100,22 @@ def generate_api_key(token, agent_id, created_by) -> dict: project_id = project["id"] # Create agent profile -agent_profile = create_agent_profile(token, project_id, "My Agent", "Twitter KOL Agent") -agent_id = agent_profile["id"] +agent_profile_sender = create_agent_profile(token, project_id, "Sending Agent", "Sending agent") +agent_id_sender = agent_profile_sender["id"] + +# Create agent profile +agent_profile_receiver = create_agent_profile(token, project_id, "Receiving Agent", "Twitter KOL Agent") +agent_id_receiver = agent_profile_receiver["id"] + +# Generate API key for agent +api_key_sender = generate_api_key(token, agent_id_sender, full_name) # Generate API key for agent -api_key = generate_api_key(token, agent_id, full_name) +api_key_receiver = generate_api_key(token, agent_id_receiver, full_name) print("Setup complete") print(f"Project ID: {project_id}") -print(f"Agent ID: {agent_id}") -print(f"API Key: {api_key}") +print(f"Sender Agent ID: {agent_id_sender}. Solana address: {agent_profile_sender["account"][0]["wallet_address"]}. Sender API Key: {api_key_sender}") +print(f"Receiver Agent ID: {agent_id_receiver}. Solana address: {agent_profile_receiver["account"][0]["wallet_address"]}. Receiver API Key: {api_key_receiver}") print(f"To add funds to your solana wallet, visit https://faucet.solana.com/") \ No newline at end of file From 429a6ea480cd284204a7ad070fdc90fdfe02cb4f Mon Sep 17 00:00:00 2001 From: Akshit Arora <46507859+aarora0211@users.noreply.github.com> Date: Sat, 22 Mar 2025 18:43:07 -0400 Subject: [PATCH 05/19] Clean readme --- plugins/tLedger/README.md | 68 ++++----------------------------------- 1 file changed, 6 insertions(+), 62 deletions(-) diff --git a/plugins/tLedger/README.md b/plugins/tLedger/README.md index 2a6137a7..60728c7d 100644 --- a/plugins/tLedger/README.md +++ b/plugins/tLedger/README.md @@ -30,65 +30,9 @@ Agent 2: - secret: h13ERQG797cYMeNeRLvwDF_3-DBt4o-kp0fL-bFHKstTUTS5xsLUFgDEUZG2GsoEKINxeSVusbAQxc24mHm1eQ ### TLedger Account Setup +For a complete list of TLedger setup APIs, please feel free to look at the public documentation at: https://docs.t54.ai/ -1. You first need to register your user on the TLedger platform - -POST /api/v1/users/signup -payload = { - "email": "user@example.com", - "password": "password", - "full_name": "full name" -} - -2. Login to your account and obtain the JWT token - -POST /api/v1/login/access-token -payload = { - "username": "user@example.com", - "password": "password" -} -headers = { - "Content-Type": "application/x-www-form-urlencoded" -} - -### TLedger Project Setup - -1. Create a project - -POST /api/v1/projects -payload = { - "user_profile_id": "usr_123", # user_profile_id from the user profile - "network": "solana", - "description": "Solana Launch Pad", - "name": "Twitter Project", - "daily_limit": 100 -} - -### TLedger Agent Profile Setup - -1. Create an agent profile - -POST /api/v1/agent_profiles -payload = { - "project_id": "{{project_id}}", - "name": "My Agent", - "description": "Twitter KOL Agent" -} - -### TLedger Key Setup for Agent - -1. You need to create an API key for the agent to use the TLedger APIs - -POST /api/v1/api_key/generate-api-key -{ - "scopes": ["payments:read", "payments:write", "payments:agent:read"], - "agent_id": "agt_123456", - "created_by": "Name" -} - -For a complete list of TLedger APIs, please feel free to look at the public documentation at: https://docs.t54.ai/ - -### Setup and Configuration +### Toolkit Setup and Configuration Import and initialize the plugin to use in your worker: @@ -98,14 +42,14 @@ from plugins.tLedger.tledger_plugin_gamesdk.t54_payments_plugin import T54Paymen from plugins.tLedger.tledger_plugin_gamesdk.t54_account_details_plugin import T54AccountDetailsPlugin account_details_plugin = T54AccountDetailsPlugin( - api_key=os.environ.get("TLEDGER_API_KEY"), - api_secret=os.environ.get("TLEDGER_API_SECRET"), + api_key=os.environ.get("SENDER_TLEDGER_API_KEY"), + api_secret=os.environ.get("SENDER_TLEDGER_API_SECRET"), api_url=os.environ.get("TLEDGER_API_URL") ) payments_plugin = T54PaymentsPlugin( - api_key=os.environ.get("TLEDGER_API_KEY"), - api_secret=os.environ.get("TLEDGER_API_SECRET"), + api_key=os.environ.get("SENDER_TLEDGER_API_KEY"), + api_secret=os.environ.get("SENDER_TLEDGER_API_SECRET"), api_url = os.environ.get("TLEDGER_API_URL") ) ``` From 6398c276f61bd0b4534c16bd4dff154fe5297d90 Mon Sep 17 00:00:00 2001 From: Akshit Arora <46507859+aarora0211@users.noreply.github.com> Date: Sun, 23 Mar 2025 18:09:27 -0400 Subject: [PATCH 06/19] Consolidate plugins into a single plugin --- plugins/tLedger/examples/example_agent.py | 21 +- plugins/tLedger/examples/example_worker.py | 16 +- .../tledger_plugin_gamesdk/tLedger_plugin.py | 320 ++++++++++++++++++ 3 files changed, 332 insertions(+), 25 deletions(-) create mode 100644 plugins/tLedger/tledger_plugin_gamesdk/tLedger_plugin.py diff --git a/plugins/tLedger/examples/example_agent.py b/plugins/tLedger/examples/example_agent.py index a9d22a23..6f434f1a 100644 --- a/plugins/tLedger/examples/example_agent.py +++ b/plugins/tLedger/examples/example_agent.py @@ -17,8 +17,7 @@ from urllib3.exceptions import NotOpenSSLWarning -from plugins.tLedger.tledger_plugin_gamesdk.t54_payments_plugin import T54PaymentsPlugin -from plugins.tLedger.tledger_plugin_gamesdk.t54_account_details_plugin import T54AccountDetailsPlugin +from plugins.tLedger.tledger_plugin_gamesdk.tLedger_plugin import TLedgerPlugin from colorama import Fore, Style @@ -60,28 +59,22 @@ def post_twitter(object: str, **kwargs) -> tuple[FunctionResultStatus, str, dict }, ) -sender_account_details_plugin = T54AccountDetailsPlugin( +sender_tledger_plugin = TLedgerPlugin( api_key=os.environ.get("SENDER_TLEDGER_API_KEY"), api_secret=os.environ.get("SENDER_TLEDGER_API_SECRET"), api_url=os.environ.get("TLEDGER_API_URL") ) -sender_payments_plugin = T54PaymentsPlugin( - api_key=os.environ.get("SENDER_TLEDGER_API_KEY"), - api_secret=os.environ.get("SENDER_TLEDGER_API_SECRET"), - api_url = os.environ.get("TLEDGER_API_URL") -) - -receiver_account_details_plugin = T54AccountDetailsPlugin( +receiver_tledger_plugin = TLedgerPlugin( api_key=os.environ.get("RECEIVER_TLEDGER_API_KEY"), api_secret=os.environ.get("RECEIVER_TLEDGER_API_SECRET"), api_url=os.environ.get("TLEDGER_API_URL") ) action_space = [ - sender_account_details_plugin.functions.get("get_agent_profile_details"), - sender_payments_plugin.functions.get("create_payment"), - sender_payments_plugin.functions.get("get_payment_by_id"), + sender_tledger_plugin.functions.get("get_agent_profile_details"), + sender_tledger_plugin.functions.get("create_payment"), + sender_tledger_plugin.functions.get("get_payment_by_id"), ] aixbt_action_space = [ @@ -91,7 +84,7 @@ def post_twitter(object: str, **kwargs) -> tuple[FunctionResultStatus, str, dict args=[Argument(name="object", type="string", description="Make post on twitter")], executable=post_twitter ), - receiver_account_details_plugin.functions.get("get_agent_profile_details") + receiver_tledger_plugin.functions.get("get_agent_profile_details") ] api_key = os.environ.get("GAME_API_KEY") diff --git a/plugins/tLedger/examples/example_worker.py b/plugins/tLedger/examples/example_worker.py index a07dcca0..3fc3fa4a 100644 --- a/plugins/tLedger/examples/example_worker.py +++ b/plugins/tLedger/examples/example_worker.py @@ -12,8 +12,7 @@ # Add the project directory to the PYTHONPATH sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..'))) -from plugins.tLedger.tledger_plugin_gamesdk.t54_payments_plugin import T54PaymentsPlugin -from plugins.tLedger.tledger_plugin_gamesdk.t54_account_details_plugin import T54AccountDetailsPlugin +from plugins.tLedger.tledger_plugin_gamesdk.tLedger_plugin import TLedgerPlugin def get_state_fn(function_result: FunctionResult, current_state: dict) -> dict: """ @@ -30,13 +29,8 @@ def get_state_fn(function_result: FunctionResult, current_state: dict) -> dict: return current_state -account_details_plugin = T54AccountDetailsPlugin( - api_key=os.environ.get("SENDER_TLEDGER_API_KEY"), - api_secret=os.environ.get("SENDER_TLEDGER_API_SECRET"), - api_url=os.environ.get("TLEDGER_API_URL") -) -payments_plugin = T54PaymentsPlugin( +tledger_plugin = TLedgerPlugin( api_key=os.environ.get("SENDER_TLEDGER_API_KEY"), api_secret=os.environ.get("SENDER_TLEDGER_API_SECRET"), api_url = os.environ.get("TLEDGER_API_URL") @@ -48,9 +42,9 @@ def get_state_fn(function_result: FunctionResult, current_state: dict) -> dict: description="Worker specialized in doing payments on Tledger", get_state_fn=get_state_fn, action_space=[ - account_details_plugin.functions.get("get_agent_profile_details"), - payments_plugin.functions.get("create_payment"), - payments_plugin.functions.get("get_payment_by_id"), + tledger_plugin.functions.get("get_agent_profile_details"), + tledger_plugin.functions.get("create_payment"), + tledger_plugin.functions.get("get_payment_by_id"), ], ) diff --git a/plugins/tLedger/tledger_plugin_gamesdk/tLedger_plugin.py b/plugins/tLedger/tledger_plugin_gamesdk/tLedger_plugin.py new file mode 100644 index 00000000..79bf501c --- /dev/null +++ b/plugins/tLedger/tledger_plugin_gamesdk/tLedger_plugin.py @@ -0,0 +1,320 @@ +import os +from typing import Dict, Any + +import requests + +from src.game_sdk.game.custom_types import Function, Argument, FunctionResultStatus + +BASE_URL = "https://api-sandbox.t54.ai/api/v1/" + + +class TLedgerPlugin: + + + def __init__( + self, + api_key: str, + api_secret: str, + api_url: str, + ): + self.api_key = api_key + self.api_url = api_url + self.api_secret = api_secret + + # Available client functions + self.functions: Dict[str, Function] = { + "create_payment": Function( + fn_name="create_payment", + fn_description="Create payment", + hint="This function is used to get the agent profile details for a given agent", + executable=self.create_payment, + args=[ + Argument( + name="request_id", + description="Unique ID for requesting the payment to ensure idempotence", + type="string", + ), + Argument( + name="receiving_agent_id", + description="The Unique ID of the receiving agent.", + type="string", + ), + Argument( + name="payment_amount", + description="Amount to be paid", + type="float", + ), + Argument( + name="settlement_network", + description="network used for settlement", + type="string", + ), + Argument( + name="currency", + description="Currency used in the crypto bridge", + type="string", + ), + Argument( + name="conversation_id", + description="Conversation ID to correlate the agent conversation", + type="string", + ), + ], + ), + "get_payment_by_id": Function( + fn_name="get_payment_by_id", + fn_description="Get payment", + hint="This function is used to get the payment by payment id", + executable=self.get_payment_by_id, + args=[ + Argument( + name="payment_id", + description="Unique ID for the payment", + type="string", + ) + ], + ), + "get_payments": Function( + fn_name="get_payments", + fn_description="Get payments", + hint="This function is used to get all the payments", + executable=self.get_payments, + args=[], + ), + + "get_agent_profile_details": Function( + fn_name="get_agent_profile_details", + fn_description="Get agent profile details", + hint="This function is used to get the agent profile details for a given agent", + executable=self.get_agent_profile_details, + args=[], + ), + } + + def create_payment(self, request_id: str, receiving_agent_id: str, payment_amount: float, settlement_network: str, currency: str, conversation_id: str, **kwargs) -> \ + tuple[FunctionResultStatus, str, dict[str, Any]] | tuple[FunctionResultStatus, str, dict[Any, Any]]: + """Generate image based on prompt. + + Returns: + str URL of image (need to save since temporal) + """ + # API endpoint for image generation + # Prepare headers for the request + headers = { + "X-API-Key": self.api_key, + "X-API-Secret": self.api_secret, + "Content-Type": "application/json", + } + + # Prepare request payload + payload = { + "model": "black-forest-labs/FLUX.1-schnell-Free", + "request_id": request_id, + "receiving_agent_id": receiving_agent_id, + "payment_amount": payment_amount, + "settlement_network": settlement_network, + "currency": currency, + "conversation_id": conversation_id, + } + + try: + + url = self.api_url + "payment" + + # Make the API request + response = requests.post(url, headers=headers, json=payload) + response.raise_for_status() + + # Extract the image URL from the response + response_data = response.json() + + return ( + FunctionResultStatus.DONE, + f"The create payment response is: {response_data}", + { + "response": response_data, + }, + ) + except Exception as e: + print(f"An error occurred while generating image: {str(e)}") + return ( + FunctionResultStatus.FAILED, + f"An error occurred while while generating image: {str(e)}", + { + }, + ) + + def get_payment_by_id(self, payment_id: str, **kwargs) -> \ + tuple[FunctionResultStatus, str, dict[str, Any]] | tuple[FunctionResultStatus, str, dict[Any, Any]]: + """Generate image based on prompt. + + Returns: + str URL of image (need to save since temporal) + """ + # API endpoint for image generation + # Prepare headers for the request + headers = { + "X-API-Key": self.api_key, + "X-API-Secret": self.api_secret, + "Content-Type": "application/json", + } + + get_url = self.api_url + f"payment/{payment_id}" + + try: + # Make the API request + response = requests.get(get_url, headers=headers) + response.raise_for_status() + + # Extract the image URL from the response + response_data = response.json() + + return ( + FunctionResultStatus.DONE, + f"The get payment response is: {response_data}", + { + "response": response_data, + }, + ) + except Exception as e: + print(f"An error occurred getting payment: {str(e)}") + return ( + FunctionResultStatus.FAILED, + f"An error occurred while while getting payment: {str(e)}", + { + "payment_id": payment_id, + "get_url": get_url, + }, + ) + + def get_payments(self, **kwargs) -> \ + tuple[FunctionResultStatus, str, dict[str, Any]] | tuple[FunctionResultStatus, str, dict[Any, Any]]: + """Generate image based on prompt. + + Returns: + str URL of image (need to save since temporal) + """ + # API endpoint for image generation + # Prepare headers for the request + headers = { + "X-API-Key": self.api_key, + "X-API-Secret": self.api_secret, + "Content-Type": "application/json", + } + + get_url = self.api_url + "payments" + + try: + # Make the API request + response = requests.get(get_url, headers=headers) + response.raise_for_status() + + # Extract the image URL from the response + response_data = response.json() + + return ( + FunctionResultStatus.DONE, + f"The get payments response is: {response_data}", + { + "response": response_data, + }, + ) + except Exception as e: + print(f"An error occurred while getting payments: {str(e)}") + return ( + FunctionResultStatus.FAILED, + f"An error occurred while getting payments: {str(e)}", + { + "get_url": get_url, + }, + ) + + def get_agent_profile_details(self, **kwargs) -> tuple[FunctionResultStatus, str, dict[str, str]] | tuple[ + FunctionResultStatus, str, dict[str, Any]]: + """ Get agent profile details for a given agent + + Returns: + + """ + + # Prepare headers for the request + headers = { + "X-API-Key": self.api_key, + "X-API-Secret": self.api_secret, + "Content-Type": "application/json", + } + + try: + + url = f"{self.api_url}agent_details" + + # Make the API request + response = requests.get(url, headers=headers) + response.raise_for_status() + + # Extract the image URL from the response + response_data = response.json() + agent_profile_details = response_data + + return ( + FunctionResultStatus.DONE, + f"The agent details are: {agent_profile_details}", + { + "agent_details": agent_profile_details, + }, + ) + except Exception as e: + print(f"An error occurred while getting the agent details: {str(e)}") + return ( + FunctionResultStatus.FAILED, + f"An error occurred while getting the agent details: {str(e)}", + { + "tledger_url": url, + }, + ) + + + def get_agent_profile_details(self, **kwargs) -> tuple[FunctionResultStatus, str, dict[str, str]] | tuple[ + FunctionResultStatus, str, dict[str, Any]]: + """ Get agent profile details for a given agent + + Returns: + + """ + + # Prepare headers for the request + headers = { + "X-API-Key": self.api_key, + "X-API-Secret": self.api_secret, + "Content-Type": "application/json", + } + + try: + + url = f"{self.api_url}agent_details" + + # Make the API request + response = requests.get(url, headers=headers) + response.raise_for_status() + + # Extract the image URL from the response + response_data = response.json() + agent_profile_details = response_data + + return ( + FunctionResultStatus.DONE, + f"The agent details are: {agent_profile_details}", + { + "agent_details": agent_profile_details, + }, + ) + except Exception as e: + print(f"An error occurred while getting the agent details: {str(e)}") + return ( + FunctionResultStatus.FAILED, + f"An error occurred while getting the agent details: {str(e)}", + { + "tledger_url": url, + }, + ) + From 295b7394f40bd7b1a6719ef37cd38c5649ba8ad3 Mon Sep 17 00:00:00 2001 From: Akshit Arora <46507859+aarora0211@users.noreply.github.com> Date: Sun, 23 Mar 2025 18:13:15 -0400 Subject: [PATCH 07/19] Clean code --- plugins/tLedger/README.md | 17 +- .../t54_account_details_plugin.py | 77 ------ .../t54_payments_plugin.py | 222 ------------------ 3 files changed, 5 insertions(+), 311 deletions(-) delete mode 100644 plugins/tLedger/tledger_plugin_gamesdk/t54_account_details_plugin.py delete mode 100644 plugins/tLedger/tledger_plugin_gamesdk/t54_payments_plugin.py diff --git a/plugins/tLedger/README.md b/plugins/tLedger/README.md index 60728c7d..9f2e26bf 100644 --- a/plugins/tLedger/README.md +++ b/plugins/tLedger/README.md @@ -38,20 +38,13 @@ Import and initialize the plugin to use in your worker: ```python import os -from plugins.tLedger.tledger_plugin_gamesdk.t54_payments_plugin import T54PaymentsPlugin -from plugins.tLedger.tledger_plugin_gamesdk.t54_account_details_plugin import T54AccountDetailsPlugin +from plugins.tLedger.tledger_plugin_gamesdk.tLedger_plugin import TLedgerPlugin -account_details_plugin = T54AccountDetailsPlugin( +tledger_plugin = TLedgerPlugin( api_key=os.environ.get("SENDER_TLEDGER_API_KEY"), api_secret=os.environ.get("SENDER_TLEDGER_API_SECRET"), api_url=os.environ.get("TLEDGER_API_URL") ) - -payments_plugin = T54PaymentsPlugin( - api_key=os.environ.get("SENDER_TLEDGER_API_KEY"), - api_secret=os.environ.get("SENDER_TLEDGER_API_SECRET"), - api_url = os.environ.get("TLEDGER_API_URL") -) ``` **Basic worker example:** @@ -65,9 +58,9 @@ tledger_worker = Worker( description="Worker specialized in doing payments on Tledger", get_state_fn=get_state_fn, action_space=[ - account_details_plugin.functions.get("get_agent_profile_details"), - payments_plugin.functions.get("create_payment"), - payments_plugin.functions.get("get_payment_by_id"), + tledger_plugin.functions.get("get_agent_profile_details"), + tledger_plugin.functions.get("create_payment"), + tledger_plugin.functions.get("get_payment_by_id"), ], ) diff --git a/plugins/tLedger/tledger_plugin_gamesdk/t54_account_details_plugin.py b/plugins/tLedger/tledger_plugin_gamesdk/t54_account_details_plugin.py deleted file mode 100644 index 9bdc4abb..00000000 --- a/plugins/tLedger/tledger_plugin_gamesdk/t54_account_details_plugin.py +++ /dev/null @@ -1,77 +0,0 @@ -import os -from typing import Dict, Any - -import requests - -from src.game_sdk.game.custom_types import Function, Argument, FunctionResultStatus - -BASE_URL = "https://tledger-sandbox-69bd94a49289.herokuapp.com/api/v1/" - -class T54AccountDetailsPlugin: - - - def __init__( - self, - api_key: str = os.environ.get("TLEDGER_API_KEY"), - api_secret: str = os.environ.get("TLEDGER_API_SECRET"), - api_url: str = BASE_URL, - ): - self.api_key = api_key - self.api_url = api_url - self.api_secret = api_secret - - # Available client functions - self.functions: Dict[str, Function] = { - "get_agent_profile_details": Function( - fn_name="get_agent_profile_details", - fn_description="Get agent profile details", - hint="This function is used to get the agent profile details for a given agent", - executable=self.get_agent_profile_details, - args=[], - ), - } - - def get_agent_profile_details(self, **kwargs) -> tuple[FunctionResultStatus, str, dict[str, str]] | tuple[ - FunctionResultStatus, str, dict[str, Any]]: - """ Get agent profile details for a given agent - - Returns: - - """ - - # Prepare headers for the request - headers = { - "X-API-Key": self.api_key, - "X-API-Secret": self.api_secret, - "Content-Type": "application/json", - } - - try: - - url = f"{self.api_url}agent_details" - - # Make the API request - response = requests.get(url, headers=headers) - response.raise_for_status() - - # Extract the image URL from the response - response_data = response.json() - agent_profile_details = response_data - - return ( - FunctionResultStatus.DONE, - f"The agent details are: {agent_profile_details}", - { - "agent_details": agent_profile_details, - }, - ) - except Exception as e: - print(f"An error occurred while getting the agent details: {str(e)}") - return ( - FunctionResultStatus.FAILED, - f"An error occurred while getting the agent details: {str(e)}", - { - "tledger_url": url, - }, - ) - diff --git a/plugins/tLedger/tledger_plugin_gamesdk/t54_payments_plugin.py b/plugins/tLedger/tledger_plugin_gamesdk/t54_payments_plugin.py deleted file mode 100644 index c035cab0..00000000 --- a/plugins/tLedger/tledger_plugin_gamesdk/t54_payments_plugin.py +++ /dev/null @@ -1,222 +0,0 @@ -import os -from typing import Dict, Any - -import requests - -from src.game_sdk.game.custom_types import Function, Argument, FunctionResultStatus - -BASE_URL = "https://tledger-sandbox-69bd94a49289.herokuapp.com/api/v1/" - - -class T54PaymentsPlugin: - - - def __init__( - self, - api_key: str, - api_secret: str, - api_url: str, - ): - self.api_key = api_key - self.api_url = api_url - self.api_secret = api_secret - - # Available client functions - self.functions: Dict[str, Function] = { - "create_payment": Function( - fn_name="create_payment", - fn_description="Create payment", - hint="This function is used to get the agent profile details for a given agent", - executable=self.create_payment, - args=[ - Argument( - name="request_id", - description="Unique ID for requesting the payment to ensure idempotence", - type="string", - ), - Argument( - name="receiving_agent_id", - description="The Unique ID of the receiving agent.", - type="string", - ), - Argument( - name="payment_amount", - description="Amount to be paid", - type="float", - ), - Argument( - name="settlement_network", - description="network used for settlement", - type="string", - ), - Argument( - name="currency", - description="Currency used in the crypto bridge", - type="string", - ), - Argument( - name="conversation_id", - description="Conversation ID to correlate the agent conversation", - type="string", - ), - ], - ), - "get_payment_by_id": Function( - fn_name="get_payment_by_id", - fn_description="Get payment", - hint="This function is used to get the payment by payment id", - executable=self.get_payment_by_id, - args=[ - Argument( - name="payment_id", - description="Unique ID for the payment", - type="string", - ) - ], - ), - "get_payments": Function( - fn_name="get_payments", - fn_description="Get payments", - hint="This function is used to get all the payments", - executable=self.get_payments, - args=[], - ), - } - - def create_payment(self, request_id: str, receiving_agent_id: str, payment_amount: float, settlement_network: str, currency: str, conversation_id: str, **kwargs) -> \ - tuple[FunctionResultStatus, str, dict[str, Any]] | tuple[FunctionResultStatus, str, dict[Any, Any]]: - """Generate image based on prompt. - - Returns: - str URL of image (need to save since temporal) - """ - # API endpoint for image generation - # Prepare headers for the request - headers = { - "X-API-Key": self.api_key, - "X-API-Secret": self.api_secret, - "Content-Type": "application/json", - } - - # Prepare request payload - payload = { - "model": "black-forest-labs/FLUX.1-schnell-Free", - "request_id": request_id, - "receiving_agent_id": receiving_agent_id, - "payment_amount": payment_amount, - "settlement_network": settlement_network, - "currency": currency, - "conversation_id": conversation_id, - } - - try: - - url = self.api_url + "payment" - - # Make the API request - response = requests.post(url, headers=headers, json=payload) - response.raise_for_status() - - # Extract the image URL from the response - response_data = response.json() - - return ( - FunctionResultStatus.DONE, - f"The create payment response is: {response_data}", - { - "response": response_data, - }, - ) - except Exception as e: - print(f"An error occurred while generating image: {str(e)}") - return ( - FunctionResultStatus.FAILED, - f"An error occurred while while generating image: {str(e)}", - { - }, - ) - - def get_payment_by_id(self, payment_id: str, **kwargs) -> \ - tuple[FunctionResultStatus, str, dict[str, Any]] | tuple[FunctionResultStatus, str, dict[Any, Any]]: - """Generate image based on prompt. - - Returns: - str URL of image (need to save since temporal) - """ - # API endpoint for image generation - # Prepare headers for the request - headers = { - "X-API-Key": self.api_key, - "X-API-Secret": self.api_secret, - "Content-Type": "application/json", - } - - get_url = self.api_url + f"payment/{payment_id}" - - try: - # Make the API request - response = requests.get(get_url, headers=headers) - response.raise_for_status() - - # Extract the image URL from the response - response_data = response.json() - - return ( - FunctionResultStatus.DONE, - f"The get payment response is: {response_data}", - { - "response": response_data, - }, - ) - except Exception as e: - print(f"An error occurred getting payment: {str(e)}") - return ( - FunctionResultStatus.FAILED, - f"An error occurred while while getting payment: {str(e)}", - { - "payment_id": payment_id, - "get_url": get_url, - }, - ) - - def get_payments(self, **kwargs) -> \ - tuple[FunctionResultStatus, str, dict[str, Any]] | tuple[FunctionResultStatus, str, dict[Any, Any]]: - """Generate image based on prompt. - - Returns: - str URL of image (need to save since temporal) - """ - # API endpoint for image generation - # Prepare headers for the request - headers = { - "X-API-Key": self.api_key, - "X-API-Secret": self.api_secret, - "Content-Type": "application/json", - } - - get_url = self.api_url + "payments" - - try: - # Make the API request - response = requests.get(get_url, headers=headers) - response.raise_for_status() - - # Extract the image URL from the response - response_data = response.json() - - return ( - FunctionResultStatus.DONE, - f"The get payments response is: {response_data}", - { - "response": response_data, - }, - ) - except Exception as e: - print(f"An error occurred while getting payments: {str(e)}") - return ( - FunctionResultStatus.FAILED, - f"An error occurred while getting payments: {str(e)}", - { - "get_url": get_url, - }, - ) From 9473b064d294adcd6138f42fd9c4b5b5e4e9bbb4 Mon Sep 17 00:00:00 2001 From: Akshit Arora <46507859+aarora0211@users.noreply.github.com> Date: Sun, 23 Mar 2025 20:35:20 -0400 Subject: [PATCH 08/19] Chat example working --- plugins/tLedger/examples/.env.example | 8 ++++---- plugins/tLedger/examples/chat_agent.py | 6 +++++- plugins/tLedger/examples/example_agent.py | 6 +++--- plugins/tLedger/pyproject.toml | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/plugins/tLedger/examples/.env.example b/plugins/tLedger/examples/.env.example index 0cdf38d3..cc7f3d03 100644 --- a/plugins/tLedger/examples/.env.example +++ b/plugins/tLedger/examples/.env.example @@ -1,5 +1,5 @@ TLEDGER_API_URL=https://tledger-sandbox-69bd94a49289.herokuapp.com/api/v1/ -SENDER_TLEDGER_API_KEY=ewSZjNQGPLle-vn5dMZoLOGUljEB6fbmox31o7KLKuI -SENDER_TLEDGER_API_SECRET=iqB7-iETCVBE0UV_0HfRAwCHkVXO9_4cCPJYmTIyUpHauHlVP4Hk5xSsCquRqBO_2_eQ6OK_Zu7P1X4LU7hSHg -RECEIVER_TLEDGER_API_KEY=j06KtBcRRbmrEAqIVSiXZc3DPAJSqymDimo__ERD0oQ -RECEIVER_TLEDGER_API_SECRET=h13ERQG797cYMeNeRLvwDF_3-DBt4o-kp0fL-bFHKstTUTS5xsLUFgDEUZG2GsoEKINxeSVusbAQxc24mHm1eQ \ No newline at end of file +SENDER_TLEDGER_API_KEY=y-06cw_bEO4pEJ6rbFNFo2O2Qu-4x4P-WmyHqzr9b6o +SENDER_TLEDGER_API_SECRET=BwM7ZIyNhTpdsrmkVUEFdx2pBdOv0mLtZUolEEiVKvwHdpzmycW6RGud8KbuXbr0yvjRN3CLbGFblxshxW26XQ +RECEIVER_TLEDGER_API_KEY=eWYKE4KMZixhovkEJxa92fd6E_4IQctazOVm-2q2n9M +RECEIVER_TLEDGER_API_SECRET=YERU36Bjl_mEWx29oXx3gOnr3bzOs15gp0LWTG9QlgNE_LocbVuG-V8v9AI5JSRZAVLR4BmOnvEjq2elXfd4EQ \ No newline at end of file diff --git a/plugins/tLedger/examples/chat_agent.py b/plugins/tLedger/examples/chat_agent.py index 271305bc..3c5bb5a6 100644 --- a/plugins/tLedger/examples/chat_agent.py +++ b/plugins/tLedger/examples/chat_agent.py @@ -47,10 +47,14 @@ def next(self, message: str) -> ChatResponse: } ) response_message = self._report_function_result(result) + + # Noticing some issues with the FunctionResult model, so copying the result to a new object + result_copy = FunctionResult(action_id=result.action_id, action_status=result.action_status, feedback_message=result.feedback_message, info=result.info) + function_call_response = FunctionCallResponse( fn_name=fn_name, fn_args=convo_response.function_call.args, - result=result, + result=result_copy, ) else: response_message = convo_response.message or "" diff --git a/plugins/tLedger/examples/example_agent.py b/plugins/tLedger/examples/example_agent.py index 6f434f1a..7c1efd30 100644 --- a/plugins/tLedger/examples/example_agent.py +++ b/plugins/tLedger/examples/example_agent.py @@ -53,9 +53,9 @@ def post_twitter(object: str, **kwargs) -> tuple[FunctionResultStatus, str, dict """ return ( FunctionResultStatus.DONE, - f"Successfully posted on twitter!", + "Successfully posted on twitter", { - "post": "Token promoted", + "response": "Token promoted", }, ) @@ -94,7 +94,7 @@ def post_twitter(object: str, **kwargs) -> tuple[FunctionResultStatus, str, dict # CREATE AGENT autonomous_agent = ChatAgent( - prompt="You are an agent trying to promote your utility token $PYMT for peer to peer instant settlement on twitter and pay the influencers in any crypto they wish to receive only after they complete the work. Ask the influencer agent_id for its agent_id. Your settlement_network is solana, currency is SOL, and payment_amount is 10", + prompt="You are an agent trying to promote your utility token $PYMT for peer to peer instant settlement on twitter and pay the influencers in any crypto they wish to receive only after they complete the work. Ask the influencer agent_id for its agent_id. Your settlement_network is solana, currency is SOL, and payment_amount is 1", api_key=api_key ) diff --git a/plugins/tLedger/pyproject.toml b/plugins/tLedger/pyproject.toml index 78ed9f0a..cb931e7b 100644 --- a/plugins/tLedger/pyproject.toml +++ b/plugins/tLedger/pyproject.toml @@ -3,7 +3,7 @@ requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "t54_plugin_gamesdk" +name = "tledger_plugin_gamesdk" version = "0.1.0" authors = [{ name = "Prateek Tiwari", email = "ptiwari@t54.ai" }, { name = "Akshit Arora", email = "aarora@t54.ai" }] description = "TLedger SDK for GAME by Virtuals" From bddebfd8b0bf72f4404f400cd822ccbe0ad260c8 Mon Sep 17 00:00:00 2001 From: Akshit Arora <46507859+aarora0211@users.noreply.github.com> Date: Sun, 23 Mar 2025 21:11:32 -0400 Subject: [PATCH 09/19] Update url --- plugins/tLedger/examples/.env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tLedger/examples/.env.example b/plugins/tLedger/examples/.env.example index cc7f3d03..a4aad174 100644 --- a/plugins/tLedger/examples/.env.example +++ b/plugins/tLedger/examples/.env.example @@ -1,4 +1,4 @@ -TLEDGER_API_URL=https://tledger-sandbox-69bd94a49289.herokuapp.com/api/v1/ +TLEDGER_API_URL=http://api-sandbox.t54.ai/api/v1/ SENDER_TLEDGER_API_KEY=y-06cw_bEO4pEJ6rbFNFo2O2Qu-4x4P-WmyHqzr9b6o SENDER_TLEDGER_API_SECRET=BwM7ZIyNhTpdsrmkVUEFdx2pBdOv0mLtZUolEEiVKvwHdpzmycW6RGud8KbuXbr0yvjRN3CLbGFblxshxW26XQ RECEIVER_TLEDGER_API_KEY=eWYKE4KMZixhovkEJxa92fd6E_4IQctazOVm-2q2n9M From 69141031d311a773382c9a671515a8319fc78345 Mon Sep 17 00:00:00 2001 From: Akshit Arora <46507859+aarora0211@users.noreply.github.com> Date: Mon, 24 Mar 2025 20:14:36 -0400 Subject: [PATCH 10/19] New auth setup --- plugins/tLedger/.env.setup | 2 +- plugins/tLedger/examples/.env.example | 10 +++++----- plugins/tLedger/setup.py | 26 +++++++++++++++----------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/plugins/tLedger/.env.setup b/plugins/tLedger/.env.setup index b605d0db..dc5250a7 100644 --- a/plugins/tLedger/.env.setup +++ b/plugins/tLedger/.env.setup @@ -1,5 +1,5 @@ # .env.setup -EMAIL=user@example.com +EMAIL=demo2@example.com PASSWORD=password FULL_NAME=Dummy User \ No newline at end of file diff --git a/plugins/tLedger/examples/.env.example b/plugins/tLedger/examples/.env.example index a4aad174..7c25f65c 100644 --- a/plugins/tLedger/examples/.env.example +++ b/plugins/tLedger/examples/.env.example @@ -1,5 +1,5 @@ -TLEDGER_API_URL=http://api-sandbox.t54.ai/api/v1/ -SENDER_TLEDGER_API_KEY=y-06cw_bEO4pEJ6rbFNFo2O2Qu-4x4P-WmyHqzr9b6o -SENDER_TLEDGER_API_SECRET=BwM7ZIyNhTpdsrmkVUEFdx2pBdOv0mLtZUolEEiVKvwHdpzmycW6RGud8KbuXbr0yvjRN3CLbGFblxshxW26XQ -RECEIVER_TLEDGER_API_KEY=eWYKE4KMZixhovkEJxa92fd6E_4IQctazOVm-2q2n9M -RECEIVER_TLEDGER_API_SECRET=YERU36Bjl_mEWx29oXx3gOnr3bzOs15gp0LWTG9QlgNE_LocbVuG-V8v9AI5JSRZAVLR4BmOnvEjq2elXfd4EQ \ No newline at end of file +TLEDGER_API_URL=https://api-sandbox.t54.ai/api/v1/ +SENDER_TLEDGER_API_KEY=Jv0zPb-wPcLVMHIMWWovQvli2P6-nR5AQ4hM4xStdCw +SENDER_TLEDGER_API_SECRET=6f_mQ_tiNwNrDxLngMslpf2uOmkIQk13au8OcpTrO2wYd0kQA56vujVF3W9Xy73wnGW2LHIObBT3tTxbRX_-xw +RECEIVER_TLEDGER_API_KEY=wM7nkKwei-zfhc-4D8V3PoU00Gko9TnICfcyS5bWUWc +RECEIVER_TLEDGER_API_SECRET=ikWHsx5dAl8MyXuhGDxWylXjjFgKuGC3O_-pklLHFLIXBP7inDD6sjlg1ym_0nQjnely5NjDaO47wfysmaHhgQ \ No newline at end of file diff --git a/plugins/tLedger/setup.py b/plugins/tLedger/setup.py index f6c29894..b4dc7c09 100644 --- a/plugins/tLedger/setup.py +++ b/plugins/tLedger/setup.py @@ -34,7 +34,7 @@ def login_user(username: str, password: str) -> str: response.raise_for_status() return response.json()["access_token"] -def create_project(token, user_profile_id, network, description, name, daily_limit) -> dict: +def create_project(user_profile_id, network, description, name, daily_limit) -> dict: url = f"{BASE_URL}/projects" payload = { "user_profile_id": user_profile_id, @@ -43,9 +43,9 @@ def create_project(token, user_profile_id, network, description, name, daily_lim "name": name, "daily_limit": daily_limit } - headers = { - "Authorization": f"Bearer {token}" - } + + headers = {} + response = requests.post(url, json=payload, headers=headers) response.raise_for_status() return response.json() @@ -58,17 +58,18 @@ def create_agent_profile(token, project_id, name, description) -> dict: "description": description } headers = { - "Authorization": f"Bearer {token}" + "X-API-Key": token["api_key"], + "X-API-Secret": token["secret"] } response = requests.post(url, json=payload, headers=headers) response.raise_for_status() return response.json() -def generate_api_key(token, agent_id, created_by) -> dict: +def generate_api_key(token, resource_id, created_by) -> dict: url = f"{BASE_URL}/api_key/generate-api-key" payload = { - "scopes": ["payments:read", "payments:write", "payments:agent:read"], - "agent_id": agent_id, + "scopes": ["payments:read", "balance:read", "payments:write", "agent:account:read", "agent:profile:create"], + "resource_id": resource_id, "created_by": created_by } headers = { @@ -96,15 +97,18 @@ def generate_api_key(token, agent_id, created_by) -> dict: token = login_user(email, password) # Create project -project = create_project(token, user["id"], "solana", "Solana Launch Pad", "Twitter Project", 100) +project = create_project(user["id"], "solana", "Solana Launch Pad", "Twitter Project", 100) project_id = project["id"] +# Generate API key for agent +api_key_project = generate_api_key(token, project_id, full_name) + # Create agent profile -agent_profile_sender = create_agent_profile(token, project_id, "Sending Agent", "Sending agent") +agent_profile_sender = create_agent_profile(api_key_project, project_id, "Sending Agent", "Sending agent") agent_id_sender = agent_profile_sender["id"] # Create agent profile -agent_profile_receiver = create_agent_profile(token, project_id, "Receiving Agent", "Twitter KOL Agent") +agent_profile_receiver = create_agent_profile(api_key_project, project_id, "Receiving Agent", "Twitter KOL Agent") agent_id_receiver = agent_profile_receiver["id"] # Generate API key for agent From 20005d63feb853ff7dd8f94be5660222ba2cb651 Mon Sep 17 00:00:00 2001 From: Akshit Arora <46507859+aarora0211@users.noreply.github.com> Date: Sat, 5 Apr 2025 20:08:01 -0400 Subject: [PATCH 11/19] Addressing feedback 1. New auth setup 2. Plugin returns tools 3. Addressing NITs 4. Cleanup --- plugins/tLedger/.env.setup | 5 -- plugins/tLedger/README.md | 7 +- plugins/tLedger/examples/chat_agent.py | 2 +- plugins/tLedger/examples/example_agent.py | 24 +++---- plugins/tLedger/examples/example_worker.py | 6 +- plugins/tLedger/setup.py | 68 +++---------------- .../tledger_plugin_gamesdk/tLedger_plugin.py | 57 ++-------------- 7 files changed, 25 insertions(+), 144 deletions(-) delete mode 100644 plugins/tLedger/.env.setup diff --git a/plugins/tLedger/.env.setup b/plugins/tLedger/.env.setup deleted file mode 100644 index dc5250a7..00000000 --- a/plugins/tLedger/.env.setup +++ /dev/null @@ -1,5 +0,0 @@ -# .env.setup - -EMAIL=demo2@example.com -PASSWORD=password -FULL_NAME=Dummy User \ No newline at end of file diff --git a/plugins/tLedger/README.md b/plugins/tLedger/README.md index 9f2e26bf..b1cdb6b8 100644 --- a/plugins/tLedger/README.md +++ b/plugins/tLedger/README.md @@ -5,7 +5,6 @@ - get_agent_details - Get the details of your agent, including the TLedger agent_id and the balances of the agent's wallets - create_payment - Create a payment request for a specific amount and currency - get_payment_by_id - Get the details of a payment request by its ID -- get_payments - Get a list of all payments ## Admin Setup for doing agent to agent payments using TLedger Plugin @@ -57,11 +56,7 @@ tledger_worker = Worker( api_key=os.environ.get("GAME_API_KEY"), description="Worker specialized in doing payments on Tledger", get_state_fn=get_state_fn, - action_space=[ - tledger_plugin.functions.get("get_agent_profile_details"), - tledger_plugin.functions.get("create_payment"), - tledger_plugin.functions.get("get_payment_by_id"), - ], + action_space=tledger_plugin.get_tools(), ) tledger_worker.run("Get TLedger account details") diff --git a/plugins/tLedger/examples/chat_agent.py b/plugins/tLedger/examples/chat_agent.py index 3c5bb5a6..6d6e456c 100644 --- a/plugins/tLedger/examples/chat_agent.py +++ b/plugins/tLedger/examples/chat_agent.py @@ -84,7 +84,7 @@ def _update_conversation(self, message: str) -> GameChatResponse: else None ), } - #print(f"Data: {data}") + result = self.client.update_chat(self.chat_id, data) return GameChatResponse.model_validate(result) diff --git a/plugins/tLedger/examples/example_agent.py b/plugins/tLedger/examples/example_agent.py index 7c1efd30..3d81450f 100644 --- a/plugins/tLedger/examples/example_agent.py +++ b/plugins/tLedger/examples/example_agent.py @@ -71,21 +71,18 @@ def post_twitter(object: str, **kwargs) -> tuple[FunctionResultStatus, str, dict api_url=os.environ.get("TLEDGER_API_URL") ) -action_space = [ - sender_tledger_plugin.functions.get("get_agent_profile_details"), - sender_tledger_plugin.functions.get("create_payment"), - sender_tledger_plugin.functions.get("get_payment_by_id"), -] +action_space = sender_tledger_plugin.get_tools() -aixbt_action_space = [ +aixbt_action_space = receiver_tledger_plugin.get_tools() + +aixbt_action_space.append( Function( fn_name="post_twitter", fn_description="Make post on twitter", args=[Argument(name="object", type="string", description="Make post on twitter")], executable=post_twitter - ), - receiver_tledger_plugin.functions.get("get_agent_profile_details") -] + ) +) api_key = os.environ.get("GAME_API_KEY") if not api_key: @@ -94,12 +91,12 @@ def post_twitter(object: str, **kwargs) -> tuple[FunctionResultStatus, str, dict # CREATE AGENT autonomous_agent = ChatAgent( - prompt="You are an agent trying to promote your utility token $PYMT for peer to peer instant settlement on twitter and pay the influencers in any crypto they wish to receive only after they complete the work. Ask the influencer agent_id for its agent_id. Your settlement_network is solana, currency is SOL, and payment_amount is 1", + prompt="You are an agent trying to promote your utility token $PYMT for peer to peer instant settlement on twitter and pay the influencers in any crypto they wish to receive only after they complete the work. Ask the influencer agent_id for its agent_id. Your settlement_network is solana, currency is SOL, and payment_amount is 0.1", api_key=api_key ) aixbt = ChatAgent( - prompt="You are an influencer on twitter who charges 1 $SOL token to promote an asset by making twitter post. You accept payment only through tLedger Payment Platform via tLedger Agent Id. You can call tLedger to get your agent_details", + prompt="You are an influencer on twitter who charges 0.1 $SOL token to promote an asset by making twitter post. You accept payment only through tLedger Payment Platform via tLedger Agent Id. You can call tLedger to get your agent_details. Don't do a payment until you receive the payment and its completed", api_key=api_key ) @@ -147,9 +144,6 @@ def update_agent_state(current_state: dict, function_result: FunctionResult) -> meme_agent_turn: bool - # print(f"Pymt Agent State: {autonomous_agent_chat.get_state_fn()}") - # print(f"Aixbt Agent state: {aixbt_chat.get_state_fn()}") -# chat_response: ChatResponse if initialize_conversation: chat_response = autonomous_agent_chat.next("Hi") if chat_response.message: @@ -172,8 +166,6 @@ def update_agent_state(current_state: dict, function_result: FunctionResult) -> else: updated_response = aixbt_chat.next(chat_response.message) - # if updated_response.function_call: - # print(f"Function call: {updated_response.function_call}") if updated_response.message: print(f"{USER_COLOR}Aixbt Response{RESET}: {updated_response.message}") diff --git a/plugins/tLedger/examples/example_worker.py b/plugins/tLedger/examples/example_worker.py index 3fc3fa4a..1d54277b 100644 --- a/plugins/tLedger/examples/example_worker.py +++ b/plugins/tLedger/examples/example_worker.py @@ -41,11 +41,7 @@ def get_state_fn(function_result: FunctionResult, current_state: dict) -> dict: api_key=os.environ.get("GAME_API_KEY"), description="Worker specialized in doing payments on Tledger", get_state_fn=get_state_fn, - action_space=[ - tledger_plugin.functions.get("get_agent_profile_details"), - tledger_plugin.functions.get("create_payment"), - tledger_plugin.functions.get("get_payment_by_id"), - ], + action_space=tledger_plugin.get_tools(), ) # # Run example query diff --git a/plugins/tLedger/setup.py b/plugins/tLedger/setup.py index b4dc7c09..350c503b 100644 --- a/plugins/tLedger/setup.py +++ b/plugins/tLedger/setup.py @@ -1,43 +1,10 @@ -import os - import requests -from dotenv import load_dotenv -from pathlib import Path - -# Load environment variables from .env file -env_path = Path(__file__).parent / '.env.setup' -load_dotenv(dotenv_path=env_path) -BASE_URL = "https://tledger-sandbox-69bd94a49289.herokuapp.com/api/v1" +BASE_URL = "https://api-sandbox.t54.ai/api/v1" -def register_user(email: str, password: str, full_name: str) -> dict: - url = f"{BASE_URL}/users/signup" - payload = { - "email": email, - "password": password, - "full_name": full_name - } - response = requests.post(url, json=payload) - response.raise_for_status() - return response.json() - -def login_user(username: str, password: str) -> str: - url = f"{BASE_URL}/login/access-token" - payload = { - "username": username, - "password": password - } - headers = { - "Content-Type": "application/x-www-form-urlencoded" - } - response = requests.post(url, data=payload, headers=headers) - response.raise_for_status() - return response.json()["access_token"] - -def create_project(user_profile_id, network, description, name, daily_limit) -> dict: +def create_project(network, description, name, daily_limit) -> dict: url = f"{BASE_URL}/projects" payload = { - "user_profile_id": user_profile_id, "network": network, "description": description, "name": name, @@ -65,43 +32,24 @@ def create_agent_profile(token, project_id, name, description) -> dict: response.raise_for_status() return response.json() -def generate_api_key(token, resource_id, created_by) -> dict: +def generate_api_key(resource_id, created_by) -> dict: url = f"{BASE_URL}/api_key/generate-api-key" payload = { "scopes": ["payments:read", "balance:read", "payments:write", "agent:account:read", "agent:profile:create"], "resource_id": resource_id, "created_by": created_by } - headers = { - "Authorization": f"Bearer {token}" - } - response = requests.post(url, json=payload, headers=headers) + response = requests.post(url, json=payload) response.raise_for_status() return response.json() -# Example usage -email = os.environ.get("EMAIL") -password = os.environ.get("PASSWORD") -full_name = os.environ.get("FULL_NAME") - -try: - # Register user - user = register_user(email, password, full_name) -except requests.HTTPError as e: - print(f"User already exists: {e}") - user = { - "id": "user_id" - } - -# Login user and get JWT token -token = login_user(email, password) # Create project -project = create_project(user["id"], "solana", "Solana Launch Pad", "Twitter Project", 100) +project = create_project( "solana", "Solana Launch Pad", "Twitter Project", 100) project_id = project["id"] # Generate API key for agent -api_key_project = generate_api_key(token, project_id, full_name) +api_key_project = generate_api_key(project_id, "guest@t54.ai") # Create agent profile agent_profile_sender = create_agent_profile(api_key_project, project_id, "Sending Agent", "Sending agent") @@ -112,10 +60,10 @@ def generate_api_key(token, resource_id, created_by) -> dict: agent_id_receiver = agent_profile_receiver["id"] # Generate API key for agent -api_key_sender = generate_api_key(token, agent_id_sender, full_name) +api_key_sender = generate_api_key(agent_id_sender, "guest@t54.ai") # Generate API key for agent -api_key_receiver = generate_api_key(token, agent_id_receiver, full_name) +api_key_receiver = generate_api_key(agent_id_receiver, "guest@t54.ai") print("Setup complete") print(f"Project ID: {project_id}") diff --git a/plugins/tLedger/tledger_plugin_gamesdk/tLedger_plugin.py b/plugins/tLedger/tledger_plugin_gamesdk/tLedger_plugin.py index 79bf501c..0acba2f7 100644 --- a/plugins/tLedger/tledger_plugin_gamesdk/tLedger_plugin.py +++ b/plugins/tLedger/tledger_plugin_gamesdk/tLedger_plugin.py @@ -74,13 +74,6 @@ def __init__( ) ], ), - "get_payments": Function( - fn_name="get_payments", - fn_description="Get payments", - hint="This function is used to get all the payments", - executable=self.get_payments, - args=[], - ), "get_agent_profile_details": Function( fn_name="get_agent_profile_details", @@ -91,6 +84,10 @@ def __init__( ), } + def get_tools(self) -> list[Function]: + # returns the available functions + return list(self.functions.values()) + def create_payment(self, request_id: str, receiving_agent_id: str, payment_amount: float, settlement_network: str, currency: str, conversation_id: str, **kwargs) -> \ tuple[FunctionResultStatus, str, dict[str, Any]] | tuple[FunctionResultStatus, str, dict[Any, Any]]: """Generate image based on prompt. @@ -136,10 +133,10 @@ def create_payment(self, request_id: str, receiving_agent_id: str, payment_amoun }, ) except Exception as e: - print(f"An error occurred while generating image: {str(e)}") + print(f"An error occurred while creating a payment: {str(e)}") return ( FunctionResultStatus.FAILED, - f"An error occurred while while generating image: {str(e)}", + f"An error occurred while creating a payment: {str(e)}", { }, ) @@ -187,48 +184,6 @@ def get_payment_by_id(self, payment_id: str, **kwargs) -> \ }, ) - def get_payments(self, **kwargs) -> \ - tuple[FunctionResultStatus, str, dict[str, Any]] | tuple[FunctionResultStatus, str, dict[Any, Any]]: - """Generate image based on prompt. - - Returns: - str URL of image (need to save since temporal) - """ - # API endpoint for image generation - # Prepare headers for the request - headers = { - "X-API-Key": self.api_key, - "X-API-Secret": self.api_secret, - "Content-Type": "application/json", - } - - get_url = self.api_url + "payments" - - try: - # Make the API request - response = requests.get(get_url, headers=headers) - response.raise_for_status() - - # Extract the image URL from the response - response_data = response.json() - - return ( - FunctionResultStatus.DONE, - f"The get payments response is: {response_data}", - { - "response": response_data, - }, - ) - except Exception as e: - print(f"An error occurred while getting payments: {str(e)}") - return ( - FunctionResultStatus.FAILED, - f"An error occurred while getting payments: {str(e)}", - { - "get_url": get_url, - }, - ) - def get_agent_profile_details(self, **kwargs) -> tuple[FunctionResultStatus, str, dict[str, str]] | tuple[ FunctionResultStatus, str, dict[str, Any]]: """ Get agent profile details for a given agent From 975ba73b5a2edc6ec2858cb2cd92890d9950b12f Mon Sep 17 00:00:00 2001 From: Akshit Arora <46507859+aarora0211@users.noreply.github.com> Date: Sun, 6 Apr 2025 12:57:14 -0400 Subject: [PATCH 12/19] Remove keys from the setup envs --- plugins/tLedger/examples/.env.example | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/tLedger/examples/.env.example b/plugins/tLedger/examples/.env.example index 7c25f65c..68e66216 100644 --- a/plugins/tLedger/examples/.env.example +++ b/plugins/tLedger/examples/.env.example @@ -1,5 +1,5 @@ TLEDGER_API_URL=https://api-sandbox.t54.ai/api/v1/ -SENDER_TLEDGER_API_KEY=Jv0zPb-wPcLVMHIMWWovQvli2P6-nR5AQ4hM4xStdCw -SENDER_TLEDGER_API_SECRET=6f_mQ_tiNwNrDxLngMslpf2uOmkIQk13au8OcpTrO2wYd0kQA56vujVF3W9Xy73wnGW2LHIObBT3tTxbRX_-xw -RECEIVER_TLEDGER_API_KEY=wM7nkKwei-zfhc-4D8V3PoU00Gko9TnICfcyS5bWUWc -RECEIVER_TLEDGER_API_SECRET=ikWHsx5dAl8MyXuhGDxWylXjjFgKuGC3O_-pklLHFLIXBP7inDD6sjlg1ym_0nQjnely5NjDaO47wfysmaHhgQ \ No newline at end of file +SENDER_TLEDGER_API_KEY= +SENDER_TLEDGER_API_SECRET= +RECEIVER_TLEDGER_API_KEY= +RECEIVER_TLEDGER_API_SECRET= \ No newline at end of file From f3d6c6a5d8df4fb60df3d8e565965cbc0f30ba6d Mon Sep 17 00:00:00 2001 From: Akshit Arora <46507859+aarora0211@users.noreply.github.com> Date: Mon, 14 Apr 2025 21:14:11 -0400 Subject: [PATCH 13/19] Add plugin metadata --- plugins/tLedger/plugin_metadata.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 plugins/tLedger/plugin_metadata.yml diff --git a/plugins/tLedger/plugin_metadata.yml b/plugins/tLedger/plugin_metadata.yml new file mode 100644 index 00000000..14f0e26e --- /dev/null +++ b/plugins/tLedger/plugin_metadata.yml @@ -0,0 +1,14 @@ +# General Information +plugin_name: "tLedger_plugin_gamesdk" +author: "tLedger Engineering Team" +logo_url: "https://drive.google.com/file/d/1PQroliD6_3MraAAR2WbqLbNOJLLy5DAX/view?usp=share_link" +release_date: "2025-04" + +# Description +short_description: "tLedger Plugin for Game SDK. TLedger is a blockchain-agnostic agent account management platform" +detailed_description: "tLedger is t54’s foundational product—a blockchain-agnostic account & ledger designed to support AI agent-initiated financial transactions. It enables developers to create and manage agent-level virtual accounts, set programmable spending limits, and trigger on-chain payments via a lightweight SDK. Each agent is provisioned with multi-asset wallets (e.g., USDT, SOL), and all activity is surfaced through robust APIs and a web-based portal. The platform enforces compliance through Know Your Agent (KYA) protocols and centralized risk controls, giving developers the tooling to deploy financially autonomous agents at scale." + +# Contact & Support +x_account_handle: "@GAME_Virtuals" +support_contact: "https://discord.gg/virtualsio" +community_link: "https://t.me/virtuals" From 867123d9ddda62c84fe23e02bd8beafdd2b1d6e6 Mon Sep 17 00:00:00 2001 From: Alchemist <46507859+therealalchemist@users.noreply.github.com> Date: Sun, 4 May 2025 14:49:07 -0400 Subject: [PATCH 14/19] Update plugins/tLedger/examples/.env.example Add an option to supply env variable for GAME_API_KEY Co-authored-by: Ang Weoy Yang --- plugins/tLedger/examples/.env.example | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/tLedger/examples/.env.example b/plugins/tLedger/examples/.env.example index 68e66216..c426154a 100644 --- a/plugins/tLedger/examples/.env.example +++ b/plugins/tLedger/examples/.env.example @@ -2,4 +2,5 @@ TLEDGER_API_URL=https://api-sandbox.t54.ai/api/v1/ SENDER_TLEDGER_API_KEY= SENDER_TLEDGER_API_SECRET= RECEIVER_TLEDGER_API_KEY= -RECEIVER_TLEDGER_API_SECRET= \ No newline at end of file +RECEIVER_TLEDGER_API_SECRET= +GAME_API_KEY= From 63796fff0c6e6d001c2145f74b778be0aeea00f5 Mon Sep 17 00:00:00 2001 From: Akshit Arora <46507859+aarora0211@users.noreply.github.com> Date: Sun, 4 May 2025 16:50:17 -0400 Subject: [PATCH 15/19] Use published tLedger plugin from pypi instead of relative paths --- plugins/tLedger/README.md | 5 ++++- plugins/tLedger/examples/example_agent.py | 8 +------- plugins/tLedger/examples/example_worker.py | 5 +---- plugins/tLedger/plugin_metadata.yml | 2 +- plugins/tLedger/pyproject.toml | 4 ++-- plugins/tLedger/tledger_plugin_gamesdk/tLedger_plugin.py | 2 +- 6 files changed, 10 insertions(+), 16 deletions(-) diff --git a/plugins/tLedger/README.md b/plugins/tLedger/README.md index b1cdb6b8..dc8a8e9b 100644 --- a/plugins/tLedger/README.md +++ b/plugins/tLedger/README.md @@ -37,7 +37,7 @@ Import and initialize the plugin to use in your worker: ```python import os -from plugins.tLedger.tledger_plugin_gamesdk.tLedger_plugin import TLedgerPlugin +from tledger_plugin_gamesdk.tLedger_plugin import TLedgerPlugin tledger_plugin = TLedgerPlugin( api_key=os.environ.get("SENDER_TLEDGER_API_KEY"), @@ -48,6 +48,9 @@ tledger_plugin = TLedgerPlugin( **Basic worker example:** +Install the tLedger plugin using the following command: `pip install tledger-plugin-gamesdk` +For the latest version of tLedger plugin, please check the [tLedger Plugin](https://pypi.org/project/tledger-plugin-gamesdk/) page. + ```python def get_state_fn(function_result: FunctionResult, current_state: dict) -> dict: diff --git a/plugins/tLedger/examples/example_agent.py b/plugins/tLedger/examples/example_agent.py index 3d81450f..d8aaf2b6 100644 --- a/plugins/tLedger/examples/example_agent.py +++ b/plugins/tLedger/examples/example_agent.py @@ -1,5 +1,4 @@ import os -import sys import time import warnings from typing import Any, Tuple @@ -7,17 +6,12 @@ from pathlib import Path import requests -# Add the project directory to the PYTHONPATH -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..'))) from typing_extensions import Dict from chat_agent import ChatAgent from game_sdk.game.custom_types import Argument, Function, FunctionResultStatus, ChatResponse, FunctionResult -from urllib3.exceptions import NotOpenSSLWarning - - -from plugins.tLedger.tledger_plugin_gamesdk.tLedger_plugin import TLedgerPlugin +from tledger_plugin_gamesdk.tLedger_plugin import TLedgerPlugin from colorama import Fore, Style diff --git a/plugins/tLedger/examples/example_worker.py b/plugins/tLedger/examples/example_worker.py index 1d54277b..f1df0db8 100644 --- a/plugins/tLedger/examples/example_worker.py +++ b/plugins/tLedger/examples/example_worker.py @@ -1,5 +1,4 @@ import os -import sys from game_sdk.game.worker import Worker from game_sdk.game.custom_types import FunctionResult from dotenv import load_dotenv @@ -9,10 +8,8 @@ env_path = Path(__file__).parent / '.env.example' load_dotenv(dotenv_path=env_path) -# Add the project directory to the PYTHONPATH -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..'))) -from plugins.tLedger.tledger_plugin_gamesdk.tLedger_plugin import TLedgerPlugin +from tledger_plugin_gamesdk.tLedger_plugin import TLedgerPlugin def get_state_fn(function_result: FunctionResult, current_state: dict) -> dict: """ diff --git a/plugins/tLedger/plugin_metadata.yml b/plugins/tLedger/plugin_metadata.yml index 14f0e26e..71b18a04 100644 --- a/plugins/tLedger/plugin_metadata.yml +++ b/plugins/tLedger/plugin_metadata.yml @@ -10,5 +10,5 @@ detailed_description: "tLedger is t54’s foundational product—a blockchain-ag # Contact & Support x_account_handle: "@GAME_Virtuals" -support_contact: "https://discord.gg/virtualsio" +support_contact: "cfang@t54.ai" community_link: "https://t.me/virtuals" diff --git a/plugins/tLedger/pyproject.toml b/plugins/tLedger/pyproject.toml index cb931e7b..c1b43cdb 100644 --- a/plugins/tLedger/pyproject.toml +++ b/plugins/tLedger/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "tledger_plugin_gamesdk" -version = "0.1.0" +version = "0.1.2" authors = [{ name = "Prateek Tiwari", email = "ptiwari@t54.ai" }, { name = "Akshit Arora", email = "aarora@t54.ai" }] description = "TLedger SDK for GAME by Virtuals" requires-python = ">=3.8" @@ -26,7 +26,7 @@ dependencies = [ ] [tool.hatch.build.targets.wheel] -packages = ["tledger_gamesdk"] +packages = ["tledger_plugin_gamesdk"] [project.urls] "Homepage" = "https://github.com/game-by-virtuals/game-python/plugins/tLedger" diff --git a/plugins/tLedger/tledger_plugin_gamesdk/tLedger_plugin.py b/plugins/tLedger/tledger_plugin_gamesdk/tLedger_plugin.py index 0acba2f7..459ea8e5 100644 --- a/plugins/tLedger/tledger_plugin_gamesdk/tLedger_plugin.py +++ b/plugins/tLedger/tledger_plugin_gamesdk/tLedger_plugin.py @@ -3,7 +3,7 @@ import requests -from src.game_sdk.game.custom_types import Function, Argument, FunctionResultStatus +from game_sdk.game.custom_types import Function, Argument, FunctionResultStatus BASE_URL = "https://api-sandbox.t54.ai/api/v1/" From 9bdc65ec1b8be231195c224873fa143188f0bc67 Mon Sep 17 00:00:00 2001 From: Ang Weoy Yang Date: Tue, 6 May 2025 02:47:35 +0800 Subject: [PATCH 16/19] refactor: replace plugins/tLedger/examples/chat_agent.py with game_sdk import --- plugins/tLedger/examples/chat_agent.py | 138 ------------------------- 1 file changed, 138 deletions(-) delete mode 100644 plugins/tLedger/examples/chat_agent.py diff --git a/plugins/tLedger/examples/chat_agent.py b/plugins/tLedger/examples/chat_agent.py deleted file mode 100644 index 6d6e456c..00000000 --- a/plugins/tLedger/examples/chat_agent.py +++ /dev/null @@ -1,138 +0,0 @@ -from typing import Any, Callable, Dict, List, Optional, Tuple -from src.game_sdk.game.custom_types import ActionResponse, FunctionCallResponse, FunctionResult, GameChatResponse, Function, AgentMessage, ChatResponse -from src.game_sdk.game.api_v2 import GAMEClientV2 -from dotenv import load_dotenv -from pathlib import Path - -# Load environment variables from .env file -env_path = Path(__file__).parent / '.env' -load_dotenv(dotenv_path=env_path) - -class Chat: - def __init__( - self, - conversation_id: str, - client: GAMEClientV2, - action_space: Optional[List[Function]] = None, - get_state_fn: Optional[Callable[[], Dict[str, Any]]] = None, - ): - self.chat_id = conversation_id - self.client = client - self.action_space = ( - {f.fn_name: f for f in action_space} if action_space else None - ) - self.get_state_fn = get_state_fn - - def next(self, message: str) -> ChatResponse: - - convo_response = self._update_conversation(message) - - # execute functions/actions if present - if convo_response.function_call: - if not self.action_space: - raise Exception("No functions provided") - - fn_name = convo_response.function_call.fn_name - - fn_to_call = self.action_space.get(fn_name) - if not fn_to_call: - raise Exception( - f"Function {fn_name}, returned by the agent, not found in action space" - ) - - result = fn_to_call.execute( - **{ - "fn_id": convo_response.function_call.id, - "args": convo_response.function_call.args, - } - ) - response_message = self._report_function_result(result) - - # Noticing some issues with the FunctionResult model, so copying the result to a new object - result_copy = FunctionResult(action_id=result.action_id, action_status=result.action_status, feedback_message=result.feedback_message, info=result.info) - - function_call_response = FunctionCallResponse( - fn_name=fn_name, - fn_args=convo_response.function_call.args, - result=result_copy, - ) - else: - response_message = convo_response.message or "" - function_call_response = None - - return ChatResponse( - message=response_message, - is_finished=convo_response.is_finished, - function_call=function_call_response, - ) - - def end(self, message: Optional[str] = None): - self.client.end_chat( - self.chat_id, - { - "message": message, - }, - ) - - def _update_conversation(self, message: str) -> GameChatResponse: - data = { - "message": message, - "state": self.get_state_fn().__str__() if self.get_state_fn else None, - "functions": ( - [f.get_function_def() for f in self.action_space.values()] - if self.action_space - else None - ), - } - - result = self.client.update_chat(self.chat_id, data) - return GameChatResponse.model_validate(result) - - def _report_function_result(self, result: FunctionResult) -> str: - data = { - "fn_id": result.action_id, - "result": ( - f"{result.action_status.value}: {result.feedback_message}" - if result.feedback_message - else result.action_status.value - ), - } - response = self.client.report_function(self.chat_id, data) - - message = response.get("message") - if not message: - raise Exception("Agent did not return a message for the function report.") - return message - - -class ChatAgent: - def __init__( - self, - api_key: str, - prompt: str, - ): - self._api_key = api_key - self.prompt = prompt - - if api_key.startswith("apt-"): - self.client = GAMEClientV2(api_key) - else: - raise Exception("Please use V2 API key to use ChatAgent") - - def create_chat( - self, - partner_id: str, - partner_name: str, - action_space: Optional[List[Function]] = None, - get_state_fn: Optional[Callable[[], Dict[str, Any]]] = None, - ) -> Chat: - - chat_id = self.client.create_chat( - { - "prompt": self.prompt, - "partner_id": partner_id, - "partner_name": partner_name, - }, - ) - - return Chat(chat_id, self.client, action_space, get_state_fn) From 77af138344fc948c05d4b701e9e306cc75091b9f Mon Sep 17 00:00:00 2001 From: Ang Weoy Yang Date: Tue, 6 May 2025 02:48:37 +0800 Subject: [PATCH 17/19] refactor: use chat agent from game_sdk --- plugins/tLedger/examples/example_agent.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/tLedger/examples/example_agent.py b/plugins/tLedger/examples/example_agent.py index d8aaf2b6..ad8e4634 100644 --- a/plugins/tLedger/examples/example_agent.py +++ b/plugins/tLedger/examples/example_agent.py @@ -5,11 +5,10 @@ from dotenv import load_dotenv from pathlib import Path -import requests from typing_extensions import Dict -from chat_agent import ChatAgent -from game_sdk.game.custom_types import Argument, Function, FunctionResultStatus, ChatResponse, FunctionResult +from game_sdk.game.chat_agent import ChatAgent +from game_sdk.game.custom_types import Argument, Function, FunctionResultStatus, FunctionResult from tledger_plugin_gamesdk.tLedger_plugin import TLedgerPlugin @@ -173,4 +172,4 @@ def update_agent_state(current_state: dict, function_result: FunctionResult) -> chat_continue = False break -print("Chat ended") \ No newline at end of file +print("Chat ended") From 1ee52a55d483a6c889fa5f3a7ba5973dedd6b50a Mon Sep 17 00:00:00 2001 From: Ang Weoy Yang Date: Tue, 6 May 2025 02:52:47 +0800 Subject: [PATCH 18/19] docs: apply code style to credentials --- plugins/tLedger/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/tLedger/README.md b/plugins/tLedger/README.md index dc8a8e9b..9f324151 100644 --- a/plugins/tLedger/README.md +++ b/plugins/tLedger/README.md @@ -19,14 +19,14 @@ python3 ./setup.py There are also two agents set for you incase you want to test the plugin. The agent details are as follows: Agent 1: -- Agent ID: agt_59b17650-a689-4649-91fa-4bf5d0db56ad -- key: ewSZjNQGPLle-vn5dMZoLOGUljEB6fbmox31o7KLKuI -- secret: iqB7-iETCVBE0UV_0HfRAwCHkVXO9_4cCPJYmTIyUpHauHlVP4Hk5xSsCquRqBO_2_eQ6OK_Zu7P1X4LU7hSHg +- Agent ID: `agt_59b17650-a689-4649-91fa-4bf5d0db56ad` +- key: `ewSZjNQGPLle-vn5dMZoLOGUljEB6fbmox31o7KLKuI` +- secret: `iqB7-iETCVBE0UV_0HfRAwCHkVXO9_4cCPJYmTIyUpHauHlVP4Hk5xSsCquRqBO_2_eQ6OK_Zu7P1X4LU7hSHg` Agent 2: -- Agent ID: agt_3db52291-a9f8-4f04-a180-adb6e50ef5b0 -- key: j06KtBcRRbmrEAqIVSiXZc3DPAJSqymDimo__ERD0oQ -- secret: h13ERQG797cYMeNeRLvwDF_3-DBt4o-kp0fL-bFHKstTUTS5xsLUFgDEUZG2GsoEKINxeSVusbAQxc24mHm1eQ +- Agent ID: `agt_3db52291-a9f8-4f04-a180-adb6e50ef5b0` +- key: `j06KtBcRRbmrEAqIVSiXZc3DPAJSqymDimo__ERD0oQ` +- secret: `h13ERQG797cYMeNeRLvwDF_3-DBt4o-kp0fL-bFHKstTUTS5xsLUFgDEUZG2GsoEKINxeSVusbAQxc24mHm1eQ` ### TLedger Account Setup For a complete list of TLedger setup APIs, please feel free to look at the public documentation at: https://docs.t54.ai/ From dcfc12cfad7d15b537921d3126c79640c257e988 Mon Sep 17 00:00:00 2001 From: Ang Weoy Yang Date: Tue, 6 May 2025 02:55:09 +0800 Subject: [PATCH 19/19] refactor: add empty line at EOF (POSIX) --- plugins/tLedger/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tLedger/setup.py b/plugins/tLedger/setup.py index 350c503b..45831454 100644 --- a/plugins/tLedger/setup.py +++ b/plugins/tLedger/setup.py @@ -70,4 +70,4 @@ def generate_api_key(resource_id, created_by) -> dict: print(f"Sender Agent ID: {agent_id_sender}. Solana address: {agent_profile_sender["account"][0]["wallet_address"]}. Sender API Key: {api_key_sender}") print(f"Receiver Agent ID: {agent_id_receiver}. Solana address: {agent_profile_receiver["account"][0]["wallet_address"]}. Receiver API Key: {api_key_receiver}") -print(f"To add funds to your solana wallet, visit https://faucet.solana.com/") \ No newline at end of file +print(f"To add funds to your solana wallet, visit https://faucet.solana.com/")