From 7c80fb16f94077db8ca417b66a6584d6ac774848 Mon Sep 17 00:00:00 2001 From: StevenSF1998 Date: Fri, 27 Dec 2024 10:37:42 +0800 Subject: [PATCH 1/6] 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 2/6] 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 b93e389210799b0c2c48714819836e997a567677 Mon Sep 17 00:00:00 2001 From: StevenSF1998 Date: Mon, 6 Jan 2025 14:58:15 +0800 Subject: [PATCH 3/6] Allow tweetUsernames params and templates to be updated --- examples/example-twitter.py | 93 ++++++++++++++++++++++++++- src/virtuals_sdk/game.py | 124 +++++++++++++++++++++++++++++++++++- src/virtuals_sdk/sdk.py | 38 +++++++---- 3 files changed, 239 insertions(+), 16 deletions(-) diff --git a/examples/example-twitter.py b/examples/example-twitter.py index 750ed98..1c1aca7 100644 --- a/examples/example-twitter.py +++ b/examples/example-twitter.py @@ -1,6 +1,13 @@ import os + +#For local development +""" import sys +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from src.virtuals_sdk import game """ + from virtuals_sdk import game + agent = game.Agent( api_key=os.environ.get("VIRTUALS_API_KEY"), goal="search for best songs", @@ -8,6 +15,8 @@ world_info="Test World Info" ) + + # applicable only for platform twitter agent.list_available_default_twitter_functions() agent.use_default_twitter_functions(["wait", "reply_tweet"]) @@ -35,12 +44,94 @@ ) ) +agent.add_template( + game.Template( + template_type="TWITTER_START_SYSTEM_PROMPT", + system_prompt="You are a twitter post generator. You can write a variety of tweets. Your tweet style should follow the character described below. ", + sys_prompt_response_format=[] + ) +) + +agent.add_template( + game.Template( + template_type="TWITTER_END_SYSTEM_PROMPT", + system_prompt="Rule: - Do not host Twitter space, do not use hashtag. - Do not give any contract address", + sys_prompt_response_format=[] + ) +) + +agent.add_template( + game.Template( + template_type="SHARED", + system_prompt="""{{twitterPublicStartSysPrompt}} + +You are roleplaying as {{agentName}}. Do not break out of character. + +Character description: +{{description}} + +Character goal: +{{twitterGoal}} + +These are the world info that might be useful as additional context for your response. You do not need to use any of the information describe in this section if you don't need it. +{{worldInfo}} + +{{retrieveKnowledge}} + +This your post history, you should evaluate if it is repetitive or aligned with your goal. Post history is sorted by oldest to newest. Be creative. +{{postHistory}} + +{{twitterPublicEndSysPrompt}} + +Prepare your thought process first and then only curate the response. You must reply in this format. You only need to have one chain of thought and 5 answers.""", + sys_prompt_response_format=[10,20,30,50,100] + ) +) + +agent.add_template( + game.Template( + template_type="POST", + user_prompt="{{agentName}}'s suggested tweet content: {{task}}. {{agentName}}'s reasoning: {{taskReasoning}}. Build a new tweet with the suggested tweet content. Do not hold twitter space. Do not use hashtag.", + sys_prompt_response_format=[10,20,30,50], + temperature=0.5, + top_k=50, + top_p=0.7, + repetition_penalty=1.0, + model="meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo", + type="POST", + isSandbox=False + ) +) + +agent.add_template( + game.Template( + template_type="REPLY", + user_prompt="""{{agentName}}'s suggested tweet content: {{task}}. {{agentName}}'s reasoning: {{taskReasoning}} + +You will be given the author information and your impression on the author. You should formulate your response based on the suggested tweet content accordingly. {{author}}'s bio: {{bio}} + +This is the ongoing conversation history: {{conversationHistory}}. +""", + sys_prompt_response_format=[10,20,30,50], + temperature=0.5, + top_k=50, + top_p=0.7, + repetition_penalty=1.0, + model="meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo", + type="REPLY" + ) +) + +# set empty array to remove the default usernames +agent.set_tweet_usernames([]) +agent.deploy_twitter() + # running reaction module only for platform twitter agent.react( session_id="session-twitter", platform="twitter", tweet_id="1869281466628349975", -) +) # running simulation module only for platform twitter agent.simulate_twitter(session_id="session-twitter") diff --git a/src/virtuals_sdk/game.py b/src/virtuals_sdk/game.py index 5a565ae..43bc647 100644 --- a/src/virtuals_sdk/game.py +++ b/src/virtuals_sdk/game.py @@ -154,6 +154,104 @@ def __call__(self, *args): raise requests.exceptions.HTTPError(f"Request failed: {error_msg}") +@dataclass +class Template: + template_type: str + system_prompt: str = None + sys_prompt_response_format: List[int] = None + model: str = "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo" + temperature: float = 1.0 + user_prompt: str = None + top_k: float = 50.0 + top_p: float = 0.7 + repetition_penalty: float = 1.0 + type: str = None + taskDescription: str = None + isSandbox: bool = False + + def _validate_fields(self): + """Validate all template fields and their values""" + # Validate required fields + if not self.template_type: + raise ValueError("template_type is required") + + if self.template_type not in ["POST", "REPLY", "SHARED", "TWITTER_START_SYSTEM_PROMPT", "TWITTER_END_SYSTEM_PROMPT"]: + raise ValueError(f"{self.template_type} is invalid, valid types are POST, REPLY, SHARED, TWITTER_START_SYSTEM_PROMPT, TWITTER_END_SYSTEM_PROMPT") + + # Set default values based on template_type + if self.template_type in ["POST", "REPLY"]: + if not self.user_prompt: + raise ValueError("user_prompt is required") + # Common settings for POST and REPLY + self.sys_prompt_response_format = self.sys_prompt_response_format or [10, 20, 40, 60, 80] + self.temperature = self.temperature or 1.0 + self.top_k = self.top_k or 50.0 + self.top_p = self.top_p or 0.7 + self.repetition_penalty = self.repetition_penalty or 1.0 + self.model = self.model or "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo" + self.type = self.template_type + self.isSandbox = False + self.userPrompt = self.user_prompt or "" + + # Additional settings for REPLY only + if self.template_type == "REPLY": + self.taskDescription = self.taskDescription or "Process incoming tweet. Ignore if it is boring or unimportant. Ignore if the conversation has gone too long." + + elif self.template_type in ["TWITTER_START_SYSTEM_PROMPT", "TWITTER_END_SYSTEM_PROMPT", "SHARED"]: + if not self.system_prompt: + raise ValueError("system_prompt is required") + self.sys_prompt_response_format = [] + + # Validate sys_prompt_response_format type and values + if self.sys_prompt_response_format is not None: + if not isinstance(self.sys_prompt_response_format, list): + raise TypeError("sys_prompt_response_format must be a list of integers") + for num in self.sys_prompt_response_format: + if not isinstance(num, int) or num < 10 or num > 80: + raise ValueError("sys_prompt_response_format values must be integers between 10 and 80") + + # Validate numeric ranges + if not 0.1 <= self.temperature <= 2.0: + raise ValueError("temperature must be between 0.0 and 2.0") + if not 0.1 <= self.top_p <= 1.0: + raise ValueError("top_p must be between 0.1 and 1.0") + if not 1 <= self.top_k <= 100: + raise ValueError("top_k must be between 1 and 100") + if not 0.1 <= self.repetition_penalty <= 2.0: + raise ValueError("repetition_penalty must be greater than or equal to 1.0") + + def __post_init__(self): + self._validate_fields() + + # Convert numeric values to proper type + self.temperature = float(self.temperature) + self.top_k = float(self.top_k) + self.top_p = float(self.top_p) + self.repetition_penalty = float(self.repetition_penalty) + + def to_dict(self) -> dict: + """Convert template to dictionary format for API submission""" + if self.template_type in ["SHARED", "TWITTER_START_SYSTEM_PROMPT", "TWITTER_END_SYSTEM_PROMPT"]: + return { + "templateType": self.template_type, + "systemPrompt": self.system_prompt, + "sysPromptResponseFormat": self.sys_prompt_response_format + } + else: + return { + "templateType": self.template_type, + "sysPromptResponseFormat": self.sys_prompt_response_format, + "model": self.model, + "temperature": self.temperature, + "userPrompt": self.user_prompt, + "topK": self.top_k, + "topP": self.top_p, + "repetitionPenalty": self.repetition_penalty, + "type": self.type, + "isSandbox": self.isSandbox + } + + class Agent: def __init__( self, @@ -172,6 +270,8 @@ def __init__( self.custom_functions: List[Function] = [] self.main_heartbeat = main_heartbeat self.reaction_heartbeat = reaction_heartbeat + self.templates: List[Template] = [] + self.tweet_usernames: List[str] = [] def set_goal(self, goal: str): self.goal = goal @@ -192,6 +292,14 @@ def set_main_heartbeat(self, main_heartbeat: int): def set_reaction_heartbeat(self, reaction_heartbeat: int): self.reaction_heartbeat = reaction_heartbeat return True + + def set_tweet_usernames(self, usernames: List[str]) -> bool: + # Check username limit + if len(usernames) > 20: + raise ValueError("Maximum number of usernames allowed is 20") + + self.tweet_usernames = usernames + return True def get_goal(self) -> str: return self.goal @@ -201,6 +309,9 @@ def get_description(self) -> str: def get_world_info(self) -> str: return self.world_info + + def get_tweet_usernames(self) -> List[str]: + return self.tweet_usernames def list_available_default_twitter_functions(self) -> Dict[str, str]: """ @@ -226,6 +337,11 @@ def add_custom_function(self, custom_function: Function) -> bool: self.custom_functions.append(custom_function) return True + + def add_template(self, template: Template) -> bool: + """Add a template to the agent""" + self.templates.append(template) + return True def simulate_twitter(self, session_id: str): """ @@ -268,7 +384,9 @@ def deploy_twitter(self): self.enabled_functions, self.custom_functions, self.main_heartbeat, - self.reaction_heartbeat + self.reaction_heartbeat, + self.templates, + self.tweet_usernames ) def export(self) -> str: @@ -288,7 +406,9 @@ def export(self) -> str: "config": asdict(func.config) } for func in self.custom_functions - ] + ], + "templates": [asdict(template) for template in self.templates], + "tweetUsernames": self.tweet_usernames } agent_json = json.dumps(export_dict, indent=4) diff --git a/src/virtuals_sdk/sdk.py b/src/virtuals_sdk/sdk.py index 64aa21f..6672427 100644 --- a/src/virtuals_sdk/sdk.py +++ b/src/virtuals_sdk/sdk.py @@ -90,24 +90,34 @@ 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, main_heartbeat: int, reaction_heartbeat: int): + def deploy(self, goal: str, description: str, world_info: str, functions: list, custom_functions: list, main_heartbeat: int, reaction_heartbeat: int, templates: list = None, tweet_usernames: list = None): """ - Simulate the agent configuration + Deploy the agent configuration """ + payload = { + "goal": goal, + "description": description, + "worldInfo": world_info, + "functions": functions, + "customFunctions": [x.toJson() for x in custom_functions], + "gameState": { + "mainHeartbeat": main_heartbeat, + "reactionHeartbeat": reaction_heartbeat, + } + } + + if tweet_usernames is not None: + payload["tweetUsernames"] = tweet_usernames + + # Add templates to payload if provided + if templates: + payload["templates"] = [template.to_dict() for template in templates] + + response = requests.post( f"{self.api_url}/deploy", json={ - "data": { - "goal": goal, - "description": description, - "worldInfo": world_info, - "functions": functions, - "customFunctions": custom_functions, - "gameState" : { - "mainHeartbeat" : main_heartbeat, - "reactionHeartbeat" : reaction_heartbeat, - } - } + "data": payload }, headers={"x-api-key": self.api_key} ) @@ -116,3 +126,5 @@ def deploy(self, goal: str, description: str, world_info: str, functions: list, raise Exception(response.json()) return response.json()["data"] + + From 885f51c718ea933dff96a2a508d764cf753756e7 Mon Sep 17 00:00:00 2001 From: StevenSF1998 Date: Mon, 6 Jan 2025 14:58:46 +0800 Subject: [PATCH 4/6] remove print --- src/virtuals_sdk/sdk.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/virtuals_sdk/sdk.py b/src/virtuals_sdk/sdk.py index 6672427..fe39588 100644 --- a/src/virtuals_sdk/sdk.py +++ b/src/virtuals_sdk/sdk.py @@ -75,7 +75,6 @@ def react(self, session_id: str, platform: str, goal: str, if (tweet_id): payload["tweetId"] = tweet_id - print(payload) response = requests.post( url, From abe3ac5dd1a904dc6ae2d12211e7e6f2ee8f8ab8 Mon Sep 17 00:00:00 2001 From: StevenSF1998 Date: Wed, 8 Jan 2025 23:01:22 +0800 Subject: [PATCH 5/6] refactor add share template to single function --- examples/example-twitter.py | 42 ++++++++----------------------------- src/virtuals_sdk/game.py | 26 +++++++++++++++++++++++ 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/examples/example-twitter.py b/examples/example-twitter.py index 1c1aca7..38ff682 100644 --- a/examples/example-twitter.py +++ b/examples/example-twitter.py @@ -1,11 +1,5 @@ import os - -#For local development -""" import sys -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from src.virtuals_sdk import game """ - -from virtuals_sdk import game +from virtuals_sdk import game agent = game.Agent( @@ -15,10 +9,8 @@ world_info="Test World Info" ) - - # applicable only for platform twitter -agent.list_available_default_twitter_functions() +#agent.list_available_default_twitter_functions() agent.use_default_twitter_functions(["wait", "reply_tweet"]) # adding custom functions only for platform twitter @@ -44,26 +36,10 @@ ) ) -agent.add_template( - game.Template( - template_type="TWITTER_START_SYSTEM_PROMPT", - system_prompt="You are a twitter post generator. You can write a variety of tweets. Your tweet style should follow the character described below. ", - sys_prompt_response_format=[] - ) -) - -agent.add_template( - game.Template( - template_type="TWITTER_END_SYSTEM_PROMPT", - system_prompt="Rule: - Do not host Twitter space, do not use hashtag. - Do not give any contract address", - sys_prompt_response_format=[] - ) -) - -agent.add_template( - game.Template( - template_type="SHARED", - system_prompt="""{{twitterPublicStartSysPrompt}} +# adding shared template for twitter +agent.add_share_template( + start_system_prompt="You are a twitter post generator. You can write a variety of tweets. Your tweet style should follow the character described below. ", + shared_prompt="""{{twitterPublicStartSysPrompt}} You are roleplaying as {{agentName}}. Do not break out of character. @@ -84,10 +60,10 @@ {{twitterPublicEndSysPrompt}} Prepare your thought process first and then only curate the response. You must reply in this format. You only need to have one chain of thought and 5 answers.""", - sys_prompt_response_format=[10,20,30,50,100] - ) + end_system_prompt="Rule: - Do not host Twitter space, do not use hashtag. - Do not give any contract address" ) +# adding template for twitter agent.add_template( game.Template( template_type="POST", @@ -131,7 +107,7 @@ session_id="session-twitter", platform="twitter", tweet_id="1869281466628349975", -) +) # running simulation module only for platform twitter agent.simulate_twitter(session_id="session-twitter") diff --git a/src/virtuals_sdk/game.py b/src/virtuals_sdk/game.py index 43bc647..afa891a 100644 --- a/src/virtuals_sdk/game.py +++ b/src/virtuals_sdk/game.py @@ -417,3 +417,29 @@ def export(self) -> str: f.write(agent_json) return agent_json + + def add_share_template( + self, + start_system_prompt: str, + shared_prompt: str, + end_system_prompt: str + ) -> bool: + self.add_template(Template( + template_type="TWITTER_START_SYSTEM_PROMPT", + system_prompt=start_system_prompt, + sys_prompt_response_format=[] + )) + + self.add_template(Template( + template_type="SHARED", + system_prompt=shared_prompt, + sys_prompt_response_format=[] + )) + + self.add_template(Template( + template_type="TWITTER_END_SYSTEM_PROMPT", + system_prompt=end_system_prompt, + sys_prompt_response_format=[] + )) + + return True From 6d4fe50fd0a1f86d498e5bbca12ac70533e1ce96 Mon Sep 17 00:00:00 2001 From: StevenSF1998 Date: Wed, 8 Jan 2025 23:14:32 +0800 Subject: [PATCH 6/6] Fix export template format is wrong and uncomment unit test code --- examples/example-twitter.py | 2 +- src/virtuals_sdk/game.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/example-twitter.py b/examples/example-twitter.py index 38ff682..285a3ab 100644 --- a/examples/example-twitter.py +++ b/examples/example-twitter.py @@ -10,7 +10,7 @@ ) # applicable only for platform twitter -#agent.list_available_default_twitter_functions() +agent.list_available_default_twitter_functions() agent.use_default_twitter_functions(["wait", "reply_tweet"]) # adding custom functions only for platform twitter diff --git a/src/virtuals_sdk/game.py b/src/virtuals_sdk/game.py index afa891a..6660b8b 100644 --- a/src/virtuals_sdk/game.py +++ b/src/virtuals_sdk/game.py @@ -407,7 +407,7 @@ def export(self) -> str: } for func in self.custom_functions ], - "templates": [asdict(template) for template in self.templates], + "templates": [template.to_dict() for template in self.templates], "tweetUsernames": self.tweet_usernames } agent_json = json.dumps(export_dict, indent=4)