Skip to content

Commit 392263e

Browse files
committed
[joonspk-research#83, joonspk-research#84] Moved each InferenceStrategy out into its separate module
1 parent 3157f6b commit 392263e

File tree

8 files changed

+519
-417
lines changed

8 files changed

+519
-417
lines changed

reverie/backend_server/persona/cognitive_modules/plan.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@
1919
from persona.cognitive_modules.converse import *
2020
from persona.prompt_template.embedding import get_embedding
2121
from persona.common import HourlyScheduleItem, string_to_time
22+
from persona.prompts.run_gpt_prompt_wake_up_hour import run_gpt_prompt_wake_up_hour
23+
from persona.prompts.run_gpt_prompt_daily_plan import run_gpt_prompt_daily_plan
24+
from persona.prompts.run_gpt_prompt_task_decomp import run_gpt_prompt_task_decomp
25+
from persona.prompts.run_gpt_prompt_action_sector import run_gpt_prompt_action_sector
26+
from persona.prompts.run_gpt_prompt_act_obj_desc import run_gpt_prompt_act_obj_desc
27+
from persona.prompts.run_gpt_prompt_act_obj_event_triple import run_gpt_prompt_act_obj_event_triple
28+
2229
##############################################################################
2330
# CHAPTER 2: Generate
2431
##############################################################################

reverie/backend_server/persona/prompt_template/run_gpt_prompt.py

Lines changed: 0 additions & 417 deletions
Large diffs are not rendered by default.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from typing import Dict, Optional
2+
3+
from persona.prompt_template.InferenceStrategySK import JSONType, OutputType, functor, InferenceStrategySK
4+
5+
@functor
6+
class run_gpt_prompt_act_obj_desc(InferenceStrategySK):
7+
output_type = OutputType.JSON
8+
config = {
9+
"max_tokens": 50,
10+
"temperature": 0,
11+
"top_p": 1,
12+
}
13+
prompt = """
14+
We want to write an object description and to understand the state of an object that is being used by someone. For example, if Jack is fixing the generator, the description would state:
15+
16+
{"object":"generator","user":"Jack","state":"being fixed"}
17+
18+
Now, let's consider {{$object_name}}. {{$firstname}} is currently performing the task "{{$action_description}}", interacting with the {{$object_name}}. Describe the interaction in the same form as above.
19+
"""
20+
21+
def prepare_context(self, act_game_object: str, act_desp: str, persona) -> Dict[str, str]:
22+
return {
23+
"object_name": act_game_object,
24+
"action_description": act_desp,
25+
"firstname": persona.scratch.get_str_firstname(),
26+
}
27+
28+
def validate_json(self, json: JSONType) -> Optional[str]:
29+
# Check for the required fields in the JSON object
30+
required_fields = ["object", "user", "state"]
31+
for field in required_fields:
32+
if field not in json:
33+
return f"Missing field: {field}"
34+
# Check if the "object" field matches the lowercased object_name property
35+
if json["object"].lower() != self.context_variables['object_name'].lower():
36+
return "Object name mismatch"
37+
# Check if the "object" field matches the lowercased object_name property
38+
if json["user"] != self.context_variables['firstname']:
39+
return "Object name mismatch"
40+
41+
def extract_json(self, json: JSONType) -> str:
42+
return json['state']
43+
44+
def fallback(self, act_game_object: str, act_desp: str, persona) -> str:
45+
return f'being used by {persona.scratch.get_str_firstname()}'
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
from typing import Optional
2+
3+
from persona.prompt_template.InferenceStrategySK import JSONType, ReturnType, OutputType, functor, InferenceStrategySK
4+
5+
@functor
6+
class run_gpt_prompt_act_obj_event_triple(InferenceStrategySK):
7+
output_type = OutputType.JSON
8+
config = {
9+
"max_tokens": 50,
10+
"temperature": 0.8,
11+
"top_p": 0.95,
12+
"top_k": 40, # not supported by SK
13+
"min_p": 0.05, # not supported by SK
14+
15+
}
16+
prompt = """
17+
Transform natural language descriptions into structured JSON, focusing on the object, predicate, and specific status. The 'status' should reflect the primary action being performed with the object, described in a passive form, and should not include additional details unrelated to the action itself. Here are examples:
18+
19+
Name: Sam
20+
Action description: Sam Johnson is eating breakfast.
21+
Object: table
22+
Object state: clear with a plate of food and a cup of coffee
23+
Output: {
24+
"object": "table",
25+
"predicate": "is",
26+
"interaction": "being eaten on"
27+
}
28+
---
29+
Name: Joon
30+
Action description: Joon Park is brewing coffee.
31+
Object: coffee maker
32+
Object state: simmering
33+
Output: {
34+
"object": "coffee maker",
35+
"predicate": "is",
36+
"interaction": "brewing coffee"
37+
}
38+
---
39+
Name: Jane
40+
Action description: Jane Cook is sleeping.
41+
Object: bed
42+
Object state: supported Jane during her sleep
43+
Output: {
44+
"object": "bed",
45+
"predicate": "is",
46+
"interaction": "being slept in"
47+
}
48+
---
49+
Name: Michael
50+
Action description: Michael Bernstein is writing email on a computer.
51+
Object: computer
52+
Object state: in use
53+
Output: {
54+
"object": "computer",
55+
"predicate": "is",
56+
"interaction": "being used to write email"
57+
}
58+
---
59+
Name: Percy
60+
Action description: Percy Liang is teaching students in a classroom.
61+
Object: classroom
62+
Object state: filled with students learning
63+
Output: {
64+
"object": "classroom",
65+
"predicate": "is",
66+
"interaction": "being used for teaching"
67+
}
68+
---
69+
Name: Merrie
70+
Action description: Merrie Morris is running on a treadmill.
71+
Object: treadmill
72+
Object state: in use
73+
Output: {
74+
"object": "treadmill",
75+
"predicate": "is",
76+
"interaction": "being run on"
77+
}
78+
79+
Now, for a new case:
80+
81+
Name: {{$firstname}}
82+
Action description: {{$action_description}}
83+
Object: {{$object_name}}
84+
Object state: {{$object_state}}
85+
86+
Based on this description, provide a single JSON object in the format shown above. The "object" field must contain object name. Do not make the "status" a generic action, such as "being used", but find a more specific word clarifying how the {{$object_name}} is being used. In addition, exclude any extraneous details not directly related to this action. No intro nor Markdown, respond just with the JSON object.
87+
"""
88+
89+
def prepare_context(self, persona, task, act_obj_desc, object_name):
90+
return {
91+
"object_name": object_name,
92+
"action_description": task,
93+
"object_state": act_obj_desc,
94+
"firstname": persona.scratch.get_str_firstname(),
95+
}
96+
97+
def validate_json(self, json: JSONType) -> Optional[str]:
98+
# Check for the required fields in the JSON object
99+
required_fields = ["object", "predicate", "interaction"]
100+
for field in required_fields:
101+
if field not in json:
102+
return f"Missing field: {field}"
103+
# Check if the "object" field matches the lowercased object_name property
104+
if json["object"].lower() != self.context_variables['object_name'].lower():
105+
return "Object name mismatch"
106+
107+
def extract_json(self, json: JSONType) -> ReturnType:
108+
return (json["object"], json["predicate"], json["interaction"])
109+
110+
def fallback(self, persona, task, act_obj_desc, object_name):
111+
return (object_name, "is", "idle")
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import json
2+
3+
from persona.prompt_template.InferenceStrategySK import JSONType, OutputType, functor, InferenceStrategySK
4+
5+
@functor
6+
class run_gpt_prompt_action_sector(InferenceStrategySK):
7+
output_type = OutputType.JSON
8+
config = {
9+
"temperature": 0.3,
10+
}
11+
prompt = """
12+
We need to choose an appropriate Sector for the task at hand.
13+
14+
* Stay in the current sector if the activity can be done there. Only go out if the activity needs to take place in another place.
15+
* Must be one of the sectors from "All Sectors," verbatim. It must be a Sector, and not an Arena.
16+
* If none of those fit very well, we must still choose the one that's the closest fit.
17+
* Return the answer as a JSON object with a single key "area". The value is the chosen area name.
18+
19+
Sam Kim lives in the "Sam Kim's house" Sector that has the following Arenas: ["Sam Kim's room", "bathroom", "kitchen"]
20+
Sam Kim is currently in the "Sam Kim's house" Sector that has the following Arenas: ["Sam Kim's room", "bathroom", "kitchen"]
21+
All Sectors: ["Sam Kim's house", "The Rose and Crown Pub", "Hobbs Cafe", "Oak Hill College", "Johnson Park", "Harvey Oak Supply Store", "The Willows Market and Pharmacy"].
22+
For performing the "taking a walk" Action, Sam Kim should go to the following Sector:
23+
{"area": "Johnson Park"}
24+
---
25+
Jane Anderson lives in the "Oak Hill College Student Dormitory" Sector that has the following Arenas: ["Jane Anderson's room"]
26+
Jane Anderson is currently in the "Oak Hill College" Sector that has the following Arenas: ["classroom", "library"]
27+
All Sectors: ["Oak Hill College Student Dormitory", "The Rose and Crown Pub", "Hobbs Cafe", "Oak Hill College", "Johnson Park", "Harvey Oak Supply Store", "The Willows Market and Pharmacy"].
28+
For performing the "eating dinner" Action, Jane Anderson should go to the following Sector:
29+
{"area": "Hobbs Cafe"}
30+
---
31+
{{$name}} lives in the {{$living_sector}} Sector that has the following Arenas: {{$living_sector_arenas}}.
32+
{{$name}} is currently in the {{$current_sector}} Sector that has the following Arenas: {{$current_sector_arenas}}.
33+
All Sectors: {{$all_sectors}}.
34+
Pick the Sector for performing {{$name}}'s current activity.
35+
* Stay in the current sector if the activity can be done there. Only go out if the activity needs to take place in another place.
36+
* Must be one of the sectors from "All Sectors," verbatim. It must be a Sector, and not an Arena.
37+
* If none of those fit very well, we must still choose the one that's the closest fit.
38+
* Return the answer as a JSON object with a single key "area". The value is the chosen area name.
39+
For performing the {{$action_description}} Action, {{$name}} should go to the following Sector:
40+
"""
41+
42+
def prepare_context(self, action_description, persona, maze):
43+
self.persona = persona
44+
world_area = maze.access_tile(persona.scratch.curr_tile)['world']
45+
self.path_to_living_sector = persona.scratch.living_area.split(":")[:2]
46+
self.path_to_current_sector = [
47+
world_area,
48+
maze.access_tile(persona.scratch.curr_tile)['sector'],
49+
]
50+
self.living_sector_arenas = persona.s_mem.get_array_accessible_sector_arenas(
51+
":".join(self.path_to_living_sector)
52+
)
53+
self.current_sector_arenas = persona.s_mem.get_array_accessible_sector_arenas(
54+
":".join(self.path_to_current_sector)
55+
)
56+
known_sectors = persona.s_mem.get_str_accessible_sectors(world_area).split(", ")
57+
self.all_sectors = [sector for sector in known_sectors if "'s house" not in sector or persona.scratch.last_name in sector]
58+
59+
return {
60+
"name": persona.scratch.get_str_name(),
61+
"action_description": json.dumps(action_description),
62+
"living_sector": json.dumps(self.path_to_living_sector[1]),
63+
"living_sector_arenas": json.dumps(self.living_sector_arenas),
64+
"current_sector": json.dumps(self.path_to_current_sector[1]),
65+
"current_sector_arenas": json.dumps(self.current_sector_arenas),
66+
"all_sectors": json.dumps(self.all_sectors),
67+
}
68+
69+
def validate_json(self, json: JSONType):
70+
if "area" not in json:
71+
return "Missing area name"
72+
if json["area"] not in self.all_sectors:
73+
if json["area"] in self.living_sector_arenas or json["area"] in self.current_sector_arenas:
74+
return "Arena name was returned instead of the Sector name"
75+
else:
76+
return f"Specified Sector doesn't exist or isn't available to {self.persona.scratch.get_str_firstname()}"
77+
78+
def extract_json(self, json: JSONType):
79+
return json["area"]
80+
81+
def fallback(self, action_description, persona, maze):
82+
return maze.access_tile(persona.scratch.curr_tile)['sector']
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
from random import Random
2+
3+
from persona.common import is_valid_time, string_to_time
4+
from persona.prompt_template.InferenceStrategySK import JSONType, OutputType, functor, InferenceStrategySK
5+
6+
"""
7+
Basically the long term planning that spans a day. Returns a list of actions
8+
that the persona will take today. Usually comes in the following form:
9+
'wake up and complete the morning routine at 6:00 am',
10+
'eat breakfast at 7:00 am',..
11+
Note that the actions come without a period.
12+
13+
INPUT:
14+
persona: The Persona class instance
15+
OUTPUT:
16+
a list of daily actions in broad strokes.
17+
"""
18+
@functor
19+
class run_gpt_prompt_daily_plan(InferenceStrategySK):
20+
# semantic_function = skill["daily_planning_v6"]
21+
output_type = OutputType.JSON
22+
config = {
23+
"max_tokens": 1000,
24+
"temperature": 1,
25+
"top_p": 0.8,
26+
}
27+
prompt = """
28+
Let's consider {{$firstname}}:
29+
30+
{{$commonset}}
31+
32+
We need to draft a daily plan for {{$firstname}} in broad-strokes (with the time of the day. e.g., have a lunch at 12:00 pm, watch TV from 7 to 8 pm). The plan must be formatted as a single JSON array of objects, each object containing the following fields:
33+
34+
* start: start time with am/pm
35+
* end: end time with am/pm
36+
* activity: the activity {{$firstname}} is performing, in plain text
37+
38+
The entries must be in the correct order and must not intersect. The plan starts with waking up at {{$wake_up_hour}} and completing the morning routine, and it ends with going to sleep. What would be other items in the {{$firstname}}'s daily plan?
39+
"""
40+
41+
def prepare_context(self, persona, wake_up_hour):
42+
return {
43+
"commonset": persona.scratch.get_str_iss(),
44+
"date": persona.scratch.get_str_curr_date_str(),
45+
"firstname": persona.scratch.get_str_firstname(),
46+
"wake_up_hour": f"{str(wake_up_hour)}:00 am"
47+
}
48+
49+
def validate_json(self, json: JSONType):
50+
if not isinstance(json, list):
51+
return "Invalid JSON format (expected a JSON array)"
52+
if not all(isinstance(item, dict) and 'start' in item and 'end' in item and 'activity' in item for item in json):
53+
return "Invalid JSON format (expected an array of objects with 'start', 'end' and 'activity' fields)"
54+
wake_up_time = string_to_time(json[0]["start"])
55+
prev_time = None
56+
prev_task = None
57+
for item in json:
58+
for field in ["start", "end"]:
59+
if not is_valid_time(item[field]):
60+
return f'Invalid {field} time format: "{item[field]}". Example time format: "6:00 am".'
61+
time = string_to_time(item["start"])
62+
# For night owls, activities may continue past midnight and resume before the "wake-up" time.
63+
# This condition allows for time entries after midnight but before the first entry's time,
64+
# accommodating a schedule that doesn't strictly follow chronological order across days.
65+
is_past_midnight = time < wake_up_time and prev_time > wake_up_time
66+
if prev_time and time < prev_time and not is_past_midnight:
67+
raise ValueError(f'Tasks are not in chronological order. "{prev_task}" intersects with "{item["activity"]}"')
68+
prev_time = string_to_time(item["end"])
69+
prev_task = item["activity"]
70+
71+
def extract_json(self, json: JSONType):
72+
rng = Random(str(json))
73+
activities = ["Relax", "Rest", "Chill", "Procrastinate"]
74+
result = []
75+
for i, item in enumerate(json):
76+
if i != 0:
77+
start = item['start']
78+
prev_end = json[i-1]['end']
79+
if string_to_time(start) != string_to_time(prev_end):
80+
random_activity = rng.choice(activities)
81+
result.append(f"{prev_end} - {random_activity}")
82+
result.append(f"{item['start']} - {item['activity']}")
83+
return result
84+
# return [line for line in output.split('\n') if line.strip() and line[0].isdigit()]
85+
86+
def fallback(self, persona, wake_up_hour):
87+
return [
88+
'6:00 am - wake up and complete the morning routine',
89+
'7:00 am - eat breakfast',
90+
'8:00 am - read a book',
91+
'12:00 pm - have lunch',
92+
'1:00 pm - take a nap',
93+
'4:00 pm - relax',
94+
'7:00 pm - watch TV',
95+
'8:00 pm - relax',
96+
'11:00 pm - go to bed',
97+
]

0 commit comments

Comments
 (0)