start using promptgen
This commit is contained in:
parent
22390631f5
commit
8a5058dbec
|
@ -91,22 +91,22 @@ def action_move(direction: str) -> str:
|
||||||
return f"You move {direction} and arrive at {destination_room.name}."
|
return f"You move {direction} and arrive at {destination_room.name}."
|
||||||
|
|
||||||
|
|
||||||
def action_take(item_name: str) -> str:
|
def action_take(item: str) -> str:
|
||||||
"""
|
"""
|
||||||
Take an item from the room and put it in your inventory.
|
Take an item from the room and put it in your inventory.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
item_name: The name of the item to take.
|
item: The name of the item to take.
|
||||||
"""
|
"""
|
||||||
with action_context() as (action_room, action_actor):
|
with action_context() as (action_room, action_actor):
|
||||||
item = find_item_in_room(action_room, item_name)
|
action_item = find_item_in_room(action_room, item)
|
||||||
if not item:
|
if not action_item:
|
||||||
return "The {item_name} item is not in the room."
|
return f"The {item} item is not in the room."
|
||||||
|
|
||||||
broadcast(f"{action_actor.name} takes the {item_name} item")
|
broadcast(f"{action_actor.name} takes the {item} item")
|
||||||
action_room.items.remove(item)
|
action_room.items.remove(action_item)
|
||||||
action_actor.items.append(item)
|
action_actor.items.append(action_item)
|
||||||
return "You take the {item_name} item and put it in your inventory."
|
return f"You take the {item} item and put it in your inventory."
|
||||||
|
|
||||||
|
|
||||||
def action_ask(character: str, question: str) -> str:
|
def action_ask(character: str, question: str) -> str:
|
||||||
|
@ -184,45 +184,45 @@ def action_tell(character: str, message: str) -> str:
|
||||||
return f"{character} does not respond."
|
return f"{character} does not respond."
|
||||||
|
|
||||||
|
|
||||||
def action_give(character: str, item_name: str) -> str:
|
def action_give(character: str, item: str) -> str:
|
||||||
"""
|
"""
|
||||||
Give an item to another character in the room.
|
Give an item to another character in the room.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
character: The name of the character to give the item to.
|
character: The name of the character to give the item to.
|
||||||
item_name: The name of the item to give.
|
item: The name of the item to give.
|
||||||
"""
|
"""
|
||||||
with action_context() as (action_room, action_actor):
|
with action_context() as (action_room, action_actor):
|
||||||
destination_actor = find_actor_in_room(action_room, character)
|
destination_actor = find_actor_in_room(action_room, character)
|
||||||
if not destination_actor:
|
if not destination_actor:
|
||||||
return f"The {character} character is not in the room."
|
return f"The {character} character is not in the room."
|
||||||
|
|
||||||
item = find_item_in_actor(action_actor, item_name)
|
action_item = find_item_in_actor(action_actor, item)
|
||||||
if not item:
|
if not action_item:
|
||||||
return f"You do not have the {item_name} item in your inventory."
|
return f"You do not have the {item} item in your inventory."
|
||||||
|
|
||||||
broadcast(f"{action_actor.name} gives {character} the {item_name} item.")
|
broadcast(f"{action_actor.name} gives {character} the {item} item.")
|
||||||
action_actor.items.remove(item)
|
action_actor.items.remove(action_item)
|
||||||
destination_actor.items.append(item)
|
destination_actor.items.append(action_item)
|
||||||
|
|
||||||
return f"You give the {item_name} item to {character}."
|
return f"You give the {item} item to {character}."
|
||||||
|
|
||||||
|
|
||||||
def action_drop(item_name: str) -> str:
|
def action_drop(item: str) -> str:
|
||||||
"""
|
"""
|
||||||
Drop an item from your inventory into the room.
|
Drop an item from your inventory into the room.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
item_name: The name of the item to drop.
|
item: The name of the item to drop.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with action_context() as (action_room, action_actor):
|
with action_context() as (action_room, action_actor):
|
||||||
item = find_item_in_actor(action_actor, item_name)
|
action_item = find_item_in_actor(action_actor, item)
|
||||||
if not item:
|
if not action_item:
|
||||||
return f"You do not have the {item_name} item in your inventory."
|
return f"You do not have the {item} item in your inventory."
|
||||||
|
|
||||||
broadcast(f"{action_actor.name} drops the {item_name} item")
|
broadcast(f"{action_actor.name} drops the {item} item")
|
||||||
action_actor.items.remove(item)
|
action_actor.items.remove(action_item)
|
||||||
action_room.items.append(item)
|
action_room.items.append(action_item)
|
||||||
|
|
||||||
return f"You drop the {item_name} item."
|
return f"You drop the {item} item."
|
||||||
|
|
|
@ -73,6 +73,8 @@ class ReplyEvent(BaseModel):
|
||||||
An actor has replied with text.
|
An actor has replied with text.
|
||||||
|
|
||||||
This is the non-JSON version of an ActionEvent.
|
This is the non-JSON version of an ActionEvent.
|
||||||
|
|
||||||
|
TODO: add the actor being replied to.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
text: str
|
text: str
|
||||||
|
|
|
@ -28,7 +28,8 @@ from adventure.models.event import (
|
||||||
ResultEvent,
|
ResultEvent,
|
||||||
StatusEvent,
|
StatusEvent,
|
||||||
)
|
)
|
||||||
from adventure.utils.world import describe_entity
|
|
||||||
|
from .prompt import prompt_from_entity, prompt_from_event
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
|
@ -211,39 +212,6 @@ def generate_images(
|
||||||
return paths
|
return paths
|
||||||
|
|
||||||
|
|
||||||
def prompt_from_event(event: GameEvent) -> str | None:
|
|
||||||
if isinstance(event, ActionEvent):
|
|
||||||
if event.item:
|
|
||||||
return (
|
|
||||||
f"{event.actor.name} uses the {event.item.name}. {describe_entity(event.item)}. "
|
|
||||||
f"{describe_entity(event.actor)}. {describe_entity(event.room)}."
|
|
||||||
)
|
|
||||||
|
|
||||||
action_name = event.action.removeprefix("action_")
|
|
||||||
return f"{event.actor.name} uses {action_name}. {describe_entity(event.actor)}. {describe_entity(event.room)}."
|
|
||||||
|
|
||||||
if isinstance(event, ReplyEvent):
|
|
||||||
return event.text
|
|
||||||
|
|
||||||
if isinstance(event, ResultEvent):
|
|
||||||
return f"{event.result}. {describe_entity(event.actor)}. {describe_entity(event.room)}."
|
|
||||||
|
|
||||||
if isinstance(event, StatusEvent):
|
|
||||||
if event.room:
|
|
||||||
if event.actor:
|
|
||||||
return f"{event.text}. {describe_entity(event.actor)}. {describe_entity(event.room)}."
|
|
||||||
|
|
||||||
return f"{event.text}. {describe_entity(event.room)}."
|
|
||||||
|
|
||||||
return event.text
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def prompt_from_entity(entity: WorldEntity) -> str:
|
|
||||||
return describe_entity(entity)
|
|
||||||
|
|
||||||
|
|
||||||
def sanitize_name(name: str) -> str:
|
def sanitize_name(name: str) -> str:
|
||||||
def valid_char(c: str) -> str:
|
def valid_char(c: str) -> str:
|
||||||
if c.isalnum() or c in ["-", "_"]:
|
if c.isalnum() or c in ["-", "_"]:
|
||||||
|
|
|
@ -0,0 +1,180 @@
|
||||||
|
import re
|
||||||
|
from logging import getLogger
|
||||||
|
from random import shuffle
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from adventure.context import get_current_world, get_dungeon_master
|
||||||
|
from adventure.models.entity import Room, WorldEntity
|
||||||
|
from adventure.models.event import (
|
||||||
|
ActionEvent,
|
||||||
|
GameEvent,
|
||||||
|
ReplyEvent,
|
||||||
|
ResultEvent,
|
||||||
|
StatusEvent,
|
||||||
|
)
|
||||||
|
from adventure.utils.search import find_actor_in_room, find_item_in_room, find_room
|
||||||
|
from adventure.utils.world import describe_entity
|
||||||
|
|
||||||
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def prompt_from_parameters(
|
||||||
|
action_room: Room, parameters: dict[str, bool | float | int | str]
|
||||||
|
) -> tuple[str, str]:
|
||||||
|
pre = []
|
||||||
|
post = []
|
||||||
|
|
||||||
|
if "character" in parameters:
|
||||||
|
# look up the character
|
||||||
|
character_name = str(parameters["character"])
|
||||||
|
logger.debug("searching for parameter character: %s", character_name)
|
||||||
|
target_actor = find_actor_in_room(action_room, character_name)
|
||||||
|
if target_actor:
|
||||||
|
logger.debug("adding actor to prompt: %s", target_actor.name)
|
||||||
|
pre.append(f"with {target_actor.name}")
|
||||||
|
post.append(describe_entity(target_actor))
|
||||||
|
|
||||||
|
if "item" in parameters:
|
||||||
|
# look up the item
|
||||||
|
item_name = str(parameters["item"])
|
||||||
|
logger.debug("searching for parameter item: %s", item_name)
|
||||||
|
target_item = find_item_in_room(
|
||||||
|
action_room,
|
||||||
|
item_name,
|
||||||
|
include_actor_inventory=True,
|
||||||
|
include_item_inventory=True,
|
||||||
|
)
|
||||||
|
if target_item:
|
||||||
|
logger.debug("adding item to prompt: %s", target_item.name)
|
||||||
|
pre.append(f"using the {target_item.name}")
|
||||||
|
post.append(describe_entity(target_item))
|
||||||
|
|
||||||
|
if "target" in parameters:
|
||||||
|
# could be a room, actor, or item
|
||||||
|
target_name = str(parameters["target"])
|
||||||
|
logger.debug("searching for parameter target: %s", target_name)
|
||||||
|
|
||||||
|
world = get_current_world()
|
||||||
|
if world:
|
||||||
|
target_room = find_room(world, target_name)
|
||||||
|
if target_room:
|
||||||
|
logger.debug("adding room to prompt: %s", target_room.name)
|
||||||
|
pre.append(f"in the {target_room.name}")
|
||||||
|
post.append(describe_entity(target_room))
|
||||||
|
|
||||||
|
target_actor = find_actor_in_room(action_room, target_name)
|
||||||
|
if target_actor:
|
||||||
|
logger.debug("adding actor to prompt: %s", target_actor.name)
|
||||||
|
pre.append(f"with {target_actor.name}")
|
||||||
|
post.append(describe_entity(target_actor))
|
||||||
|
|
||||||
|
target_item = find_item_in_room(
|
||||||
|
action_room,
|
||||||
|
target_name,
|
||||||
|
include_actor_inventory=True,
|
||||||
|
include_item_inventory=True,
|
||||||
|
)
|
||||||
|
if target_item:
|
||||||
|
logger.debug("adding item to prompt: %s", target_item.name)
|
||||||
|
pre.append(f"using the {target_item.name}")
|
||||||
|
post.append(describe_entity(target_item))
|
||||||
|
|
||||||
|
return (" and ".join(pre) if pre else "", " and ".join(post) if post else "")
|
||||||
|
|
||||||
|
|
||||||
|
def scene_from_event(event: GameEvent) -> str | None:
|
||||||
|
logger.debug("generating scene from event: %s", event)
|
||||||
|
|
||||||
|
if isinstance(event, ActionEvent):
|
||||||
|
action_name = event.action.removeprefix("action_")
|
||||||
|
parameter_pre, parameter_post = prompt_from_parameters(
|
||||||
|
event.room, event.parameters
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
f"{event.actor.name} uses the {action_name} action {parameter_pre}. "
|
||||||
|
"{describe_entity(event.actor)}. {describe_entity(event.room)}. {parameter_post}."
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(event, ReplyEvent):
|
||||||
|
return f"{event.actor.name} replies: {event.text}. {describe_entity(event.actor)}. {describe_entity(event.room)}."
|
||||||
|
|
||||||
|
if isinstance(event, ResultEvent):
|
||||||
|
return f"{event.result}. {describe_entity(event.actor)}. {describe_entity(event.room)}."
|
||||||
|
|
||||||
|
if isinstance(event, StatusEvent):
|
||||||
|
if event.room:
|
||||||
|
if event.actor:
|
||||||
|
return f"{event.text}. {describe_entity(event.actor)}. {describe_entity(event.room)}."
|
||||||
|
|
||||||
|
return f"{event.text}. {describe_entity(event.room)}."
|
||||||
|
|
||||||
|
return event.text
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def scene_from_entity(entity: WorldEntity) -> str:
|
||||||
|
logger.debug("generating scene from entity: %s", entity)
|
||||||
|
|
||||||
|
return f"Describe the {entity.type} called {entity.name}. {describe_entity(entity)}"
|
||||||
|
|
||||||
|
|
||||||
|
def make_example_prompts(keywords: List[str], k=5, q=10) -> List[str]:
|
||||||
|
logger.debug("generating %s example prompts from keywords: %s", k, keywords)
|
||||||
|
|
||||||
|
examples = []
|
||||||
|
for _ in range(k):
|
||||||
|
example = list(keywords)
|
||||||
|
shuffle(example)
|
||||||
|
examples.append(", ".join(example[:q]))
|
||||||
|
|
||||||
|
return examples
|
||||||
|
|
||||||
|
|
||||||
|
def generate_keywords_from_scene(scene: str) -> List[str]:
|
||||||
|
logger.debug("generating keywords from scene: %s", scene)
|
||||||
|
|
||||||
|
# TODO: use a gpt2 model to generate keywords from scene
|
||||||
|
# hack for now: split on punctuation and whitespace
|
||||||
|
return re.split(r"\W+", scene)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_prompt_from_scene(scene: str, example_prompts: List[str]) -> str:
|
||||||
|
logger.debug(
|
||||||
|
"generating prompt from scene and example prompts: %s, %s",
|
||||||
|
scene,
|
||||||
|
example_prompts,
|
||||||
|
)
|
||||||
|
# generate prompt from scene and example prompts
|
||||||
|
dungeon_master = get_dungeon_master()
|
||||||
|
return dungeon_master(
|
||||||
|
"Generate a prompt for the following scene:\n"
|
||||||
|
"{scene}\n"
|
||||||
|
"Here are some example prompts:\n"
|
||||||
|
"{examples}\n"
|
||||||
|
"Reply with a comma-separated list of keywords that summarize the visual details of the scene."
|
||||||
|
"Make sure you describe the location, characters, and any items present. Be creative with the details."
|
||||||
|
"Do not include the question or any JSON. Only include the list of keywords on a single line.",
|
||||||
|
examples=example_prompts,
|
||||||
|
scene=scene,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def prompt_from_event(event: GameEvent) -> str | None:
|
||||||
|
scene = scene_from_event(event)
|
||||||
|
if not scene:
|
||||||
|
return None
|
||||||
|
|
||||||
|
keywords = generate_keywords_from_scene(scene)
|
||||||
|
example_prompts = make_example_prompts(keywords)
|
||||||
|
result = generate_prompt_from_scene(scene, example_prompts)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def prompt_from_entity(entity: WorldEntity) -> str:
|
||||||
|
scene = scene_from_entity(entity)
|
||||||
|
keywords = generate_keywords_from_scene(scene)
|
||||||
|
example_prompts = make_example_prompts(keywords)
|
||||||
|
result = generate_prompt_from_scene(scene, example_prompts)
|
||||||
|
return result
|
|
@ -24,23 +24,23 @@ recipes = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def action_craft(item_name: str) -> str:
|
def action_craft(item: str) -> str:
|
||||||
"""
|
"""
|
||||||
Craft an item using available recipes and inventory items.
|
Craft an item using available recipes and inventory items.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
item_name: The name of the item to craft.
|
item: The name of the item to craft.
|
||||||
"""
|
"""
|
||||||
with world_context() as (action_world, _, action_actor):
|
with world_context() as (action_world, _, action_actor):
|
||||||
if item_name not in recipes:
|
if item not in recipes:
|
||||||
return f"There is no recipe to craft a {item_name}."
|
return f"There is no recipe to craft a {item}."
|
||||||
|
|
||||||
recipe = recipes[item_name]
|
recipe = recipes[item]
|
||||||
|
|
||||||
# Check if the actor has the required skill level
|
# Check if the actor has the required skill level
|
||||||
skill = randint(1, 20)
|
skill = randint(1, 20)
|
||||||
if skill < recipe.difficulty:
|
if skill < recipe.difficulty:
|
||||||
return f"You need a crafting skill level of {recipe.difficulty} to craft {item_name}."
|
return f"You need a crafting skill level of {recipe.difficulty} to craft {item}."
|
||||||
|
|
||||||
# Collect inventory items names
|
# Collect inventory items names
|
||||||
inventory_items = {item.name for item in action_actor.items}
|
inventory_items = {item.name for item in action_actor.items}
|
||||||
|
@ -50,9 +50,7 @@ def action_craft(item_name: str) -> str:
|
||||||
item for item in recipe.ingredients if item not in inventory_items
|
item for item in recipe.ingredients if item not in inventory_items
|
||||||
]
|
]
|
||||||
if missing_items:
|
if missing_items:
|
||||||
return (
|
return f"You are missing {' and '.join(missing_items)} to craft {item}."
|
||||||
f"You are missing {' and '.join(missing_items)} to craft {item_name}."
|
|
||||||
)
|
|
||||||
|
|
||||||
# Deduct the ingredients from inventory
|
# Deduct the ingredients from inventory
|
||||||
for ingredient in recipe.ingredients:
|
for ingredient in recipe.ingredients:
|
||||||
|
@ -76,5 +74,5 @@ def action_craft(item_name: str) -> str:
|
||||||
|
|
||||||
action_actor.items.append(new_item)
|
action_actor.items.append(new_item)
|
||||||
|
|
||||||
broadcast(f"{action_actor.name} crafts a {item_name}.")
|
broadcast(f"{action_actor.name} crafts a {item}.")
|
||||||
return f"You successfully craft a {item_name}."
|
return f"You successfully craft a {item}."
|
||||||
|
|
|
@ -2,20 +2,20 @@ from adventure.context import action_context, broadcast
|
||||||
from adventure.utils.search import find_item_in_actor
|
from adventure.utils.search import find_item_in_actor
|
||||||
|
|
||||||
|
|
||||||
def action_read(item_name: str) -> str:
|
def action_read(item: str) -> str:
|
||||||
"""
|
"""
|
||||||
Read an item like a book or a sign.
|
Read an item like a book or a sign.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
item_name: The name of the item to read.
|
item: The name of the item to read.
|
||||||
"""
|
"""
|
||||||
with action_context() as (_, action_actor):
|
with action_context() as (_, action_actor):
|
||||||
item = find_item_in_actor(action_actor, item_name)
|
action_item = find_item_in_actor(action_actor, item)
|
||||||
if not item:
|
if not action_item:
|
||||||
return f"You do not have a {item_name} to read."
|
return f"You do not have a {item} to read."
|
||||||
|
|
||||||
if "text" in item.attributes:
|
if "text" in action_item.attributes:
|
||||||
broadcast(f"{action_actor.name} reads {item_name}")
|
broadcast(f"{action_actor.name} reads {item}")
|
||||||
return str(item.attributes["text"])
|
return str(action_item.attributes["text"])
|
||||||
|
|
||||||
return f"The {item_name} has nothing to read."
|
return f"The {item} has nothing to read."
|
||||||
|
|
|
@ -39,6 +39,7 @@ def find_actor_in_room(room: Room, actor_name: str) -> Actor | None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: allow item or str
|
||||||
def find_item(
|
def find_item(
|
||||||
world: World,
|
world: World,
|
||||||
item_name: str,
|
item_name: str,
|
||||||
|
|
Loading…
Reference in New Issue