give worlds a name, save graphs to save folder as world, add a way to load custom actions
This commit is contained in:
parent
793b4a7cb8
commit
4d7db75ffb
|
@ -1,2 +1,3 @@
|
||||||
|
adventure/custom_actions.py
|
||||||
worlds/
|
worlds/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
|
|
@ -163,9 +163,3 @@ def action_give(character: str, item_name: str) -> str:
|
||||||
destination_actor.items.append(item)
|
destination_actor.items.append(item)
|
||||||
|
|
||||||
return f"You give the {item_name} item to {character}."
|
return f"You give the {item_name} item to {character}."
|
||||||
|
|
||||||
|
|
||||||
def action_stop() -> str:
|
|
||||||
_, _, action_actor = get_current_context()
|
|
||||||
logger.info(f"{action_actor.name} end their turn")
|
|
||||||
return "You stop your actions and end your turn."
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ def generate_actor(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def generate_world(agent: Agent, theme: str) -> World:
|
def generate_world(agent: Agent, name: str, theme: str) -> World:
|
||||||
room_count = randint(3, 5)
|
room_count = randint(3, 5)
|
||||||
logger.info(f"Generating a {theme} with {room_count} rooms")
|
logger.info(f"Generating a {theme} with {room_count} rooms")
|
||||||
|
|
||||||
|
@ -177,4 +177,4 @@ def generate_world(agent: Agent, theme: str) -> World:
|
||||||
room.portals[direction] = dest_room.name
|
room.portals[direction] = dest_room.name
|
||||||
dest_room.portals[opposite_directions[direction]] = room.name
|
dest_room.portals[opposite_directions[direction]] = room.name
|
||||||
|
|
||||||
return World(rooms=rooms, theme=theme)
|
return World(name=name, rooms=rooms, theme=theme)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from importlib import import_module
|
||||||
from json import load
|
from json import load
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
|
@ -53,7 +54,7 @@ def world_result_parser(value, agent, **kwargs):
|
||||||
return multi_function_or_str_result(value, agent=agent, **kwargs)
|
return multi_function_or_str_result(value, agent=agent, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def simulate_world(world: World, steps: int = 10, callback=None):
|
def simulate_world(world: World, steps: int = 10, callback=None, extra_actions=[]):
|
||||||
logger.info("Simulating the world")
|
logger.info("Simulating the world")
|
||||||
|
|
||||||
# collect actors, so they are only processed once
|
# collect actors, so they are only processed once
|
||||||
|
@ -68,6 +69,7 @@ def simulate_world(world: World, steps: int = 10, callback=None):
|
||||||
action_move,
|
action_move,
|
||||||
action_take,
|
action_take,
|
||||||
action_tell,
|
action_tell,
|
||||||
|
*extra_actions,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -105,6 +107,7 @@ def simulate_world(world: World, steps: int = 10, callback=None):
|
||||||
"What will you do next? Reply with a JSON function call, calling one of the actions."
|
"What will you do next? Reply with a JSON function call, calling one of the actions."
|
||||||
),
|
),
|
||||||
context={
|
context={
|
||||||
|
# TODO: add custom action names or remove this list entirely
|
||||||
"actions": [
|
"actions": [
|
||||||
"ask",
|
"ask",
|
||||||
"give",
|
"give",
|
||||||
|
@ -147,7 +150,7 @@ def simulate_world(world: World, steps: int = 10, callback=None):
|
||||||
if callback:
|
if callback:
|
||||||
callback(world, current_step)
|
callback(world, current_step)
|
||||||
|
|
||||||
current_step += 1
|
set_step(current_step + 1)
|
||||||
|
|
||||||
|
|
||||||
# main
|
# main
|
||||||
|
@ -157,6 +160,9 @@ def parse_args():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Generate and simulate a fantasy world"
|
description="Generate and simulate a fantasy world"
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--actions", type=str, help="Extra actions to include in the simulation"
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--steps", type=int, default=10, help="The number of simulation steps to run"
|
"--steps", type=int, default=10, help="The number of simulation steps to run"
|
||||||
)
|
)
|
||||||
|
@ -166,13 +172,13 @@ def parse_args():
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--world",
|
"--world",
|
||||||
type=str,
|
type=str,
|
||||||
default="world.json",
|
default="world",
|
||||||
help="The file to save the generated world to",
|
help="The file to save the generated world to",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--world-state",
|
"--state",
|
||||||
type=str,
|
type=str,
|
||||||
default="world-state.json",
|
# default="world-state.json",
|
||||||
help="The file to save the world state to",
|
help="The file to save the world state to",
|
||||||
)
|
)
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
@ -181,18 +187,23 @@ def parse_args():
|
||||||
def main():
|
def main():
|
||||||
args = parse_args()
|
args = parse_args()
|
||||||
|
|
||||||
if args.world_state and path.exists(args.world_state):
|
world_file = args.world + ".json"
|
||||||
logger.info(f"Loading world state from {args.world_state}")
|
world_state_file = args.state or (args.world + ".state.json")
|
||||||
with open(args.world_state, "r") as f:
|
|
||||||
|
if path.exists(world_state_file):
|
||||||
|
logger.info(f"Loading world state from {world_state_file}")
|
||||||
|
with open(world_state_file, "r") as f:
|
||||||
state = WorldState(**load(f))
|
state = WorldState(**load(f))
|
||||||
|
|
||||||
set_step(state.step)
|
set_step(state.step)
|
||||||
create_agents(state.world, state.memory)
|
create_agents(state.world, state.memory)
|
||||||
|
|
||||||
world = state.world
|
world = state.world
|
||||||
elif args.world and path.exists(args.world):
|
world.name = args.world
|
||||||
logger.info(f"Loading world from {args.world}")
|
elif path.exists(world_file):
|
||||||
with open(args.world, "r") as f:
|
logger.info(f"Loading world from {world_file}")
|
||||||
world = World(**load(f))
|
with open(world_file, "r") as f:
|
||||||
|
world = World(**load(f), name=args.world)
|
||||||
create_agents(world)
|
create_agents(world)
|
||||||
else:
|
else:
|
||||||
logger.info(f"Generating a new {args.theme} world")
|
logger.info(f"Generating a new {args.theme} world")
|
||||||
|
@ -203,18 +214,29 @@ def main():
|
||||||
{},
|
{},
|
||||||
llm,
|
llm,
|
||||||
)
|
)
|
||||||
world = generate_world(agent, args.theme)
|
world = generate_world(agent, args.world, args.theme)
|
||||||
create_agents(world)
|
create_agents(world)
|
||||||
|
save_world(world, world_file)
|
||||||
|
|
||||||
logger.debug("Loaded world: %s", world)
|
# load extra actions
|
||||||
|
extra_actions = []
|
||||||
if args.world:
|
if args.actions:
|
||||||
save_world(world, args.world)
|
logger.info(f"Loading extra actions from {args.actions}")
|
||||||
|
action_module, action_function = args.actions.rsplit(":", 1)
|
||||||
|
action_module = import_module(action_module)
|
||||||
|
action_function = getattr(action_module, action_function)
|
||||||
|
module_actions = action_function()
|
||||||
|
logger.info(
|
||||||
|
f"Loaded extra actions: {[action.__name__ for action in module_actions]}"
|
||||||
|
)
|
||||||
|
extra_actions.append(module_actions)
|
||||||
|
|
||||||
|
logger.debug("Simulating world: %s", world)
|
||||||
simulate_world(
|
simulate_world(
|
||||||
world,
|
world,
|
||||||
steps=args.steps,
|
steps=args.steps,
|
||||||
callback=lambda w, s: save_world_state(w, s, args.world_state),
|
callback=lambda w, s: save_world_state(w, s, world_state_file),
|
||||||
|
extra_actions=extra_actions,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ class Room:
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class World:
|
class World:
|
||||||
|
name: str
|
||||||
rooms: List[Room]
|
rooms: List[Room]
|
||||||
theme: str
|
theme: str
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from json import dump
|
from json import dump
|
||||||
|
from os import path
|
||||||
from typing import Dict, List, Sequence
|
from typing import Dict, List, Sequence
|
||||||
|
|
||||||
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, SystemMessage
|
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, SystemMessage
|
||||||
|
@ -27,11 +28,12 @@ def graph_world(world: World, step: int):
|
||||||
graph = graphviz.Digraph(f"{world.theme}-{step}", format="png")
|
graph = graphviz.Digraph(f"{world.theme}-{step}", format="png")
|
||||||
for room in world.rooms:
|
for room in world.rooms:
|
||||||
room_label = "\n".join([room.name, *[actor.name for actor in room.actors]])
|
room_label = "\n".join([room.name, *[actor.name for actor in room.actors]])
|
||||||
graph.node(room.name, room_label) # , room.description)
|
graph.node(room.name, room_label)
|
||||||
for direction, destination in room.portals.items():
|
for direction, destination in room.portals.items():
|
||||||
graph.edge(room.name, destination, label=direction)
|
graph.edge(room.name, destination, label=direction)
|
||||||
|
|
||||||
graph.render(directory="worlds", view=True)
|
graph_path = path.dirname(world.name)
|
||||||
|
graph.render(directory=graph_path)
|
||||||
|
|
||||||
|
|
||||||
def snapshot_world(world: World, step: int):
|
def snapshot_world(world: World, step: int):
|
||||||
|
|
Loading…
Reference in New Issue