add new characters to turn order, fix leaving, move image count to config
This commit is contained in:
parent
fea124b3f3
commit
16e7243da1
|
@ -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"]
|
||||
|
|
11
README.md
11
README.md
|
@ -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.
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -38,6 +38,9 @@ rules:
|
|||
|
||||
# weather initial state
|
||||
- group: weather
|
||||
match:
|
||||
type: room
|
||||
outdoor: true
|
||||
rule: |
|
||||
"weather" not in attributes
|
||||
set:
|
||||
|
|
Loading…
Reference in New Issue