1
0
Fork 0

start using promptgen
Run Docker Build / build (push) Failing after 1m50s Details
Run Python Build / build (push) Failing after 1m53s Details

This commit is contained in:
Sean Sube 2024-05-19 15:27:56 -05:00
parent 22390631f5
commit 8a5058dbec
Signed by: ssube
GPG Key ID: 3EED7B957D362AF1
7 changed files with 230 additions and 81 deletions

View File

@ -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."

View File

@ -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

View File

@ -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 ["-", "_"]:

180
adventure/render/prompt.py Normal file
View File

@ -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

View File

@ -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}."

View File

@ -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."

View File

@ -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,