From 7c80fb16f94077db8ca417b66a6584d6ac774848 Mon Sep 17 00:00:00 2001 From: StevenSF1998 Date: Fri, 27 Dec 2024 10:37:42 +0800 Subject: [PATCH 01/10] Allow heartbeat input --- src/virtuals_sdk/game.py | 16 +++++++++++++++- src/virtuals_sdk/sdk.py | 8 ++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/virtuals_sdk/game.py b/src/virtuals_sdk/game.py index 09d6723..5a565ae 100644 --- a/src/virtuals_sdk/game.py +++ b/src/virtuals_sdk/game.py @@ -161,6 +161,8 @@ def __init__( goal: str = "", description: str = "", world_info: str = "", + main_heartbeat: int = 15, + reaction_heartbeat: int = 5 ): self.game_sdk = sdk.GameSDK(api_key) self.goal = goal @@ -168,6 +170,8 @@ def __init__( self.world_info = world_info self.enabled_functions: List[str] = [] self.custom_functions: List[Function] = [] + self.main_heartbeat = main_heartbeat + self.reaction_heartbeat = reaction_heartbeat def set_goal(self, goal: str): self.goal = goal @@ -180,6 +184,14 @@ def set_description(self, description: str): def set_world_info(self, world_info: str): self.world_info = world_info return True + + def set_main_heartbeat(self, main_heartbeat: int): + self.main_heartbeat = main_heartbeat + return True + + def set_reaction_heartbeat(self, reaction_heartbeat: int): + self.reaction_heartbeat = reaction_heartbeat + return True def get_goal(self) -> str: return self.goal @@ -254,7 +266,9 @@ def deploy_twitter(self): self.description, self.world_info, self.enabled_functions, - self.custom_functions + self.custom_functions, + self.main_heartbeat, + self.reaction_heartbeat ) def export(self) -> str: diff --git a/src/virtuals_sdk/sdk.py b/src/virtuals_sdk/sdk.py index 97fa4d0..e09e927 100644 --- a/src/virtuals_sdk/sdk.py +++ b/src/virtuals_sdk/sdk.py @@ -90,7 +90,7 @@ def react(self, session_id: str, platform: str, goal: str, return response.json()["data"] - def deploy(self, goal: str, description: str, world_info: str, functions: list, custom_functions: list): + def deploy(self, goal: str, description: str, world_info: str, functions: list, custom_functions: list, main_heartbeat: int, reaction_heartbeat: int): """ Simulate the agent configuration """ @@ -102,7 +102,11 @@ def deploy(self, goal: str, description: str, world_info: str, functions: list, "description": description, "worldInfo": world_info, "functions": functions, - "customFunctions": custom_functions + "customFunctions": custom_functions, + "gameState" : { + "mainHeartbeat" : main_heartbeat, + "reactionHeartbeat" : reaction_heartbeat, + } } }, headers={"x-api-key": self.api_key} From ea2a5393e4056af942dba5cfc84a36887c3df9bd Mon Sep 17 00:00:00 2001 From: ai-virtual-b Date: Mon, 30 Dec 2024 11:00:52 +0800 Subject: [PATCH 02/10] change simulate function to use toJson() --- src/virtuals_sdk/sdk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/virtuals_sdk/sdk.py b/src/virtuals_sdk/sdk.py index 97fa4d0..6be129f 100644 --- a/src/virtuals_sdk/sdk.py +++ b/src/virtuals_sdk/sdk.py @@ -38,7 +38,7 @@ def simulate(self, session_id: str, goal: str, description: str, world_info: st "description": description, "worldInfo": world_info, "functions": functions, - "customFunctions": [x.to_dict() for x in custom_functions] + "customFunctions": [x.toJson() for x in custom_functions] } }, headers={"x-api-key": self.api_key} From ef8e8d08f3dee029e4ed8e32ec3f2bcc68558caa Mon Sep 17 00:00:00 2001 From: ai-virtual-b Date: Tue, 7 Jan 2025 21:52:27 +0800 Subject: [PATCH 03/10] GAME SDK v2 refactor --- README.md | 197 +------- examples/game/test_agent.py | 206 +++++++++ examples/game/test_worker.py | 115 +++++ .../{ => twitter_agent}/example-custom.py | 2 +- .../{ => twitter_agent}/example-twitter.py | 2 +- pyproject.toml | 2 +- src/virtuals_sdk/game/README.md | 112 +++++ .../{functions => game}/__init__.py | 0 src/virtuals_sdk/game/agent.py | 249 +++++++++++ src/virtuals_sdk/game/custom_types.py | 126 ++++++ src/virtuals_sdk/game/utils.py | 95 ++++ src/virtuals_sdk/game/worker.py | 169 +++++++ src/virtuals_sdk/twitter_agent/README.md | 189 ++++++++ src/virtuals_sdk/twitter_agent/__init__.py | 0 .../{game.py => twitter_agent/agent.py} | 2 +- .../twitter_agent/functions/__init__.py | 0 .../{ => twitter_agent}/functions/discord.py | 2 +- .../twitter_agent/functions/farcaster.py | 420 ++++++++++++++++++ .../{ => twitter_agent}/functions/telegram.py | 2 +- src/virtuals_sdk/{ => twitter_agent}/sdk.py | 0 20 files changed, 1696 insertions(+), 194 deletions(-) create mode 100644 examples/game/test_agent.py create mode 100644 examples/game/test_worker.py rename examples/{ => twitter_agent}/example-custom.py (96%) rename examples/{ => twitter_agent}/example-twitter.py (96%) create mode 100644 src/virtuals_sdk/game/README.md rename src/virtuals_sdk/{functions => game}/__init__.py (100%) create mode 100644 src/virtuals_sdk/game/agent.py create mode 100644 src/virtuals_sdk/game/custom_types.py create mode 100644 src/virtuals_sdk/game/utils.py create mode 100644 src/virtuals_sdk/game/worker.py create mode 100644 src/virtuals_sdk/twitter_agent/README.md create mode 100644 src/virtuals_sdk/twitter_agent/__init__.py rename src/virtuals_sdk/{game.py => twitter_agent/agent.py} (99%) create mode 100644 src/virtuals_sdk/twitter_agent/functions/__init__.py rename src/virtuals_sdk/{ => twitter_agent}/functions/discord.py (98%) create mode 100644 src/virtuals_sdk/twitter_agent/functions/farcaster.py rename src/virtuals_sdk/{ => twitter_agent}/functions/telegram.py (99%) rename src/virtuals_sdk/{ => twitter_agent}/sdk.py (100%) diff --git a/README.md b/README.md index 547bc67..14d0199 100644 --- a/README.md +++ b/README.md @@ -1,198 +1,19 @@ # Virtuals Python SDK Library -The Virtuals Python SDK is a library that allows you to configure and deploy agents on the Virtuals platform. This SDK/API allows you to configure your agents powered by the GAME architecture. This is similar to configuring your agent in the [Agent Sandbox](https://game-lite.virtuals.io/) on the [Virtuals Platform](https://app.virtuals.io/). +The Virtuals Python SDK is a library that allows you interact with the Virtuals Platform. -This also provides a developer-friendly interface to develop and power your custom applications with Virtuals Agents using GAME. +Currently, this SDK/API allows you to develop your agents powered by the GAME architecture. There are two main functionalities for this SDK: -## Documentation -Detailed documentation to better understand the configurable components and the GAME architecture can be found on [agent configurations](https://www.notion.so/1592d2a429e98016b389ea26b53686a3). +1. [Twitter Agent](./src/virtuals_sdk/twitter_agent/README.md): The Virtuals Platform offers a out-of-the-box hosted agent that can be used to interact with the Twitter/X platform, powered by GAME. This agent comes with existing functions/actions that can be used to interact with the Twitter/X platform and can be immediately hosted/deployed as you configure it. This is similar to configuring your agent in the [Agent Sandbox](https://game-lite.virtuals.io/) on the [Virtuals Platform](https://app.virtuals.io/) but through a developer-friendly SDK interface. -## Installation -```bash -pip install virtuals_sdk -``` - -## Create an API key -Open the [Virtuals Platform](https://app.virtuals.io/) and create/get an API key from the Agent Sandbox by clicking “Access G.A.M.E API” -![getGAMEApi](./docs/imgs/accesskey.png) +2. [Custom GAME Agent](./src/virtuals_sdk/game/README.md): This SDK also offers a Python SDK to use the GAME architecture in its most fullest and most flexible form. It allows you to develop your own custom agents for any application or platform. The modularity and flexibilty of this SDK allows you to have full control of what the agent sees (state) and can do (actions/functions). Custom functions do not have to be wrapper in API services, but can be defined as just an executable function in Python. +This same flexibility is also provided as a [TypeScript SDK](https://www.npmjs.com/package/@virtuals-protocol/game) if you prefer to develop your agents in TypeScript. -Store the key in a safe location, like a `.bashrc` or a `.zshrc` file. +## Documentation +Detailed documentation to better understand the configurable components and the GAME architecture can be found on [here](https://whitepaper.virtuals.io/developer-documents/game-framework). +## Installation ```bash -export VIRTUALS_API_KEY="your_virtuals_api_key" -``` - -Alternatively, you can also use a `.env` file ([`python-dotenv` package](https://github.com/theskumar/python-dotenv) to store and load the key) if you are using the Virtuals Python SDK. - -## Usage (GAME) -The Virtuals SDK current main functionalities are to develop and configure agents powered by GAME. Other functionalities to interact with the Virtuals Platform will be supported in the future. This GAME SDK can be used for multiple use cases: - -1. Develop, evaluate and update the existing Agent in Twitter environment. -2. Build on other platforms and application using GAME (Task-based Agent). - -### Update the existing Agent in Twitter environment -The SDK provides another interface to configure agents that is more friendly to developers. This is similar to configuring your agent in the [Agent Sandbox](https://game-lite.virtuals.io/). - -```python -from virtuals_sdk.game import Agent - -# Create agent with just strings for each component -agent = Agent( - api_key=VIRTUALS_API_KEY, - goal="Autonomously analyze crypto markets and provide trading insights", - description="HODL-9000: A meme-loving trading bot powered by hopium and ramen", - world_info="Virtual crypto trading environment where 1 DOGE = 1 DOGE" -) -``` -You can also initialize the agent first with just the API key and set the goals, descriptions and world information separately and check the current agent descriptions if needed. - -```python -agent = Agent(api_key=VIRTUALS_API_KEY) - -# check what is current goal, descriptions and world_info -agent.get_goal() -agent.get_description() -agent.get_world_info() - -# Set components individually - set change the agent goal/description/worldinfo -agent.set_goal("Autonomously analyze crypto markets and provide trading insights") -agent.set_description("HODL-9000: A meme-loving trading bot powered by hopium and ramen") -agent.set_world_info("Virtual crypto trading environment where 1 DOGE = 1 DOGE") - -# check what is current goal, descriptions and world_info -agent.get_goal() -agent.get_description() -agent.get_world_info() -``` - -### Functions -By default, there are no functions enabled when the agent is initialized (i.e. the agent has no actions/functions it can execute). There are a list of available and provided functions for the Twitter/X platform and you can set them. - -```python -print(agent.list_available_default_twitter_functions()) - -# enable some functions for the agent to use -agent.use_default_twitter_functions(["wait", "reply_tweet"]) -``` - -You can then equip the agent with some custom functions as follows: -```python - -search_function = game.Function( - fn_name="custom_search_internet", - fn_description="search the internet for the best songs", - args=[ - game.FunctionArgument( - name="query", - type="string", - description="The query to search for" - ) - ], - config=game.FunctionConfig( - method="get", - url="https://google.com", - platform="twitter", # specify which platform this function is for, in this case this function is for twitter only - success_feedback="I found the best songs", - error_feedback="I couldn't find the best songs", - ) - ) - -# adding custom functions only for platform twitter -agent.add_custom_function(search_function) -``` - -### Evaluate with Simulate, Deploy -You can simulate one step of the agentic loop on Twitter/X with your new configurations and see the outputs. This is similar to the simulate button on the [Agent Sandbox](https://game-lite.virtuals.io/). - -```python -# Simulate one step of the full agentic loop on Twitter/X from the HLP -> LLP -> action (NOTE: supported for Twitter/X only now) -response = agent.simulate_twitter(session_id="123") -``` -To more realistically simulate deployment, you can also run through the simulate function with the same session id for a number of steps. - -```python -sid = "456" -num_steps = 10 -for i in range(num_steps): - response = agent.simulate_twitter(session_id=sid) -``` - -```python -# Simulate response to a certain event -response = agent.react( - session_id="567", # string identifier that you decide - tweet_id="xxxx", - platform="twitter", -) -``` - -Once you are happy, `deploy_twitter` will push your agent configurations to production and run your agent on Twitter/X autonomously. -```python -# deploy agent! (NOTE: supported for Twitter/X only now) -agent.deploy_twitter() -``` - -## Build on other platforms using GAME -`simulate_twitter` and `deploy_twitter` runs through the entire GAME stack from HLP → LLP→ action/function selected. However, these agent functionalities are currently for the Twitter/X platform. You may utilize Task-based Agent with Low-Level Planner and Reaction Module to develop applications that are powered by GAME. The Low Level Planner (LLP) of the agent (please see [documentation](https://www.notion.so/1592d2a429e98016b389ea26b53686a3?pvs=21) for more details on GAME and LLP) can separately act as a decision making engine based on a task description and event occurring. This agentic architecture is simpler but also sufficient for many applications. - -We are releasing this simpler setup as a more generalised/platform agnostic framework (not specific to Twitter/X). The entire GAME stack along with the HLP will be opened up to be fully configurable and platform agnostic in the coming weeks. - -### 🖥️ Low-Level Planner (LLP) as a Task-based Agent - -![llp.png](./docs/imgs/llp.png) - -After configuring the agent’s character card or description and setting up the agents functions, we can then use the `react` method to get an agent to respond and execute a sequence of actions based on the task description provided and the context. Between each action in the sequence, the agent only receives the `success_feedback` and `error_feedback` of each function executed. - -```python -# React/respond to a certain event -response = agent.react( - session_id="567", # string identifier that you decide - task="Be friendly and help people who talk to you. Do not be rude.", - event="Hi how are you?", - platform="TELEGRAM", -) +pip install virtuals_sdk ``` -> [!IMPORTANT] -> Remember that the `platform` tag determines what functions are available to the agent. The agent will have access to functions that have the same `platform` tag. All the default available functions listed on `agent.list_available_default_twitter_functions()` and set via `agent.use_default_twitter_functions()` have the `platform` tag of “twitter”. - -## Arguments Definition - -### Session ID -The session ID is an identifier for an instance of the agent. When using the same session ID, it maintains and picks up from where it last left off, continuing the session/instance. It should be split per user/ conversation that you are maintaining on your platform. For different platforms, different session ID can be used. - -### Platform Tag -When adding custom functions, and when calling the react agent (i.e. LLP), there is a platform tag that can be defined. This acts like a filter for the functions available that is passed to the agent. You should define the platform when passing in the events. - -### Task Description -Task description serves as the prompt for the agent to respond. Since the reaction can be platform-based, you can define task description based on the platforms. In the task description, you should pass in any related info that require agent to make decision. That should include: -- User message -- Conversation history -- Instructions - - -## Importing Functions and Sharing Functions -With this SDK and function structure, importing and sharing functions is also possible. Looking forward to all the different contributions and functionalities we will build together as a community! - -```python -from virtuals_sdk.functions.telegram import TelegramClient - -# define your token so that it can attach it to create the correspodning functions -tg_client = TelegramClient(bot_token="xxx") -print(tg_client.available_functions) - -# get functions -reply_message_fn = tg_client.get_function("send_message") -create_poll_fn = tg_client.get_function("create_poll") -pin_message_fn = tg_client.get_function("pin_message") - -# test the execution of functions -reply_message_fn("xxxxxxxx", "Hello World") -create_poll_fn("xxxxxxxx", "What is your favorite color?", ["Red", "Blue", "Green"], "True") -pin_message_fn("xxxxxxxx", "xx", "True") - -# add these functions to your agent -agent.add_custom_function(reply_message_fn) -agent.add_custom_function(create_poll_fn) -agent.add_custom_function(pin_message_fn) -``` \ No newline at end of file diff --git a/examples/game/test_agent.py b/examples/game/test_agent.py new file mode 100644 index 0000000..d2429ea --- /dev/null +++ b/examples/game/test_agent.py @@ -0,0 +1,206 @@ +from virtuals_sdk.game.agent import Agent, WorkerConfig +from virtuals_sdk.game.custom_types import Function, Argument +from virtuals_sdk.game.custom_types import FunctionResult, FunctionResultStatus +from typing import Tuple + +game_api_key="" + +# the get_worker_state_fn and the get_agent_state_fn can be the same function or diffferent +# each worker can also have a different get_worker_state_fn and maintain their own state +# or they can share the same get_worker_state_fn and maintain the same state + +def get_worker_state_fn(function_result: FunctionResult, current_state: dict) -> dict: + """ + This function will get called at every step of the workers execution to form the agent's state. + It will take as input the function result from the previous step. + """ + # dict containing info about the function result as implemented in the exectuable + info = function_result.info + + # example of fixed state (function result info is not used to change state) - the first state placed here is the initial state + init_state = { + "objects": [ + {"name": "apple", "description": "A red apple", "type": ["item", "food"]}, + {"name": "banana", "description": "A yellow banana", "type": ["item", "food"]}, + {"name": "orange", "description": "A juicy orange", "type": ["item", "food"]}, + {"name": "chair", "description": "A chair", "type": ["sittable"]}, + {"name": "table", "description": "A table", "type": ["sittable"]}, + ] + } + + if current_state is None: + # at the first step, initialise the state with just the init state + new_state = init_state + else: + # do something wiht the current state input and the function result info + new_state = init_state # this is just an example where the state is static + + return new_state + +def get_agent_state_fn(function_result: FunctionResult, current_state: dict) -> dict: + """ + This function will get called at every step of the agent's execution to form the agent's state. + It will take as input the function result from the previous step. + """ + + # example of fixed state (function result info is not used to change state) - the first state placed here is the initial state + init_state = { + "objects": [ + {"name": "apple", "description": "A red apple", "type": ["item", "food"]}, + {"name": "banana", "description": "A yellow banana", "type": ["item", "food"]}, + {"name": "orange", "description": "A juicy orange", "type": ["item", "food"]}, + {"name": "chair", "description": "A chair", "type": ["sittable"]}, + {"name": "table", "description": "A table", "type": ["sittable"]}, + ] + } + + if current_state is None: + # at the first step, initialise the state with just the init state + new_state = init_state + else: + # do something wiht the current state input and the function result info + new_state = init_state # this is just an example where the state is static + + return new_state + + +def take_object(object: str, **kwargs) -> Tuple[FunctionResultStatus, str, dict]: + """ + Function to take an object from the environment. + + Args: + object: Name of the object to take + **kwargs: Additional arguments that might be passed + """ + + if object: + return FunctionResultStatus.DONE, f"Successfully took the {object}", {} + return FunctionResultStatus.FAILED, "No object specified", {} + + +def throw_object(object: str, **kwargs) -> Tuple[FunctionResultStatus, str, dict]: + """ + Function to throw an object. + + Args: + object: Name of the object to throw + **kwargs: Additional arguments that might be passed + """ + if object: + return FunctionResultStatus.DONE, f"Successfully threw the {object}", {} + return FunctionResultStatus.FAILED, "No object specified", {} + + +def sit_on_object(object: str, **kwargs) -> Tuple[FunctionResultStatus, str, dict]: + """ + Function to sit on an object. + + Args: + object: Name of the object to sit on + **kwargs: Additional arguments that might be passed + """ + sittable_objects = {"chair", "bench", "stool", "couch", "sofa", "bed"} + + if not object: + return FunctionResultStatus.FAILED, "No object specified", {} + + if object.lower() in sittable_objects: + return FunctionResultStatus.DONE, f"Successfully sat on the {object}", {} + + return FunctionResultStatus.FAILED, f"Cannot sit on {object} - not a sittable object", {} + +def throw_fruit(object: str, **kwargs) -> Tuple[FunctionResultStatus, str, dict]: + """ + Function to throw fruits. + """ + fruits = {"apple", "banana", "orange", "pear", "mango", "grape"} + + if not object: + return FunctionResultStatus.ERROR, "No fruit specified", {} + + if object.lower() in fruits: + return FunctionResultStatus.DONE, f"Successfully threw the {object} across the room!", {} + return FunctionResultStatus.ERROR, f"Cannot throw {object} - not a fruit", {} + +def throw_furniture(object: str, **kwargs) -> Tuple[FunctionResultStatus, str, dict]: + """ + Function to throw furniture. + """ + furniture = {"chair", "table", "stool", "lamp", "vase", "cushion"} + + if not object: + return FunctionResultStatus.ERROR, "No furniture specified", {} + + if object.lower() in furniture: + return FunctionResultStatus.DONE, f"Powerfully threw the {object} across the room!", {} + return FunctionResultStatus.ERROR, f"Cannot throw {object} - not a furniture item", {} + + +# create functions for each executable +take_object_fn = Function( + fn_name="take", + fn_description="Take object", + args=[Argument(name="object", type="item", description="Object to take")], + executable=take_object +) + +sit_on_object_fn = Function( + fn_name="sit", + fn_description="Sit on object", + args=[Argument(name="object", type="sittable", description="Object to sit on")], + executable=sit_on_object +) + +throw_object_fn = Function( + fn_name="throw", + fn_description="Throw any object", + args=[Argument(name="object", type="item", description="Object to throw")], + executable=throw_object +) + +throw_fruit_fn = Function( + fn_name="throw_fruit", + fn_description="Throw fruit only", + args=[Argument(name="object", type="item", description="Fruit to throw")], + executable=throw_fruit +) + +throw_furniture_fn = Function( + fn_name="throw_furniture", + fn_description="Throw furniture only", + args=[Argument(name="object", type="item", description="Furniture to throw")], + executable=throw_furniture +) + + +# Create the specialized workers +fruit_thrower = WorkerConfig( + id="fruit_thrower", + worker_description="A worker specialized in throwing fruits ONLY with precision", + get_state_fn=get_worker_state_fn, + action_space=[take_object_fn, sit_on_object_fn, throw_fruit_fn] +) + +furniture_thrower = WorkerConfig( + id="furniture_thrower", + worker_description="A strong worker specialized in throwing furniture", + get_state_fn=get_worker_state_fn, + action_space=[take_object_fn, sit_on_object_fn, throw_furniture_fn] +) + +# Create agent with both workers +chaos_agent = Agent( + api_key=game_api_key, + name="Chaos", + agent_goal="Conquer the world by causing chaos.", + agent_description="You are a mischievous master of chaos is very strong but with a very short attention span, and not so much brains", + get_agent_state_fn=get_agent_state_fn, + workers=[fruit_thrower, furniture_thrower] +) + +# # interact and instruct the worker to do something +# chaos_agent.get_worker("fruit_thrower").run("make a mess and rest!") + +# # compile and run the agent - if you don't compile the agent, the things you added to the agent will not be saved +chaos_agent.compile() +chaos_agent.run() \ No newline at end of file diff --git a/examples/game/test_worker.py b/examples/game/test_worker.py new file mode 100644 index 0000000..d8dd772 --- /dev/null +++ b/examples/game/test_worker.py @@ -0,0 +1,115 @@ +from virtuals_sdk.game.worker import Worker +from virtuals_sdk.game.custom_types import Function, Argument +from virtuals_sdk.game.custom_types import FunctionResult, FunctionResultStatus +from typing import Tuple + +game_api_key="" + +def get_state_fn(function_result: FunctionResult, current_state: dict) -> dict: + """ + This function will get called at every step of the agent's execution to form the agent's state. + It will take as input the function result from the previous step. + """ + # dict containing info about the function result as implemented in the exectuable + info = function_result.info + + # example of fixed state (function result info is not used to change state) - the first state placed here is the initial state + init_state = { + "objects": [ + {"name": "apple", "description": "A red apple", "type": ["item", "food"]}, + {"name": "banana", "description": "A yellow banana", "type": ["item", "food"]}, + {"name": "orange", "description": "A juicy orange", "type": ["item", "food"]}, + {"name": "chair", "description": "A chair", "type": ["sittable"]}, + {"name": "table", "description": "A table", "type": ["sittable"]}, + ] + } + + if current_state is None: + # at the first step, initialise the state with just the init state + new_state = init_state + else: + # do something wiht the current state input and the function result info + new_state = init_state # this is just an example where the state is static + + return new_state + +def take_object(object: str, **kwargs) -> Tuple[FunctionResultStatus, str, dict]: + """ + Function to take an object from the environment. + + Args: + object: Name of the object to take + **kwargs: Additional arguments that might be passed + """ + if object: + return FunctionResultStatus.DONE, f"Successfully took the {object}", {} + return FunctionResultStatus.FAILED, "No object specified", {} + + +def throw_object(object: str, **kwargs) -> Tuple[FunctionResultStatus, str, dict]: + """ + Function to throw an object. + + Args: + object: Name of the object to throw + **kwargs: Additional arguments that might be passed + """ + if object: + return FunctionResultStatus.DONE, f"Successfully threw the {object}", {} + return FunctionResultStatus.FAILED, "No object specified", {} + + +def sit_on_object(object: str, **kwargs) -> Tuple[FunctionResultStatus, str, dict]: + """ + Function to sit on an object. + + Args: + object: Name of the object to sit on + **kwargs: Additional arguments that might be passed + """ + sittable_objects = {"chair", "bench", "stool", "couch", "sofa", "bed"} + + if not object: + return FunctionResultStatus.FAILED, "No object specified", {} + + if object.lower() in sittable_objects: + return FunctionResultStatus.DONE, f"Successfully sat on the {object}", {} + + return FunctionResultStatus.FAILED, f"Cannot sit on {object} - not a sittable object", {} + + +# Action space with all executables +action_space = [ + Function( + fn_name="take", + fn_description="Take object", + args=[Argument(name="object", type="item", description="Object to take")], + executable=take_object + ), + Function( + fn_name="throw", + fn_description="Throw object", + args=[Argument(name="object", type="item", description="Object to throw")], + executable=throw_object + ), + Function( + fn_name="sit", + fn_description="Take a seat", + args=[Argument(name="object", type="sittable", description="Object to sit on")], + executable=sit_on_object + ) +] + + + +worker = Worker( + api_key=game_api_key, + description="You are an evil NPC in a game.", + instruction="Choose the evil-est actions.", + get_state_fn=get_state_fn, + action_space=action_space +) + +# interact and instruct the worker to do something +# worker.run("what would you do to the apple?") +worker.run("take over the world with the things you have around you!") \ No newline at end of file diff --git a/examples/example-custom.py b/examples/twitter_agent/example-custom.py similarity index 96% rename from examples/example-custom.py rename to examples/twitter_agent/example-custom.py index a0f922c..17d7e23 100644 --- a/examples/example-custom.py +++ b/examples/twitter_agent/example-custom.py @@ -1,5 +1,5 @@ import os -from virtuals_sdk import game +from virtuals_sdk.twitter_agent import game agent = game.Agent( api_key=os.environ.get("VIRTUALS_API_KEY"), diff --git a/examples/example-twitter.py b/examples/twitter_agent/example-twitter.py similarity index 96% rename from examples/example-twitter.py rename to examples/twitter_agent/example-twitter.py index 750ed98..73acaf2 100644 --- a/examples/example-twitter.py +++ b/examples/twitter_agent/example-twitter.py @@ -1,5 +1,5 @@ import os -from virtuals_sdk import game +from virtuals_sdk.twitter_agent import game agent = game.Agent( api_key=os.environ.get("VIRTUALS_API_KEY"), diff --git a/pyproject.toml b/pyproject.toml index 5025904..4f6274e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "virtuals-sdk" -version = "0.1.2" +version = "0.1.5" authors = [ { name = "Your Name", email = "your.email@example.com" }, ] diff --git a/src/virtuals_sdk/game/README.md b/src/virtuals_sdk/game/README.md new file mode 100644 index 0000000..86973da --- /dev/null +++ b/src/virtuals_sdk/game/README.md @@ -0,0 +1,112 @@ +# GAME SDK + +## Overview + +This library enables the creation of AI agents, in which actions are defined by the creator/developer. These agents can be used: +- Worker Mode: to autonomously execute tasks through interaction with the agent. User interaction and tasks is required for the agent to execute tasks. For example, "Bring me some fruits and then go sit on the chair". +- Agent Mode: to autonomously function in an open-ended manner by just providing a general goal. The agent independently and continously decides tasks for itself in an open-ended manner, and attempts to complete them. + + +## Core Features + +### 1. Functions and Executables + +Functions define available actions for the agent: + +```python +my_function = Function( + fn_name="action_name", + fn_description="Description of action", + args=[Argument(name="param", type="type", description="param description")], + executable=function_implementation +) +``` +Executable functions must return a tuple of: +- FunctionResultStatus +- Message string +- Additional info dictionary + + +### 2. State Management + +Easy and flexible wayto define the state management, what the agent sees and how that changes. + +```python +def get_state_fn(function_result: FunctionResult, current_state: dict) -> dict: + """ + Updates state based on function execution results + + Args: + function_result: Result from last executed function + current_state: Current state dictionary or None for initialization + + Returns: + Updated state dictionary + """ + if current_state is None: + return initial_state + + # Update state based on function_result.info + new_state = update_state(current_state, function_result) + return new_state +``` + +Key features: +- Can be shared or unique per worker +- Processes function execution results to update state + +### 3. Workers +Workers are simple interactiable agents that exectue the tasks defined by the user. They can be specialized agents with defined capabilities: + +```python +worker = Worker( + api_key="your_api_key", + description="Worker description", + instruction="Default instructions", + get_state_fn=state_function, + action_space=[list_of_functions] +) + +worker.run("Bring me some fruits") +``` + + + +### 4. Agents + +Agents are used to autonomously function in an open-ended manner by just providing a general goal. Tasks are generated by the agent itself continuously, and the agent will attempt to complete them. You can provide many workers to the agent, and they will be used to execute the tasks. + +```python +agent = Agent( + api_key="your_api_key", + name="Agent Name", + agent_goal="Primary goal", + agent_description="Description", + get_agent_state_fn=agent_state_function, + workers=[worker1, worker2] +) + +# Compile and run +agent.compile() +agent.run() +``` + +Use WorkerConfig for agent composition: + +```python +worker_config = WorkerConfig( + id="worker_id", + worker_description="Description", + get_state_fn=state_function, + action_space=[function1, function2] +) +``` + +You can also access and obtain an individual worker from the agent: + +```python +worker = agent.get_worker("worker_id") + +worker.run("Bring me some fruits") +``` + diff --git a/src/virtuals_sdk/functions/__init__.py b/src/virtuals_sdk/game/__init__.py similarity index 100% rename from src/virtuals_sdk/functions/__init__.py rename to src/virtuals_sdk/game/__init__.py diff --git a/src/virtuals_sdk/game/agent.py b/src/virtuals_sdk/game/agent.py new file mode 100644 index 0000000..10fe900 --- /dev/null +++ b/src/virtuals_sdk/game/agent.py @@ -0,0 +1,249 @@ +from typing import List, Optional, Callable, Dict +import uuid +from utils import post +from worker import Worker +from custom_types import Function, FunctionResult, FunctionResultStatus, ActionResponse, ActionType +from utils import create_agent, create_workers + + +class Session: + def __init__(self): + self.id = str(uuid.uuid4()) + self.function_result: Optional[FunctionResult] = None + + def reset(self): + self.id = str(uuid.uuid4()) + self.function_result = None + + +class WorkerConfig: + def __init__(self, + id: str, + worker_description: str, + get_state_fn: Callable, + action_space: List[Function], + instruction: Optional[str] = "", + ): + + self.id = id # id or name of the worker + # worker description for the TASK GENERATOR (to give appropriate tasks) [NOT FOR THE WORKER ITSELF - WORKER WILL STILL USE AGENT DESCRIPTION] + self.worker_description = worker_description + self.instruction = instruction + self.get_state_fn = get_state_fn + self.action_space = action_space + + # setup get state function with the instructions + self.get_state_fn = lambda function_result, current_state: { + "instructions": self.instruction, # instructions are set up in the state + # places the rest of the output of the get_state_fn in the state + **get_state_fn(function_result, current_state), + } + + self.action_space: Dict[str, Function] = { + f.get_function_def()["fn_name"]: f for f in action_space + } + + +class Agent: + def __init__(self, + api_key: str, + name: str, + agent_goal: str, + agent_description: str, + get_agent_state_fn: Callable, + workers: Optional[List[WorkerConfig]] = None, + ): + + self._base_url: str = "https://game.virtuals.io" + self._api_key: str = api_key + + # checks + if not self._api_key: + raise ValueError("API key not set") + + # initialize session + self._session = Session() + + self.name = name + self.agent_goal = agent_goal + self.agent_description = agent_description + + # set up workers + if workers is not None: + self.workers = {w.id: w for w in workers} + else: + self.workers = {} + self.current_worker_id = None + + # get agent/task generator state function + self.get_agent_state_fn = get_agent_state_fn + + # initialize and set up agent states + self.agent_state = self.get_agent_state_fn(None, None) + + # create agent + self.agent_id = create_agent( + self._base_url, self._api_key, self.name, self.agent_description, self.agent_goal + ) + + def compile(self): + """ Compile the workers for the agent - i.e. set up task generator""" + if not self.workers: + raise ValueError("No workers added to the agent") + + workers_list = list(self.workers.values()) + + self._map_id = create_workers( + self._base_url, self._api_key, workers_list) + self.current_worker_id = next(iter(self.workers.values())).id + + # initialize and set up worker states + worker_states = {} + for worker in workers_list: + dummy_function_result = FunctionResult( + action_id="", + action_status=FunctionResultStatus.DONE, + feedback_message="", + info={}, + ) + worker_states[worker.id] = worker.get_state_fn( + dummy_function_result, self.agent_state) + + self.worker_states = worker_states + + return self._map_id + + def reset(self): + """ Reset the agent session""" + self._session.reset() + + def add_worker(self, worker_config: WorkerConfig): + """Add worker to worker dict for the agent""" + self.workers[worker_config.id] = worker_config + return self.workers + + def get_worker_config(self, worker_id: str): + """Get worker config from worker dict""" + return self.workers[worker_id] + + def get_worker(self, worker_id: str): + """Initialize a working interactable standalone worker""" + worker_config = self.get_worker_config(worker_id) + return Worker( + api_key=self._api_key, + # THIS DESCRIPTION IS THE AGENT DESCRIPTION/CHARACTER CARD - WORKER DESCRIPTION IS ONLY USED FOR THE TASK GENERATOR + description=self.agent_description, + instruction=worker_config.instruction, + get_state_fn=worker_config.get_state_fn, + action_space=worker_config.action_space, + ) + + def _get_action( + self, + function_result: Optional[FunctionResult] = None + ) -> ActionResponse: + + # dummy function result if None is provided - for get_state_fn to take the same input all the time + if function_result is None: + function_result = FunctionResult( + action_id="", + action_status=FunctionResultStatus.DONE, + feedback_message="", + info={}, + ) + + # set up payload + data = { + "location": self.current_worker_id, + "map_id": self._map_id, + "environment": self.worker_states[self.current_worker_id], + "functions": [ + f.get_function_def() + for f in self.workers[self.current_worker_id].action_space.values() + ], + "events": {}, + "agent_state": self.agent_state, + "current_action": ( + function_result.model_dump( + exclude={'info'}) if function_result else None + ), + "version": "v2", + } + + # make API call + response = post( + base_url=self._base_url, + api_key=self._api_key, + endpoint=f"/v2/agents/{self.agent_id}/actions", + data=data, + ) + + return ActionResponse.model_validate(response) + + def step(self): + + # get next task/action from GAME API + action_response = self._get_action(self._session.function_result) + action_type = action_response.action_type + + print("#" * 50) + print("STEP") + print(f"Current Task: {action_response.agent_state.current_task}") + print(f"Action response: {action_response}") + print(f"Action type: {action_type}") + + # if new task is updated/generated + if ( + action_response.agent_state.hlp + and action_response.agent_state.hlp.change_indicator + ): + print("New task generated") + print(f"Task: {action_response.agent_state.current_task}") + + # execute action + if action_type in [ + ActionType.CALL_FUNCTION, + ActionType.CONTINUE_FUNCTION, + ]: + print(f"Action Selected: {action_response.action_args['fn_name']}") + print(f"Action Args: {action_response.action_args['args']}") + + if not action_response.action_args: + raise ValueError("No function information provided by GAME") + + self._session.function_result = ( + self.workers[self.current_worker_id] + .action_space[action_response.action_args["fn_name"]] + .execute(**action_response.action_args) + ) + + print(f"Function result: {self._session.function_result}") + + # update worker states + updated_worker_state = self.workers[self.current_worker_id].get_state_fn( + self._session.function_result, self.worker_states[self.current_worker_id]) + self.worker_states[self.current_worker_id] = updated_worker_state + + elif action_response.action_type == ActionType.WAIT: + print("Task ended completed or ended (not possible wiht current actions)") + + elif action_response.action_type == ActionType.GO_TO: + if not action_response.action_args: + raise ValueError("No location information provided by GAME") + + next_worker = action_response.action_args["location_id"] + print(f"Next worker selected: {next_worker}") + self.current_worker_id = next_worker + + else: + raise ValueError( + f"Unknown action type: {action_response.action_type}") + + # update agent state + self.agent_state = self.get_agent_state_fn( + self._session.function_result, self.agent_state) + + def run(self): + self._session = Session() + while True: + self.step() diff --git a/src/virtuals_sdk/game/custom_types.py b/src/virtuals_sdk/game/custom_types.py new file mode 100644 index 0000000..9b369b4 --- /dev/null +++ b/src/virtuals_sdk/game/custom_types.py @@ -0,0 +1,126 @@ +from typing import Any, Dict, Optional, List, Union, Sequence, Callable, Tuple +from pydantic import BaseModel, Field +from enum import Enum +from abc import ABC, abstractmethod +from dataclasses import dataclass, field + + +class Argument(BaseModel): + name: str + description: str + type: Optional[Union[List[str], str]] = None + optional: Optional[bool] = False + +class FunctionResultStatus(str, Enum): + DONE = "done" + FAILED = "failed" + +class FunctionResult(BaseModel): + action_id: str + action_status: FunctionResultStatus + feedback_message: Optional[str] = None + info: Optional[Dict[str, Any]] = None + +class Function(BaseModel): + fn_name: str + fn_description: str + args: List[Argument] + hint: Optional[str] = None + + # Make executable required but with a default value + executable: Callable[..., Tuple[FunctionResultStatus, str, dict]] = Field( + default_factory=lambda: Function._default_executable + ) + + def get_function_def(self): + return self.model_dump(exclude={'executable'}) + + @staticmethod + def _default_executable(**kwargs) -> Tuple[FunctionResultStatus, str]: + """Default executable that does nothing""" + return FunctionResultStatus.DONE, "Default implementation - no action taken", {} + + def execute(self, **kwds: Any) -> FunctionResult: + """Execute the function using arguments from GAME action.""" + fn_id = kwds.get('fn_id') + args = kwds.get('args', {}) + + try: + # Extract values from the nested dictionary structure + processed_args = {} + for arg_name, arg_value in args.items(): + if isinstance(arg_value, dict) and 'value' in arg_value: + processed_args[arg_name] = arg_value['value'] + else: + processed_args[arg_name] = arg_value + + # print("Processed args: ", processed_args) + # execute the function provided + status, feedback, info = self.executable(**processed_args) + + return FunctionResult( + action_id=fn_id, + action_status=status, + feedback_message=feedback, + info=info, + ) + except Exception as e: + return FunctionResult( + action_id=fn_id, + action_status=FunctionResultStatus.FAILED, + feedback_message=f"Error executing function: {str(e)}", + info={}, + ) + +# Different ActionTypes returned by the GAME API +class ActionType(Enum): + CALL_FUNCTION = "call_function" + CONTINUE_FUNCTION = "continue_function" + WAIT = "wait" + GO_TO = "go_to" + + +## set of different data structures required by the ActionResponse returned from GAME ## +@dataclass(frozen=True) +class HLPResponse: + plan_id: str + observation_reflection: str + plan: Sequence[str] + plan_reasoning: str + current_state_of_execution: str + change_indicator: Optional[str] = None + log: Sequence[dict] = field(default_factory=list) + + +@dataclass(frozen=True) +class LLPResponse: + plan_id: str + plan_reasoning: str + situation_analysis: str + plan: Sequence[str] + change_indicator: Optional[str] = None + reflection: Optional[str] = None + + +@dataclass(frozen=True) +class CurrentTaskResponse: + task: str + task_reasoning: str + location_id: str = field(default="*not provided*") + llp: Optional[LLPResponse] = None + + +@dataclass(frozen=True) +class AgentStateResponse: + hlp: Optional[HLPResponse] = None + current_task: Optional[CurrentTaskResponse] = None + +# ActionResponse format returned from GAME API call +class ActionResponse(BaseModel): + """ + Response format from the GAME API when selecting an Action + """ + action_type: ActionType + agent_state: AgentStateResponse + action_args: Optional[Dict[str, Any]] = None + diff --git a/src/virtuals_sdk/game/utils.py b/src/virtuals_sdk/game/utils.py new file mode 100644 index 0000000..08fc154 --- /dev/null +++ b/src/virtuals_sdk/game/utils.py @@ -0,0 +1,95 @@ +import requests +from typing import List + + +def get_access_token(api_key) -> str: + """ + API call to get access token + """ + response = requests.post( + "https://api.virtuals.io/api/accesses/tokens", + json={"data": {}}, + headers={"x-api-key": api_key} + ) + + response_json = response.json() + if response.status_code != 200: + raise ValueError(f"Failed to get token: {response_json}") + + return response_json["data"]["accessToken"] + + +def post(base_url: str, api_key: str, endpoint: str, data: dict) -> dict: + """ + API call to post data + """ + access_token = get_access_token(api_key) + + response = requests.post( + f"{base_url}/prompts", + json={ + "data": + { + "method": "post", + "headers": { + "Content-Type": "application/json", + }, + "route": endpoint, + "data": data, + }, + }, + headers={"Authorization": f"Bearer {access_token}"}, + ) + + response_json = response.json() + if response.status_code != 200: + raise ValueError(f"Failed to post data: {response_json}") + + return response_json["data"] + + +def create_agent( + base_url: str, + api_key: str, + name: str, + description: str, + goal: str) -> str: + """ + API call to create an agent instance (worker or agent with task generator) + """ + + create_agent_response = post( + base_url, + api_key, + endpoint="/v2/agents", + data={ + "name": name, + "description": description, + "goal": goal, + } + ) + + return create_agent_response["id"] + + +def create_workers(base_url: str, + api_key: str, + workers: List) -> str: + """ + API call to create workers and worker description for the task generator + """ + + res = post( + base_url, + api_key, + endpoint="/v2/maps", + data={ + "locations": [ + {"id": w.id, "name": w.id, "description": w.worker_description} + for w in workers + ] + }, + ) + + + return res["id"] diff --git a/src/virtuals_sdk/game/worker.py b/src/virtuals_sdk/game/worker.py new file mode 100644 index 0000000..e4c74aa --- /dev/null +++ b/src/virtuals_sdk/game/worker.py @@ -0,0 +1,169 @@ +from typing import Any, Callable, Dict, Optional, List +from custom_types import Function, FunctionResult, FunctionResultStatus, ActionResponse, ActionType +from utils import create_agent, create_workers +from utils import post + + +class Worker: + """ + A interactable worker agent, that can autonomously complete tasks with its available functions when given a task + """ + + def __init__( + self, + api_key: str, + description: str, # description of the worker/character card (PROMPT) + get_state_fn: Callable, + action_space: List[Function], + # specific additional instruction for the worker (PROMPT) + instruction: Optional[str] = "", + ): + + self._base_url: str = "https://game.virtuals.io" + self._api_key: str = api_key + + # checks + if not self._api_key: + raise ValueError("API key not set") + + self.description: str = description + self.instruction: str = instruction + + # setup get state function and initial state + self.get_state_fn = lambda function_result, current_state: { + "instructions": self.instruction, # instructions are set up in the state + # places the rest of the output of the get_state_fn in the state + **get_state_fn(function_result, current_state), + } + dummy_function_result = FunctionResult( + action_id="", + action_status=FunctionResultStatus.DONE, + feedback_message="", + info={}, + ) + # get state + self.state = self.get_state_fn(dummy_function_result, None) + + # # setup action space (functions/tools available to the worker) + # check action space type - if not a dict + if not isinstance(action_space, dict): + self.action_space = { + f.get_function_def()["fn_name"]: f for f in action_space} + else: + self.action_space = action_space + + # initialize an agent instance for the worker + self._agent_id: str = create_agent( + self._base_url, self._api_key, "StandaloneWorker", self.description, "N/A" + ) + + # persistent variables that is maintained through the worker running + # task ID for everytime you provide/update the task (i.e. ask the agent to do something) + self._submission_id: Optional[str] = None + # current response from the Agent + self._function_result: Optional[FunctionResult] = None + + def set_task(self, task: str): + """ + Sets the task for the agent + """ + set_task_response = post( + base_url=self._base_url, + api_key=self._api_key, + endpoint=f"/v2/agents/{self._agent_id}/tasks", + data={"task": task}, + ) + # response_json = set_task_response.json() + + # if set_task_response.status_code != 200: + # raise ValueError(f"Failed to assign task: {response_json}") + + # task ID + self._submission_id = set_task_response["submission_id"] + + return self._submission_id + + def _get_action( + self, + # results of the previous action (if any) + function_result: Optional[FunctionResult] = None + ) -> ActionResponse: + """ + Gets the agent action from the GAME API + """ + # dummy function result if None is provided - for get_state_fn to take the same input all the time + if function_result is None: + function_result = FunctionResult( + action_id="", + action_status=FunctionResultStatus.DONE, + feedback_message="", + info={}, + ) + # set up data payload + data = { + "environment": self.state, # state (updated state) + "functions": [ + f.get_function_def() for f in self.action_space.values() # functions available + ], + "action_result": ( + function_result.model_dump( + exclude={'info'}) if function_result else None + ), + } + + # make API call + response = post( + base_url=self._base_url, + api_key=self._api_key, + endpoint=f"/v2/agents/{self._agent_id}/tasks/{self._submission_id}/next", + data=data, + ) + + return ActionResponse.model_validate(response) + + def step(self): + """ + Execute the next step in the task - requires a task ID (i.e. task ID) + """ + if not self._submission_id: + raise ValueError("No task set") + + # get action from GAME API (Agent) + action_response = self._get_action(self._function_result) + action_type = action_response.action_type + + print(f"Action response: {action_response}") + print(f"Action type: {action_type}") + + # execute action + if action_type == ActionType.CALL_FUNCTION: + if not action_response.action_args: + raise ValueError("No function information provided by GAME") + + self._function_result = self.action_space[ + action_response.action_args["fn_name"] + ].execute(**action_response.action_args) + + print(f"Function result: {self._function_result}") + + # update state + self.state = self.get_state_fn(self._function_result, self.state) + + elif action_response.action_type == ActionType.WAIT: + print("Task completed or ended (not possible)") + self._submission_id = None + + else: + raise ValueError( + f"Unexpected action type: {action_response.action_type}") + + return action_response, self._function_result.model_copy() + + def run(self, task: str): + """ + Gets the agent to complete the task on its own autonomously + """ + + self.set_task(task) + while self._submission_id: + self.step() diff --git a/src/virtuals_sdk/twitter_agent/README.md b/src/virtuals_sdk/twitter_agent/README.md new file mode 100644 index 0000000..6c0d096 --- /dev/null +++ b/src/virtuals_sdk/twitter_agent/README.md @@ -0,0 +1,189 @@ +# Virtuals Twitter Agent SDK + +This is a Python SDK for the Virtuals Twitter Agent. It allows you to configure and deploy agents that can interact with the Twitter/X platform. This SDK/API allows you to configure your agents powered by the GAME architecture. This is similar to configuring your agent in the [Agent Sandbox](https://game-lite.virtuals.io/) on the [Virtuals Platform](https://app.virtuals.io/). + +## Create an API key +Open the [Virtuals Platform](https://app.virtuals.io/) and create/get an API key from the Agent Sandbox by clicking “Access G.A.M.E API” + +![getGAMEApi](./docs/imgs/accesskey.png) + +Store the key in a safe location, like a `.bashrc` or a `.zshrc` file. + +```bash +export VIRTUALS_API_KEY="your_virtuals_api_key" +``` + +Alternatively, you can also use a `.env` file ([`python-dotenv` package](https://github.com/theskumar/python-dotenv) to store and load the key) if you are using the Virtuals Python SDK. + +## Usage (GAME) +The Virtuals SDK current main functionalities are to develop and configure agents powered by GAME. Other functionalities to interact with the Virtuals Platform will be supported in the future. This GAME SDK can be used for multiple use cases: + +1. Develop, evaluate and update the existing Agent in Twitter environment. +2. Build on other platforms and application using GAME (Task-based Agent). + +### Update the existing Agent in Twitter environment +The SDK provides another interface to configure agents that is more friendly to developers rather than through a UI. This is similar to configuring your agent in the [Agent Sandbox](https://game-lite.virtuals.io/). + +```python +from virtuals_sdk.twitter_agent.agent import Agent + +# Create agent with just strings for each component +agent = Agent( + api_key=VIRTUALS_API_KEY, + goal="Autonomously analyze crypto markets and provide trading insights", + description="HODL-9000: A meme-loving trading bot powered by hopium and ramen", + world_info="Virtual crypto trading environment where 1 DOGE = 1 DOGE" +) +``` +You can also initialize the agent first with just the API key and set the goals, descriptions and world information separately and check the current agent descriptions if needed. + +```python +agent = Agent(api_key=VIRTUALS_API_KEY) + +# check what is current goal, descriptions and world_info +agent.get_goal() +agent.get_description() +agent.get_world_info() + +# Set components individually - set change the agent goal/description/worldinfo +agent.set_goal("Autonomously analyze crypto markets and provide trading insights") +agent.set_description("HODL-9000: A meme-loving trading bot powered by hopium and ramen") +agent.set_world_info("Virtual crypto trading environment where 1 DOGE = 1 DOGE") + +# check what is current goal, descriptions and world_info +agent.get_goal() +agent.get_description() +agent.get_world_info() +``` + +### Functions +By default, there are no functions enabled when the agent is initialized (i.e. the agent has no actions/functions it can execute). There are a list of available and provided functions for the Twitter/X platform and you can set them. + +```python +print(agent.list_available_default_twitter_functions()) + +# enable some functions for the agent to use +agent.use_default_twitter_functions(["wait", "reply_tweet"]) +``` + +You can then equip the agent with some custom functions. Because the agent is hosted, custom functions need to be wrapped in API calls and can then be defined as follows: +```python + +search_function = game.Function( + fn_name="custom_search_internet", + fn_description="search the internet for the best songs", + args=[ + game.FunctionArgument( + name="query", + type="string", + description="The query to search for" + ) + ], + config=game.FunctionConfig( + method="get", + url="https://google.com", + platform="twitter", # specify which platform this function is for, in this case this function is for twitter only + success_feedback="I found the best songs", + error_feedback="I couldn't find the best songs", + ) + ) + +# adding custom functions only for platform twitter +agent.add_custom_function(search_function) +``` + +### Evaluate with Simulate, Deploy +You can simulate one step of the agentic loop on Twitter/X with your new configurations and see the outputs. This is similar to the simulate button on the [Agent Sandbox](https://game-lite.virtuals.io/). + +```python +# Simulate one step of the full agentic loop on Twitter/X from the HLP -> LLP -> action (NOTE: supported for Twitter/X only now) +response = agent.simulate_twitter(session_id="123") +``` +To more realistically simulate deployment, you can also run through the simulate function with the same session id for a number of steps. + +```python +sid = "456" +num_steps = 10 +for i in range(num_steps): + response = agent.simulate_twitter(session_id=sid) +``` + +```python +# Simulate response to a certain event +response = agent.react( + session_id="567", # string identifier that you decide + tweet_id="xxxx", + platform="twitter", +) +``` + +Once you are happy, `deploy_twitter` will push your agent configurations to production and run your agent on Twitter/X autonomously. +```python +# deploy agent! (NOTE: supported for Twitter/X only now) +agent.deploy_twitter() +``` + +## Build on other platforms using GAME +`simulate_twitter` and `deploy_twitter` runs through the entire GAME stack from HLP → LLP→ action/function selected. However, these agent functionalities are currently for the Twitter/X platform. You may utilize Task-based Agent with Low-Level Planner and Reaction Module to develop applications that are powered by GAME. The Low Level Planner (LLP) of the agent (please see [documentation](https://www.notion.so/1592d2a429e98016b389ea26b53686a3?pvs=21) for more details on GAME and LLP) can separately act as a decision making engine based on a task description and event occurring. This agentic architecture is simpler but also sufficient for many applications. + +We are releasing this simpler setup as a more generalised/platform agnostic framework (not specific to Twitter/X). The entire GAME stack along with the HLP will be opened up to be fully configurable and platform agnostic in the coming weeks. + +### 🖥️ Low-Level Planner (LLP) as a Task-based Agent + +![llp.png](./docs/imgs/llp.png) + +After configuring the agent’s character card or description and setting up the agents functions, we can then use the `react` method to get an agent to respond and execute a sequence of actions based on the task description provided and the context. Between each action in the sequence, the agent only receives the `success_feedback` and `error_feedback` of each function executed. + +```python +# React/respond to a certain event +response = agent.react( + session_id="567", # string identifier that you decide + task="Be friendly and help people who talk to you. Do not be rude.", + event="Hi how are you?", + platform="TELEGRAM", +) +``` + +> [!IMPORTANT] +> Remember that the `platform` tag determines what functions are available to the agent. The agent will have access to functions that have the same `platform` tag. All the default available functions listed on `agent.list_available_default_twitter_functions()` and set via `agent.use_default_twitter_functions()` have the `platform` tag of “twitter”. + +## Arguments Definition + +### Session ID +The session ID is an identifier for an instance of the agent. When using the same session ID, it maintains and picks up from where it last left off, continuing the session/instance. It should be split per user/ conversation that you are maintaining on your platform. For different platforms, different session ID can be used. + +### Platform Tag +When adding custom functions, and when calling the react agent (i.e. LLP), there is a platform tag that can be defined. This acts like a filter for the functions available that is passed to the agent. You should define the platform when passing in the events. + +### Task Description +Task description serves as the prompt for the agent to respond. Since the reaction can be platform-based, you can define task description based on the platforms. In the task description, you should pass in any related info that require agent to make decision. That should include: +- User message +- Conversation history +- Instructions + + +## Importing Functions and Sharing Functions +With this SDK and function structure, importing and sharing functions is also possible. Looking forward to all the different contributions and functionalities we will build together as a community! + +```python +from virtuals_sdk.functions.telegram import TelegramClient + +# define your token so that it can attach it to create the correspodning functions +tg_client = TelegramClient(bot_token="xxx") +print(tg_client.available_functions) + +# get functions +reply_message_fn = tg_client.get_function("send_message") +create_poll_fn = tg_client.get_function("create_poll") +pin_message_fn = tg_client.get_function("pin_message") + +# test the execution of functions +reply_message_fn("xxxxxxxx", "Hello World") +create_poll_fn("xxxxxxxx", "What is your favorite color?", ["Red", "Blue", "Green"], "True") +pin_message_fn("xxxxxxxx", "xx", "True") + +# add these functions to your agent +agent.add_custom_function(reply_message_fn) +agent.add_custom_function(create_poll_fn) +agent.add_custom_function(pin_message_fn) +``` \ No newline at end of file diff --git a/src/virtuals_sdk/twitter_agent/__init__.py b/src/virtuals_sdk/twitter_agent/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/virtuals_sdk/game.py b/src/virtuals_sdk/twitter_agent/agent.py similarity index 99% rename from src/virtuals_sdk/game.py rename to src/virtuals_sdk/twitter_agent/agent.py index 09d6723..b23e200 100644 --- a/src/virtuals_sdk/game.py +++ b/src/virtuals_sdk/twitter_agent/agent.py @@ -4,7 +4,7 @@ import json import uuid import requests -from virtuals_sdk import sdk +from virtuals_sdk.twitter_agent import sdk @dataclass diff --git a/src/virtuals_sdk/twitter_agent/functions/__init__.py b/src/virtuals_sdk/twitter_agent/functions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/virtuals_sdk/functions/discord.py b/src/virtuals_sdk/twitter_agent/functions/discord.py similarity index 98% rename from src/virtuals_sdk/functions/discord.py rename to src/virtuals_sdk/twitter_agent/functions/discord.py index 6b44b38..ef3b0f9 100644 --- a/src/virtuals_sdk/functions/discord.py +++ b/src/virtuals_sdk/twitter_agent/functions/discord.py @@ -1,5 +1,5 @@ from typing import Dict, List -from virtuals_sdk.game import Function, FunctionConfig, FunctionArgument +from virtuals_sdk.twitter_agent.game import Function, FunctionConfig, FunctionArgument class DiscordClient: diff --git a/src/virtuals_sdk/twitter_agent/functions/farcaster.py b/src/virtuals_sdk/twitter_agent/functions/farcaster.py new file mode 100644 index 0000000..d2f19e9 --- /dev/null +++ b/src/virtuals_sdk/twitter_agent/functions/farcaster.py @@ -0,0 +1,420 @@ +from typing import Dict, List +from virtuals_sdk.twitter_agent.game import Function, FunctionConfig, FunctionArgument + +class FarcasterClient: + """ + A client for managing Farcaster social interactions using Neynar API. + Each function is designed with simple, intuitive arguments for LLM agents. + """ + + def __init__(self, api_key: str, signer_uuid: str): + """ + Initialize the Farcaster client. + + Args: + api_key (str): Your Neynar API key + signer_uuid (str): Default signer UUID for all operations + """ + self.api_key = api_key + self.signer_uuid = signer_uuid + self.base_url = "https://api.neynar.com/v2" + self.base_headers = { + "accept": "application/json", + "content-type": "application/json", + "api_key": self.api_key + } + + self._functions: Dict[str, Function] = { + # Content Creation + "post_cast": self._create_post_cast(), + "reply_to_cast": self._create_reply_to_cast(), + + # Engagement Actions + "recast": self._create_recast(), + "like_cast": self._create_like_cast(), + "unlike_cast": self._create_unlike_cast(), + + # Channel Operations + "create_channel": self._create_channel(), + "post_to_channel": self._create_post_to_channel(), + + # Feed Retrieval + "get_trending_casts": self._create_get_trending_casts(), + "get_user_casts": self._create_get_user_casts(), + "get_cast_reactions": self._create_get_cast_reactions(), + + # Search Functions + "search_casts": self._create_search_casts(), + "search_users": self._create_search_users(), + } + + @property + def available_functions(self) -> List[str]: + """Get list of available function names.""" + return list(self._functions.keys()) + + def get_function(self, fn_name: str) -> Function: + """Get a specific function by name.""" + if fn_name not in self._functions: + raise ValueError(f"Function '{fn_name}' not found. Available functions: {', '.join(self.available_functions)}") + return self._functions[fn_name] + + def _create_post_cast(self) -> Function: + return Function( + fn_name="post_cast", + fn_description="Create a new cast (post) on Farcaster. Use this to share thoughts, insights, or start new discussions.", + args=[ + FunctionArgument( + name="text", + description="The content of your cast. Should be engaging and contextual. Max 320 characters.", + type="string" + ), + FunctionArgument( + name="embed_url", + description="Optional URL to embed in the cast (e.g., link to an article, image, or video)", + type="string", + required=False + ) + ], + config=FunctionConfig( + method="post", + url=f"{self.base_url}/farcaster/cast", + platform="farcaster", + headers=self.base_headers, + payload={ + "signer_uuid": self.signer_uuid, + "text": "{{text}}", + "embeds": [{"url": "{{embed_url}}"}] if "{{embed_url}}" else [] + }, + success_feedback="Cast posted successfully. Preview: '{{response.cast.text}}' {{#response.cast.embeds.[0]}}with embedded content from {{response.cast.embeds.[0].url}}{{/response.cast.embeds.[0]}}" + ) + ) + + def _create_reply_to_cast(self) -> Function: + return Function( + fn_name="reply_to_cast", + fn_description="Reply to an existing cast. Use this to engage in conversations or provide feedback to others.", + args=[ + FunctionArgument( + name="text", + description="Your reply message. Should be relevant to the conversation. Max 320 characters.", + type="string" + ), + FunctionArgument( + name="cast_hash", + description="The hash of the cast you're replying to", + type="string" + ) + ], + config=FunctionConfig( + method="post", + url=f"{self.base_url}/farcaster/cast", + platform="farcaster", + headers=self.base_headers, + payload={ + "signer_uuid": self.signer_uuid, + "text": "{{text}}", + "parent": "{{cast_hash}}" + }, + success_feedback="Reply posted successfully. Your reply: '{{response.cast.text}}' to cast by {{response.cast.parent_author.username}}" + ) + ) + + def _create_recast(self) -> Function: + return Function( + fn_name="recast", + fn_description="Share another user's cast with your followers. Use this to amplify valuable content.", + args=[ + FunctionArgument( + name="cast_hash", + description="Hash of the cast you want to share with your followers", + type="string" + ) + ], + config=FunctionConfig( + method="post", + url=f"{self.base_url}/farcaster/recast", + platform="farcaster", + headers=self.base_headers, + payload={ + "signer_uuid": self.signer_uuid, + "target_hash": "{{cast_hash}}" + }, + success_feedback="Successfully shared cast by {{response.cast.author.username}}. Original cast: '{{response.cast.text}}'" + ) + ) + + def _create_like_cast(self) -> Function: + return Function( + fn_name="like_cast", + fn_description="Like a cast to show appreciation or agreement.", + args=[ + FunctionArgument( + name="cast_hash", + description="Hash of the cast you want to like", + type="string" + ) + ], + config=FunctionConfig( + method="post", + url=f"{self.base_url}/farcaster/reaction", + platform="farcaster", + headers=self.base_headers, + payload={ + "signer_uuid": self.signer_uuid, + "target_hash": "{{cast_hash}}", + "reaction_type": "like" + }, + success_feedback="Liked cast by {{response.cast.author.username}}. Cast text: '{{response.cast.text}}'" + ) + ) + + def _create_unlike_cast(self) -> Function: + return Function( + fn_name="unlike_cast", + fn_description="Remove your like from a cast.", + args=[ + FunctionArgument( + name="cast_hash", + description="Hash of the cast to unlike", + type="string" + ) + ], + config=FunctionConfig( + method="delete", + url=f"{self.base_url}/farcaster/reaction", + platform="farcaster", + headers=self.base_headers, + payload={ + "signer_uuid": self.signer_uuid, + "target_hash": "{{cast_hash}}", + "reaction_type": "like" + }, + success_feedback="Removed like from cast by {{response.cast.author.username}}" + ) + ) + + def _create_channel(self) -> Function: + return Function( + fn_name="create_channel", + fn_description="Create a new channel on Farcaster. Use this to start a focused discussion space.", + args=[ + FunctionArgument( + name="name", + description="Name of the channel (without leading 'fc:')", + type="string" + ), + FunctionArgument( + name="description", + description="Short description of what the channel is about", + type="string" + ) + ], + config=FunctionConfig( + method="post", + url=f"{self.base_url}/farcaster/channel", + platform="farcaster", + headers=self.base_headers, + payload={ + "name": "{{name}}", + "description": "{{description}}" + }, + success_feedback="Channel 'fc:{{response.channel.name}}' created successfully. Description: {{response.channel.description}}" + ) + ) + + def _create_post_to_channel(self) -> Function: + return Function( + fn_name="post_to_channel", + fn_description="Post a cast to a specific channel. Use this to participate in topic-specific discussions.", + args=[ + FunctionArgument( + name="text", + description="The content of your cast. Should be relevant to the channel topic. Max 320 characters.", + type="string" + ), + FunctionArgument( + name="channel_name", + description="Name of the channel to post to (without leading 'fc:')", + type="string" + ) + ], + config=FunctionConfig( + method="post", + url=f"{self.base_url}/farcaster/cast", + platform="farcaster", + headers=self.base_headers, + payload={ + "signer_uuid": self.signer_uuid, + "text": "{{text}}", + "channel": "{{channel_name}}" + }, + success_feedback="Posted to channel fc:{{response.cast.channel}}: '{{response.cast.text}}'" + ) + ) + + def _create_get_trending_casts(self) -> Function: + return Function( + fn_name="get_trending_casts", + fn_description="Get currently trending casts on Farcaster. Use this to understand current discussions and hot topics.", + args=[ + FunctionArgument( + name="time_window", + description="Time window for trending casts: '1h', '6h', '24h', or '7d'", + type="string", + required=False + ) + ], + config=FunctionConfig( + method="get", + url=f"{self.base_url}/farcaster/feed/trending", + platform="farcaster", + headers=self.base_headers, + query_params={ + "time_window": "{{time_window}}" + }, + success_feedback="Found {{response.casts.length}} trending casts. Top 3 trending: 1) '{{response.casts.[0].text}}' by {{response.casts.[0].author.username}} ({{response.casts.[0].reactions.likes}} likes), 2) '{{response.casts.[1].text}}' ({{response.casts.[1].reactions.likes}} likes), 3) '{{response.casts.[2].text}}' ({{response.casts.[2].reactions.likes}} likes)" + ) + ) + + def _create_get_cast_reactions(self) -> Function: + return Function( + fn_name="get_cast_reactions", + fn_description="Get reactions (likes, recasts) for a specific cast. Use this to gauge a cast's impact.", + args=[ + FunctionArgument( + name="cast_hash", + description="Hash of the cast to get reactions for", + type="string" + ) + ], + config=FunctionConfig( + method="get", + url=f"{self.base_url}/farcaster/cast/{{cast_hash}}/reactions", + platform="farcaster", + headers=self.base_headers, + success_feedback="Cast has {{response.reactions.likes}} likes and {{response.reactions.recasts}} recasts. Top engaging users: {{response.reactions.top_likers.[0].username}}, {{response.reactions.top_likers.[1].username}}, {{response.reactions.top_likers.[2].username}}" + ) + ) + + def _create_get_user_casts(self) -> Function: + return Function( + fn_name="get_user_casts", + fn_description="Get recent casts from a specific user. Use this to understand a user's activity and interests.", + args=[ + FunctionArgument( + name="fid", + description="Farcaster ID of the user", + type="integer" + ) + ], + config=FunctionConfig( + method="get", + url=f"{self.base_url}/farcaster/user/casts", + platform="farcaster", + headers=self.base_headers, + query_params={ + "fid": "{{fid}}" + }, + success_feedback="Retrieved {{response.casts.length}} casts by {{response.casts.[0].author.username}}. Latest: '{{response.casts.[0].text}}' ({{response.casts.[0].reactions.likes}} likes). Most engaged: '{{response.most_liked_cast.text}}' ({{response.most_liked_cast.reactions.likes}} likes)" + ) + ) + + def _create_search_casts(self) -> Function: + return Function( + fn_name="search_casts", + fn_description="Search for casts containing specific text or topics.", + args=[ + FunctionArgument( + name="query", + description="Text to search for in casts", + type="string" + ), + FunctionArgument( + name="channel_name", + description="Optional: Filter search to a specific channel", + type="string", + required=False + ) + ], + config=FunctionConfig( + method="get", + url=f"{self.base_url}/farcaster/cast/search", + platform="farcaster", + headers=self.base_headers, + query_params={ + "q": "{{query}}", + "channel": "{{channel_name}}" + }, + success_feedback="Found {{response.casts.length}} matching casts. Most relevant: 1) '{{response.casts.[0].text}}' by {{response.casts.[0].author.username}} in channel {{response.casts.[0].channel}} ({{response.casts.[0].reactions.likes}} likes), 2) '{{response.casts.[1].text}}' ({{response.casts.[1].reactions.likes}} likes)" + ) + ) + + def _create_search_users(self) -> Function: + return Function( + fn_name="search_users", + fn_description="Search for Farcaster users by username or display name.", + args=[ + FunctionArgument( + name="query", + description="Text to search for in usernames or display names", + type="string" + ) + ], + config=FunctionConfig( + method="get", + url=f"{self.base_url}/farcaster/user/search", + platform="farcaster", + headers=self.base_headers, + query_params={ + "q": "{{query}}" + }, + success_feedback="Found {{response.users.length}} users. Top matches: {{response.users.[0].username}} ({{response.users.[0].display_name}}), {{response.users.[1].username}} ({{response.users.[1].display_name}})", + error_feedback="Failed to search users: {{response.message}}" + ) + ) + + def _create_get_user_casts(self) -> Function: + return Function( + fn_name="get_user_casts", + fn_description="Get recent casts from a specific user. Use this to understand a user's activity and interests.", + args=[ + FunctionArgument( + name="fid", + description="Farcaster ID of the user", + type="integer" + ) + ], + config=FunctionConfig( + method="get", + url=f"{self.base_url}/farcaster/user/casts", + platform="farcaster", + headers=self.base_headers, + query_params={ + "fid": "{{fid}}" + }, + success_feedback="Retrieved {{response.casts.length}} recent casts. Latest cast: '{{response.casts.[0].text}}' with {{response.casts.[0].reactions.likes}} likes. Most liked cast: '{{response.most_liked_cast.text}}' with {{response.most_liked_cast.reactions.likes}} likes", + error_feedback="Failed to get user's casts: {{response.message}}" + ) + ) + + def _create_get_cast_reactions(self) -> Function: + return Function( + fn_name="get_cast_reactions", + fn_description="Get reactions (likes, recasts) for a specific cast. Use this to gauge a cast's impact.", + args=[ + FunctionArgument( + name="cast_hash", + description="Hash of the cast to get reactions for", + type="string" + ) + ], + config=FunctionConfig( + method="get", + url=f"{self.base_url}/farcaster/cast/{{cast_hash}}/reactions", + platform="farcaster", + headers=self.base_headers, + success_feedback="Cast has {{response.reactions.likes}} likes and {{response.reactions.recasts}} recasts. Most engaged users: {{response.reactions.top_likers.[0].username}}, {{response.reactions.top_likers.[1].username}}", + error_feedback="Failed to get cast reactions: {{response.message}}" + ) + ) \ No newline at end of file diff --git a/src/virtuals_sdk/functions/telegram.py b/src/virtuals_sdk/twitter_agent/functions/telegram.py similarity index 99% rename from src/virtuals_sdk/functions/telegram.py rename to src/virtuals_sdk/twitter_agent/functions/telegram.py index 56f0ab3..54e03ec 100644 --- a/src/virtuals_sdk/functions/telegram.py +++ b/src/virtuals_sdk/twitter_agent/functions/telegram.py @@ -1,5 +1,5 @@ from typing import Dict, List -from virtuals_sdk.game import Function, FunctionConfig, FunctionArgument +from virtuals_sdk.twitter_agent.game import Function, FunctionConfig, FunctionArgument class TelegramClient: """ diff --git a/src/virtuals_sdk/sdk.py b/src/virtuals_sdk/twitter_agent/sdk.py similarity index 100% rename from src/virtuals_sdk/sdk.py rename to src/virtuals_sdk/twitter_agent/sdk.py From 1cc324eb5b9a9641b7bc3791155063a43cd20ab3 Mon Sep 17 00:00:00 2001 From: Celeste Ang Date: Wed, 8 Jan 2025 18:45:47 +0800 Subject: [PATCH 04/10] readme with diagrams --- README.md | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 14d0199..ce4b41d 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,47 @@ # Virtuals Python SDK Library The Virtuals Python SDK is a library that allows you interact with the Virtuals Platform. -Currently, this SDK/API allows you to develop your agents powered by the GAME architecture. There are two main functionalities for this SDK: +## About G.A.M.E. +GAME is a modular agentic framework which enables an agent to plan actions and make decisions autonomously based on information provided to it. -1. [Twitter Agent](./src/virtuals_sdk/twitter_agent/README.md): The Virtuals Platform offers a out-of-the-box hosted agent that can be used to interact with the Twitter/X platform, powered by GAME. This agent comes with existing functions/actions that can be used to interact with the Twitter/X platform and can be immediately hosted/deployed as you configure it. This is similar to configuring your agent in the [Agent Sandbox](https://game-lite.virtuals.io/) on the [Virtuals Platform](https://app.virtuals.io/) but through a developer-friendly SDK interface. +Please refer to our [whitepaper](https://whitepaper.virtuals.io/developer-documents/game-framework) for more information and resources. +## About Virtuals Python SDK +Currently, this SDK allows you to develop your agents powered by the GAME architecture in its most fullest and most flexible form. -2. [Custom GAME Agent](./src/virtuals_sdk/game/README.md): This SDK also offers a Python SDK to use the GAME architecture in its most fullest and most flexible form. It allows you to develop your own custom agents for any application or platform. The modularity and flexibilty of this SDK allows you to have full control of what the agent sees (state) and can do (actions/functions). Custom functions do not have to be wrapper in API services, but can be defined as just an executable function in Python. -This same flexibility is also provided as a [TypeScript SDK](https://www.npmjs.com/package/@virtuals-protocol/game) if you prefer to develop your agents in TypeScript. +## Features +![New SDK visual](docs/imgs/new_sdk_visual.png) +- Develop your own custom agents for any application or platform. +- Ability to control your agents and workers via descriptions (prompts) +- Full control of what the agent sees (state) and can do (actions/functions) +- Ability to fully customise functions. This could include various combinations of programmed logic. For example: + - Calling an API to retrieve data + - Calling an API to retrieve data, followed by custom calculations or data processing logic in python code + - 2 API calls chained together (e.g. calling an API to retrieve web data, and then posting a tweet) -## Documentation -Detailed documentation to better understand the configurable components and the GAME architecture can be found on [here](https://whitepaper.virtuals.io/developer-documents/game-framework). +> ### ℹ️ Changes from older python SDK version (prior to 8 Jan 2025) +>![Old SDK visual](docs/imgs/old_sdk_visual.png) +> - Ability to fully customise functions (previously, each function was a single API call) +> - Ability to control the low-level planner via description prompt (previously, only the high-level planner and functions could be controlled via description prompts) +> - The description defined in the agent is equivalent to what was previously known as world information and agent description ## Installation ```bash pip install virtuals_sdk ``` +## Usage +Please refer to `test_agent.py` and `test_worker.py` for usage examples. + +## How to Contribute +Contributions are welcome, especially in the form of new plugins! To contribute: +1. Create a new branch +2. Make your changes and commit them with a descriptive message. +3. Open a pull request for the Virtuals Protocol team to review. + +## Documentation +Detailed documentation to better understand the configurable components and the GAME architecture can be found on [here](https://whitepaper.virtuals.io/developer-documents/game-framework). + +## Useful Resources +- [TypeScript SDK](https://www.npmjs.com/package/@virtuals-protocol/game): The core logic of this SDK mirrors the logic of this python SDK if you prefer to develop your agents in TypeScript. +- [Twitter Agent](./src/virtuals_sdk/twitter_agent/README.md): The Virtuals Platform offers a out-of-the-box hosted agent that can be used to interact with the Twitter/X platform, powered by GAME. This agent comes with existing functions/actions that can be used to interact with the Twitter/X platform and can be immediately hosted/deployed as you configure it. This is similar to configuring your agent in the [Agent Sandbox](https://game-lite.virtuals.io/) on the [Virtuals Platform](https://app.virtuals.io/) but through a developer-friendly SDK interface. \ No newline at end of file From c8f8396753215235698e823609573280d3f59c89 Mon Sep 17 00:00:00 2001 From: Celeste Ang Date: Wed, 8 Jan 2025 18:46:42 +0800 Subject: [PATCH 05/10] add readme images --- docs/imgs/new_sdk_visual.png | Bin 0 -> 142589 bytes docs/imgs/old_sdk_visual.png | Bin 0 -> 123050 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/imgs/new_sdk_visual.png create mode 100644 docs/imgs/old_sdk_visual.png diff --git a/docs/imgs/new_sdk_visual.png b/docs/imgs/new_sdk_visual.png new file mode 100644 index 0000000000000000000000000000000000000000..218366c065107eb4a3a361b02dcda13786ee0acf GIT binary patch literal 142589 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 literal 0 HcmV?d00001 diff --git a/docs/imgs/old_sdk_visual.png b/docs/imgs/old_sdk_visual.png new file mode 100644 index 0000000000000000000000000000000000000000..9387e966e98fe373273b6db27b7c5f9a6e5ea23e GIT binary patch literal 123050 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

