1
0
Fork 0

add new characters to turn order, fix leaving, move image count to config

This commit is contained in:
Sean Sube 2024-05-27 12:03:39 -05:00
parent fea124b3f3
commit 16e7243da1
Signed by: ssube
GPG Key ID: 3EED7B957D362AF1
13 changed files with 111 additions and 38 deletions

View File

@ -9,6 +9,6 @@ COPY requirements/base.txt /taleweave/requirements/base.txt
RUN pip install --no-cache-dir -r requirements/base.txt
RUN pip install --no-cache-dir --index-url https://test.pypi.org/simple/ packit_llm==0.1.0
COPY adventure/ /taleweave/adventure/
COPY taleweave/ /taleweave/taleweave/
CMD ["python", "-m", "adventure.main"]
CMD ["python", "-m", "taleweave.main"]

View File

@ -107,19 +107,16 @@ server:
```bash
# Start the TaleWeave AI engine
python3 -m adventure.main \
python3 -m taleweave.main \
--world worlds/outback-animals-1 \
--world-prompt ./adventure/prompts.yml:outback-animals \
--world-prompt ./taleweave/prompts.yml:outback-animals \
--discord=true \
--server=true \
--rooms 3 \
--turns 30 \
--optional-actions=true \
--actions adventure.sim_systems:init_actions \
--systems adventure.sim_systems:init_logic
# --actions adventure.custom_systems:init_actions
# --systems adventure.custom_systems:init_logic
--actions taleweave.systems.sim:init_actions \
--systems taleweave.systems.sim:init_logic
```
This will generate a relatively small world with 3 rooms or areas, run for 30 steps, then shut down.

View File

