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 -r requirements/base.txt
RUN pip install --no-cache-dir --index-url https://test.pypi.org/simple/ packit_llm==0.1.0 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 ```bash
# Start the TaleWeave AI engine # Start the TaleWeave AI engine
python3 -m adventure.main \ python3 -m taleweave.main \
--world worlds/outback-animals-1 \ --world worlds/outback-animals-1 \
--world-prompt ./adventure/prompts.yml:outback-animals \ --world-prompt ./taleweave/prompts.yml:outback-animals \
--discord=true \ --discord=true \
--server=true \ --server=true \
--rooms 3 \ --rooms 3 \
--turns 30 \ --turns 30 \
--optional-actions=true \ --optional-actions=true \
--actions adventure.sim_systems:init_actions \ --actions taleweave.systems.sim:init_actions \
--systems adventure.sim_systems:init_logic --systems taleweave.systems.sim:init_logic
# --actions adventure.custom_systems:init_actions
# --systems adventure.custom_systems:init_logic
``` ```
This will generate a relatively small world with 3 rooms or areas, run for 30 steps, then shut down. 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>) { function setPlayer(character: Maybe<Character>) {
// do not call setCharacter until the server confirms the player change // 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)) { if (doesExist(character)) {
sendMessage(JSON.stringify({ type: 'player', become: character.name })); become = character.name;
} }
sendMessage(JSON.stringify({ type: 'player', become }));
} }
function sendInput(input: string) { function sendInput(input: string) {
@ -96,10 +99,20 @@ export function App(props: AppProps) {
setActiveTurn(event.client === clientId); setActiveTurn(event.client === clientId);
break; break;
case 'player': case 'player':
if (event.status === 'join' && doesExist(world) && event.client === clientId) { if (doesExist(world) && event.client === clientId) {
const { character: characterName } = event; switch (event.status) {
const character = world.rooms.flatMap((room) => room.characters).find((a) => a.name === characterName); case 'join': {
setCharacter(character); 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; break;
case 'players': case 'players':

View File

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

View File

@ -81,14 +81,18 @@ class AdventureClient(Client):
channel = message.channel channel = message.channel
user_name = author.name # include nick 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() world = get_current_world()
if world: if world:
active_world = f"Active world: {world.name} (theme: {world.theme})" active_world = f"Active world: {world.name} (theme: {world.theme})"
else: else:
active_world = "No active world" 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 return
if message.content.startswith("!help"): if message.content.startswith("!help"):

View File

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

View File

@ -286,6 +286,7 @@ def generate_character(
dest_room: Room, dest_room: Room,
additional_prompt: str = "", additional_prompt: str = "",
detail_prompt: str = "", detail_prompt: str = "",
add_to_world_order: bool = True,
) -> Character: ) -> Character:
existing_characters = [character.name for character in list_characters(world)] + [ existing_characters = [character.name for character in list_characters(world)] + [
character.name for character in list_characters_in_room(dest_room) character.name for character in list_characters_in_room(dest_room)
@ -350,6 +351,10 @@ def generate_character(
except Exception: except Exception:
logger.exception("error generating item") 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 return character

View File

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

View File

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

View File

@ -20,7 +20,7 @@ from taleweave.context import (
subscribe, subscribe,
) )
from taleweave.models.config import DEFAULT_CONFIG, WebsocketServerConfig 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 ( from taleweave.models.event import (
GameEvent, GameEvent,
PlayerEvent, PlayerEvent,
@ -128,14 +128,39 @@ async def handler(websocket):
elif "become" in data: elif "become" in data:
character_name = data["become"] character_name = data["become"]
if has_player(character_name): if character_name is not None and has_player(character_name):
logger.error( logger.error(
f"character {character_name} is already in use" f"character {character_name} is already in use"
) )
continue continue
# TODO: should this always remove? player = get_player(id)
remove_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, llm_agent = get_character_agent_for_name(
character_name character_name
@ -263,7 +288,7 @@ socket_thread = None
def server_json(obj): def server_json(obj):
if isinstance(obj, (Character, Item, Room)): if isinstance(obj, WorldEntity):
return obj.name return obj.name
return world_json(obj) return world_json(obj)

View File

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

View File

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

View File

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