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}."
|
||||
|
||||
|
||||
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."
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ["-", "_"]:
|
||||
|
|
|
@ -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.
|
||||
|
||||
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}."
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue