Skip to content

Feat/allow x prompt and following for deploy #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 69 additions & 2 deletions examples/example-twitter.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
from virtuals_sdk import game
from virtuals_sdk import game


agent = game.Agent(
api_key=os.environ.get("VIRTUALS_API_KEY"),
Expand Down Expand Up @@ -35,12 +36,78 @@
)
)

# 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.

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.""",
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",
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")
164 changes: 162 additions & 2 deletions src/virtuals_sdk/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,20 +154,124 @@ 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,
api_key: str,
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
self.description = description
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
self.templates: List[Template] = []
self.tweet_usernames: List[str] = []

def set_goal(self, goal: str):
self.goal = goal
Expand All @@ -180,6 +284,22 @@ 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 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
Expand All @@ -189,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]:
"""
Expand All @@ -214,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):
"""
Expand Down Expand Up @@ -254,7 +382,11 @@ def deploy_twitter(self):
self.description,
self.world_info,
self.enabled_functions,
self.custom_functions
self.custom_functions,
self.main_heartbeat,
self.reaction_heartbeat,
self.templates,
self.tweet_usernames
)

def export(self) -> str:
Expand All @@ -274,7 +406,9 @@ def export(self) -> str:
"config": asdict(func.config)
}
for func in self.custom_functions
]
],
"templates": [template.to_dict() for template in self.templates],
"tweetUsernames": self.tweet_usernames
}
agent_json = json.dumps(export_dict, indent=4)

Expand All @@ -283,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
37 changes: 26 additions & 11 deletions src/virtuals_sdk/sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -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,
Expand All @@ -90,20 +89,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):
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
}
"data": payload
},
headers={"x-api-key": self.api_key}
)
Expand All @@ -112,3 +125,5 @@ def deploy(self, goal: str, description: str, world_info: str, functions: list,
raise Exception(response.json())

return response.json()["data"]