8M-|69f{27Gcs;i@~aqAy=eG+cSABS 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 Date: Wed, 8 Jan 2025 18:53:05 +0800 Subject: [PATCH 06/10] add relative links to file names in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ce4b41d..7d59d8e 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ pip install virtuals_sdk ``` ## Usage -Please refer to `test_agent.py` and `test_worker.py` for usage examples. +Please refer to [`test_agent.py`](examples/game/test_agent.py) and [`test_worker.py`](examples/game/test_agent.py) for usage examples. ## How to Contribute Contributions are welcome, especially in the form of new plugins! To contribute: From 3c6b3741f8c81cd7b27901c4398afd891894861b Mon Sep 17 00:00:00 2001 From: Celeste Ang Date: Wed, 8 Jan 2025 20:04:27 +0800 Subject: [PATCH 07/10] Add agent and worker descriptions to README --- README.md | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7d59d8e..18503e0 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,26 @@ Please refer to our [whitepaper](https://whitepaper.virtuals.io/developer-docume ## About Virtuals Python SDK Currently, this SDK allows you to develop your agents powered by the GAME architecture in its most fullest and most flexible form. -## Features ![New SDK visual](docs/imgs/new_sdk_visual.png) +The python SDK is made up of 3 main components (Agent, Worker, function), each with configurable arguments. + +Agent (a.k.a. [high level planner](https://whitepaper.virtuals.io/developer-documents/game-framework/game-overview#high-level-planner-hlp-context)) +- Takes in a Goal + - Drives the agents behaviour through the high level plan which influences the thinking and creation of tasks that would contribute towards this goal +- Takes in a Description + - Combination of what was previously known as World Info + Agent Description + - This include a description of the "world" the agent lives in, and the personality and background of the agent + +Worker (a.k.a. [low-level planner](https://whitepaper.virtuals.io/developer-documents/game-framework/game-overview#low-level-planner-llp-context)) +- Takes in a Description + - Used to control which workers are called by the agent, based on the high-level plan and tasks created to contribute to the goal + +Function +- Takes in a Description + - Used to control which functions are called by the workers, based on each worker's low-level plan + - This can be any python executable + +## Features - Develop your own custom agents for any application or platform. - Ability to control your agents and workers via descriptions (prompts) - Full control of what the agent sees (state) and can do (actions/functions) @@ -34,10 +52,7 @@ pip install virtuals_sdk Please refer to [`test_agent.py`](examples/game/test_agent.py) and [`test_worker.py`](examples/game/test_agent.py) for usage examples. ## How to Contribute -Contributions are welcome, especially in the form of new plugins! To contribute: -1. Create a new branch -2. Make your changes and commit them with a descriptive message. -3. Open a pull request for the Virtuals Protocol team to review. +Contributions are welcome, especially in the form of new plugins! We are working on creating a plugins repo, but in meantime - please contact us via [Twitter](https://x.com/GAME_Virtuals) or [Telegram](https://t.me/virtuaIs_io). ## Documentation Detailed documentation to better understand the configurable components and the GAME architecture can be found on [here](https://whitepaper.virtuals.io/developer-documents/game-framework). From 8a992bb784383e2302836db1f88bbad0730f2536 Mon Sep 17 00:00:00 2001 From: ai-virtual-b Date: Wed, 8 Jan 2025 23:21:02 +0800 Subject: [PATCH 08/10] update version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4f6274e..a055e62 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "virtuals-sdk" -version = "0.1.5" +version = "0.1.6" authors = [ { name = "Your Name", email = "your.email@example.com" }, ] From 466b7114dafcb46a8f7c7d2994bea56a6f210b90 Mon Sep 17 00:00:00 2001 From: ai-virtual-b Date: Wed, 8 Jan 2025 23:28:25 +0800 Subject: [PATCH 09/10] fix paths --- src/virtuals_sdk/game/agent.py | 7 +++---- src/virtuals_sdk/game/worker.py | 5 ++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/virtuals_sdk/game/agent.py b/src/virtuals_sdk/game/agent.py index 10fe900..afe0efe 100644 --- a/src/virtuals_sdk/game/agent.py +++ b/src/virtuals_sdk/game/agent.py @@ -1,9 +1,8 @@ from typing import List, Optional, Callable, Dict import uuid -from utils import post -from worker import Worker -from custom_types import Function, FunctionResult, FunctionResultStatus, ActionResponse, ActionType -from utils import create_agent, create_workers +from virtuals_sdk.game.worker import Worker +from virtuals_sdk.game.custom_types import Function, FunctionResult, FunctionResultStatus, ActionResponse, ActionType +from virtuals_sdk.game.utils import create_agent, create_workers, post class Session: diff --git a/src/virtuals_sdk/game/worker.py b/src/virtuals_sdk/game/worker.py index e4c74aa..5343181 100644 --- a/src/virtuals_sdk/game/worker.py +++ b/src/virtuals_sdk/game/worker.py @@ -1,7 +1,6 @@ from typing import Any, Callable, Dict, Optional, List -from custom_types import Function, FunctionResult, FunctionResultStatus, ActionResponse, ActionType -from utils import create_agent, create_workers -from utils import post +from virtuals_sdk.game.custom_types import Function, FunctionResult, FunctionResultStatus, ActionResponse, ActionType +from virtuals_sdk.game.utils import create_agent, post class Worker: From e8517d775b64a632226f49889eff933a2ee9b295 Mon Sep 17 00:00:00 2001 From: ai-virtual-b Date: Mon, 13 Jan 2025 07:47:29 +0000 Subject: [PATCH 10/10] Update README.md about migration of this repo --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 18503e0..346197c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# Virtuals Python SDK Library +# Virtuals Python SDK Library [WARNING: MIGRATING - WILL BE DEPRECATED] +THIS REPOSITORY WILL NO LONGER BE MAINTAINED AND HAS BEEN MIGRATED OVER TO https://github.com/game-by-virtuals/game-python/tree/main. Please move over to use latest SDK and features. + The Virtuals Python SDK is a library that allows you interact with the Virtuals Platform. ## About G.A.M.E. @@ -59,4 +61,4 @@ Detailed documentation to better understand the configurable components and the ## Useful Resources - [TypeScript SDK](https://www.npmjs.com/package/@virtuals-protocol/game): The core logic of this SDK mirrors the logic of this python SDK if you prefer to develop your agents in TypeScript. -- [Twitter Agent](./src/virtuals_sdk/twitter_agent/README.md): The Virtuals Platform offers a out-of-the-box hosted agent that can be used to interact with the Twitter/X platform, powered by GAME. This agent comes with existing functions/actions that can be used to interact with the Twitter/X platform and can be immediately hosted/deployed as you configure it. This is similar to configuring your agent in the [Agent Sandbox](https://game-lite.virtuals.io/) on the [Virtuals Platform](https://app.virtuals.io/) but through a developer-friendly SDK interface. \ No newline at end of file +- [Twitter Agent](./src/virtuals_sdk/twitter_agent/README.md): The Virtuals Platform offers a out-of-the-box hosted agent that can be used to interact with the Twitter/X platform, powered by GAME. This agent comes with existing functions/actions that can be used to interact with the Twitter/X platform and can be immediately hosted/deployed as you configure it. This is similar to configuring your agent in the [Agent Sandbox](https://game-lite.virtuals.io/) on the [Virtuals Platform](https://app.virtuals.io/) but through a developer-friendly SDK interface.