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}."
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.
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):
item = find_item_in_room(action_room, item_name)
if not item:
return "The {item_name} item is not in the room."
action_item = find_item_in_room(action_room, item)
if not action_item:
return f"The {item} item is not in the room."
broadcast(f"{action_actor.name} takes the {item_name} item")
action_room.items.remove(item)
action_actor.items.append(item)
return "You take the {item_name} item and put it in your inventory."
broadcast(f"{action_actor.name} takes the {item} item")
action_room.items.remove(action_item)
action_actor.items.append(action_item)
return f"You take the {item} item and put it in your inventory."
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."
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.
Args:
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):
destination_actor = find_actor_in_room(action_room, character)
if not destination_actor:
return f"The {character} character is not in the room."
item = find_item_in_actor(action_actor, item_name)
if not item:
return f"You do not have the {item_name} item in your inventory."
action_item = find_item_in_actor(action_actor, item)
if not action_item:
return f"You do not have the {item} item in your inventory."
broadcast(f"{action_actor.name} gives {character} the {item_name} item.")
action_actor.items.remove(item)
destination_actor.items.append(item)
broadcast(f"{action_actor.name} gives {character} the {item} item.")
action_actor.items.remove(action_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.
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):
item = find_item_in_actor(action_actor, item_name)
if not item:
return f"You do not have the {item_name} item in your inventory."
action_item = find_item_in_actor(action_actor, item)
if not action_item:
return f"You do not have the {item} item in your inventory."
broadcast(f"{action_actor.name} drops the {item_name} item")
action_actor.items.remove(item)
action_room.items.append(item)
broadcast(f"{action_actor.name} drops the {item} item")
action_actor.items.remove(action_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.
This is the non-JSON version of an ActionEvent.
TODO: add the actor being replied to.
"""
text: str

View File

@ -28,7 +28,8 @@ from adventure.models.event import (
ResultEvent,
StatusEvent,
)
from adventure.utils.world import describe_entity
from .prompt import prompt_from_entity, prompt_from_event
logger = getLogger(__name__)
@ -211,39 +212,6 @@ def generate_images(
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 valid_char(c: str) -> str:
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.
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):
if item_name not in recipes:
return f"There is no recipe to craft a {item_name}."
if item not in recipes:
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
skill = randint(1, 20)
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
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
]
if missing_items:
return (
f"You are missing {' and '.join(missing_items)} to craft {item_name}."
)
return f"You are missing {' and '.join(missing_items)} to craft {item}."
# Deduct the ingredients from inventory
for ingredient in recipe.ingredients:
@ -76,5 +74,5 @@ def action_craft(item_name: str) -> str:
action_actor.items.append(new_item)
broadcast(f"{action_actor.name} crafts a {item_name}.")
return f"You successfully craft a {item_name}."
broadcast(f"{action_actor.name} crafts a {item}.")
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
def action_read(item_name: str) -> str:
def action_read(item: str) -> str:
"""
Read an item like a book or a sign.
Args:
item_name: The name of the item to read.
item: The name of the item to read.
"""
with action_context() as (_, action_actor):
item = find_item_in_actor(action_actor, item_name)
if not item:
return f"You do not have a {item_name} to read."
action_item = find_item_in_actor(action_actor, item)
if not action_item:
return f"You do not have a {item} to read."
if "text" in item.attributes:
broadcast(f"{action_actor.name} reads {item_name}")
return str(item.attributes["text"])
if "text" in action_item.attributes:
broadcast(f"{action_actor.name} reads {item}")
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
# TODO: allow item or str
def find_item(
world: World,
item_name: str,