@ -54,9 +54,12 @@ export function App(props: AppProps) {
function setPlayer(character: Maybe<Character>) {
// do not call setCharacter until the server confirms the player change
// eslint-disable-next-line no-null/no-null
let become = null;
if (doesExist(character)) {
sendMessage(JSON.stringify({ type: 'player', become: character.name }));
become = character.name;
}
sendMessage(JSON.stringify({ type: 'player', become }));
}
function sendInput(input: string) {
@ -96,10 +99,20 @@ export function App(props: AppProps) {
setActiveTurn(event.client === clientId);
break;
case 'player':
if (event.status === 'join' && doesExist(world) && event.client === clientId) {
if (doesExist(world) && event.client === clientId) {
switch (event.status) {
case 'join': {
const { character: characterName } = event;
const character = world.rooms.flatMap((room) => room.characters).find((a) => a.name === characterName);
setCharacter(character);
break;
}
case 'leave':
setCharacter(undefined);
break;
default:
// ignore other player events
}
}
break;
case 'players':

View File

@ -8,7 +8,7 @@ render:
checkpoints: [
"diffusion-sdxl-dynavision-0-5-5-7.safetensors",
]
path: /tmp/adventure-images
path: /tmp/taleweave-images
sizes:
landscape:
width: 1280

View File

@ -81,14 +81,18 @@ class AdventureClient(Client):
channel = message.channel
user_name = author.name # include nick
if message.content.startswith("!adventure"):
if message.content.startswith(
bot_config.command_prefix + bot_config.name_command
):
world = get_current_world()
if world:
active_world = f"Active world: {world.name} (theme: {world.theme})"
else:
active_world = "No active world"
await message.channel.send(f"Hello! Welcome to Adventure! {active_world}")
await message.channel.send(
f"Hello! Welcome to {bot_config.name_title}! {active_world}"
)
return
if message.content.startswith("!help"):

View File

@ -19,7 +19,7 @@ from pyee.base import EventEmitter
from taleweave.game_system import GameSystem
from taleweave.models.entity import Character, Room, World
from taleweave.models.event import GameEvent
from taleweave.models.event import GameEvent, StatusEvent
from taleweave.utils.string import normalize_name
logger = getLogger(__name__)
@ -49,12 +49,17 @@ def get_event_name(event: GameEvent | Type[GameEvent]):
def broadcast(message: str | GameEvent):
if isinstance(message, GameEvent):
event_name = get_event_name(message)
logger.debug(f"broadcasting {event_name}")
event_emitter.emit(event_name, message)
event = message
else:
logger.warning("broadcasting a string message is deprecated")
event_emitter.emit(STRING_EVENT_TYPE, message)
logger.warning(
"broadcasting a string message is deprecated, converting to status event: %s",
message,
)
event = StatusEvent(text=message)
event_name = get_event_name(event)
logger.debug(f"broadcasting {event_name}: {event}")
event_emitter.emit(event_name, event)
def is_union(type_: Type | UnionType):

View File

@ -286,6 +286,7 @@ def generate_character(
dest_room: Room,
additional_prompt: str = "",
detail_prompt: str = "",
add_to_world_order: bool = True,
) -> Character:
existing_characters = [character.name for character in list_characters(world)] + [
character.name for character in list_characters_in_room(dest_room)
@ -350,6 +351,10 @@ def generate_character(
except Exception:
logger.exception("error generating item")
if add_to_world_order:
logger.info(f"adding character {name} to end of world turn order")
world.order.append(name)
return character

View File

@ -12,6 +12,9 @@ class Size:
@dataclass
class DiscordBotConfig:
channels: List[str]
command_prefix: str
name_command: str
name_title: str
content_intent: bool = False
@ -24,6 +27,7 @@ class BotConfig:
class RenderConfig:
cfg: int | IntRange
checkpoints: List[str]
count: int
path: str
sizes: Dict[str, Size]
steps: int | IntRange
@ -80,19 +84,27 @@ class Config:
DEFAULT_CONFIG = Config(
bot=BotConfig(discord=DiscordBotConfig(channels=["adventure"])),
bot=BotConfig(
discord=DiscordBotConfig(
channels=["taleweave"],
command_prefix="!",
name_command="taleweave",
name_title="Taleweave AI",
),
),
render=RenderConfig(
cfg=IntRange(min=5, max=8),
checkpoints=[
"diffusion-sdxl-dynavision-0-5-5-7.safetensors",
],
path="/tmp/adventure-images",
count=2,
path="/tmp/taleweave-images",
sizes={
"landscape": Size(width=1024, height=768),
"portrait": Size(width=768, height=1024),
"square": Size(width=768, height=768),
},
steps=IntRange(min=30, max=30),
steps=30,
),
server=ServerConfig(websocket=WebsocketServerConfig(host="localhost", port=8001)),
world=WorldConfig(

View File

@ -9,7 +9,6 @@ from random import choice, randint
from re import sub
from threading import Thread
from typing import List
from uuid import uuid4
import websocket # NOTE: websocket-client (https://github.com/websocket-client/websocket-client)
from fnvhash import fnv1a_32
@ -17,6 +16,7 @@ from jinja2 import Environment, FileSystemLoader, select_autoescape
from PIL import Image
from taleweave.context import broadcast
from taleweave.models.base import uuid
from taleweave.models.config import DEFAULT_CONFIG, RenderConfig
from taleweave.models.entity import WorldEntity
from taleweave.models.event import (
@ -35,7 +35,7 @@ from .prompt import prompt_from_entity, prompt_from_event
logger = getLogger(__name__)
server_address = environ["COMFY_API"]
client_id = uuid4().hex
client_id = uuid()
render_config: RenderConfig = DEFAULT_CONFIG.render
@ -265,12 +265,18 @@ def render_loop():
logger.info(
"using existing images for event %s: %s", event, existing_images
)
if isinstance(event, WorldEntity):
title = event.name # TODO: generate a real title
else:
title = event.type
broadcast(
RenderEvent(
paths=existing_images,
prompt="",
prompt="reusing existing images",
source=event,
title="Existing Images",
title=title,
)
)
continue
@ -288,7 +294,7 @@ def render_loop():
# render or not
if prompt:
logger.debug("rendering prompt for event %s: %s", event, prompt)
image_paths = generate_images(prompt, 2, prefix=prefix)
image_paths = generate_images(prompt, render_config.count, prefix=prefix)
broadcast(
RenderEvent(paths=image_paths, prompt=prompt, source=event, title=title)
)

View File

@ -20,7 +20,7 @@ from taleweave.context import (
subscribe,
)
from taleweave.models.config import DEFAULT_CONFIG, WebsocketServerConfig
from taleweave.models.entity import Character, Item, Room, World
from taleweave.models.entity import World, WorldEntity
from taleweave.models.event import (
GameEvent,
PlayerEvent,
@ -128,15 +128,40 @@ async def handler(websocket):
elif "become" in data:
character_name = data["become"]
if has_player(character_name):
if character_name is not None and has_player(character_name):
logger.error(
f"character {character_name} is already in use"
)
continue
# TODO: should this always remove?
player = get_player(id)
if player and isinstance(player, RemotePlayer):
remove_player(id)
# TODO: deduplicate this leaving block
if character_name is None:
player_name = get_player_name(id)
logger.info(
"disconnecting player %s from %s",
player_name,
player.name,
)
broadcast_player_event(
player.name, player_name, "leave"
)
broadcast_player_list()
character, _ = get_character_agent_for_name(player.name)
if character and player.fallback_agent:
logger.info(
"restoring LLM agent for %s", player.name
)
set_character_agent(
player.name, character, player.fallback_agent
)
continue
character, llm_agent = get_character_agent_for_name(
character_name
)
@ -263,7 +288,7 @@ socket_thread = None
def server_json(obj):
if isinstance(obj, (Character, Item, Room)):
if isinstance(obj, WorldEntity):
return obj.name
return world_json(obj)

View File

@ -22,14 +22,14 @@ rules:
type: room
temperature: hot
chance: 0.2
trigger: [adventure.systems.sim.environment_triggers:hot_room]
trigger: [taleweave.systems.sim.environment_triggers:hot_room]
- group: environment-temperature
match:
type: room
temperature: cold
chance: 0.2
trigger: [adventure.systems.sim.environment_triggers:cold_room]
trigger: [taleweave.systems.sim.environment_triggers:cold_room]
labels:
- match:

View File

@ -42,6 +42,9 @@ def initialize_weather(world: World):
room.attributes["time"] = time_of_day.name
# TODO: generate indoor/outdoor attributes
def simulate_weather(world: World, turn: int, data: None = None):
time_of_day = get_time_of_day(turn)
for room in world.rooms:

View File

@ -38,6 +38,9 @@ rules:
# weather initial state
- group: weather
match:
type: room
outdoor: true
rule: |
"weather" not in attributes
set: