use the same events on the react client
This commit is contained in:
parent
94e02ebfe1
commit
90912d2bfe
15
README.md
15
README.md
|
@ -125,19 +125,20 @@ python3 -m adventure.main \
|
|||
# --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. The world will be
|
||||
saved to a file named `worlds/outback-animals-1.json` and the state will be saved after each step to another file named
|
||||
`worlds/outback-animals-1.state.json`. The world can be stopped at any time by pressing Ctrl-C, although the step in
|
||||
progress will be lost. The saved state can be resumed and played for any number of additional steps.
|
||||
This will generate a relatively small world with 3 rooms or areas, run for 30 steps, then shut down.
|
||||
|
||||
The world will be saved to a file named `worlds/outback-animals-1.json` and the state will be saved after each step to
|
||||
another file named `worlds/outback-animals-1.state.json`. The world can be stopped at any time by pressing Ctrl-C,
|
||||
although the step in progress will be lost. The saved state can be resumed and played for any number of additional
|
||||
steps by running the server again with the same arguments.
|
||||
|
||||
> Note: `module.name:function_name` and `path/filename.yml:key` are patterns you will see repeated throughout TaleWeave AI.
|
||||
> They indicate a Python module and function within it, or a data file and key within it, respectively.
|
||||
|
||||
The `sim_systems` provide many mechanics from popular life simulations, including hunger, thirst, exhaustion, and mood.
|
||||
Custom actions and systems can be used to provide any other mechanics that are desired for your setting. The logic
|
||||
system uses a combination of Python and YAML to build complex systems that add and modify the attributes on rooms,
|
||||
characters, and items. Attributes can become sentences and fragments in the character prompt and entity description,
|
||||
allowing the logic to influence the language models.
|
||||
system uses a combination of Python and YAML to modify the prompts connected to rooms, characters, and items in the
|
||||
world, influencing the behavior of the language models.
|
||||
|
||||
## Documentation
|
||||
|
||||
|
|
|
@ -3,8 +3,9 @@ from typing import Callable, Dict, Tuple
|
|||
from packit.agent import Agent
|
||||
|
||||
from adventure.models.entity import Actor, Room, World
|
||||
from adventure.models.event import GameEvent
|
||||
|
||||
current_broadcast: Callable[[str], None] | None = None
|
||||
current_broadcast: Callable[[str | GameEvent], None] | None = None
|
||||
current_world: World | None = None
|
||||
current_room: Room | None = None
|
||||
current_actor: Actor | None = None
|
||||
|
@ -16,7 +17,7 @@ dungeon_master: Agent | None = None
|
|||
actor_agents: Dict[str, Tuple[Actor, Agent]] = {}
|
||||
|
||||
|
||||
def broadcast(message: str):
|
||||
def broadcast(message: str | GameEvent):
|
||||
if current_broadcast:
|
||||
current_broadcast(message)
|
||||
|
||||
|
|
|
@ -3,11 +3,11 @@ from os import environ
|
|||
from queue import Queue
|
||||
from re import sub
|
||||
from threading import Thread
|
||||
from typing import Tuple
|
||||
|
||||
from discord import Client, Embed, File, Intents
|
||||
|
||||
from adventure.context import (
|
||||
broadcast,
|
||||
get_actor_agent_for_name,
|
||||
get_current_world,
|
||||
set_actor_agent,
|
||||
|
@ -16,6 +16,7 @@ from adventure.models.event import (
|
|||
ActionEvent,
|
||||
GameEvent,
|
||||
GenerateEvent,
|
||||
PlayerEvent,
|
||||
PromptEvent,
|
||||
ReplyEvent,
|
||||
ResultEvent,
|
||||
|
@ -28,7 +29,7 @@ logger = getLogger(__name__)
|
|||
client = None
|
||||
|
||||
active_tasks = set()
|
||||
prompt_queue: Queue[Tuple[GameEvent, Embed | str]] = Queue()
|
||||
event_queue: Queue[GameEvent] = Queue()
|
||||
|
||||
|
||||
def remove_tags(text: str) -> str:
|
||||
|
@ -156,8 +157,7 @@ class AdventureClient(Client):
|
|||
event.prompt,
|
||||
)
|
||||
|
||||
# TODO: build an embed from the prompt
|
||||
prompt_queue.put((event, event.prompt))
|
||||
event_queue.put(event)
|
||||
return True
|
||||
|
||||
player = RemotePlayer(
|
||||
|
@ -167,19 +167,19 @@ class AdventureClient(Client):
|
|||
set_player(user_name, player)
|
||||
|
||||
logger.info(f"{user_name} has joined the game as {actor.name}!")
|
||||
await message.channel.send(
|
||||
f"{user_name} has joined the game as {actor.name}!"
|
||||
)
|
||||
return
|
||||
|
||||
if message.content.startswith("!leave"):
|
||||
# TODO: revert to LLM agent
|
||||
logger.info(f"{user_name} has left the game!")
|
||||
await message.channel.send(f"{user_name} has left the game!")
|
||||
return
|
||||
join_event = PlayerEvent("join", character_name, user_name)
|
||||
return broadcast(join_event)
|
||||
|
||||
player = get_player(user_name)
|
||||
if player and isinstance(player, RemotePlayer):
|
||||
if player:
|
||||
if message.content.startswith("!leave"):
|
||||
# TODO: check if player is playing
|
||||
# TODO: revert to LLM agent
|
||||
logger.info(f"{user_name} has left the game!")
|
||||
leave_event = PlayerEvent("leave", player.name, user_name)
|
||||
return broadcast(leave_event)
|
||||
|
||||
if isinstance(player, RemotePlayer):
|
||||
content = remove_tags(message.content)
|
||||
player.input_queue.put(content)
|
||||
logger.info(
|
||||
|
@ -194,41 +194,49 @@ class AdventureClient(Client):
|
|||
|
||||
|
||||
def launch_bot():
|
||||
def bot_main():
|
||||
global client
|
||||
|
||||
intents = Intents.default()
|
||||
# intents.message_content = True
|
||||
|
||||
client = AdventureClient(intents=intents)
|
||||
|
||||
def bot_main():
|
||||
if not client:
|
||||
raise ValueError("No Discord client available")
|
||||
|
||||
client.run(environ["DISCORD_TOKEN"])
|
||||
|
||||
def prompt_main():
|
||||
def send_main():
|
||||
from time import sleep
|
||||
|
||||
while True:
|
||||
sleep(0.1)
|
||||
if prompt_queue.empty():
|
||||
if event_queue.empty():
|
||||
# logger.debug("no events to prompt")
|
||||
continue
|
||||
|
||||
if len(active_tasks) > 0:
|
||||
logger.debug("waiting for active tasks to complete")
|
||||
continue
|
||||
|
||||
event, prompt = prompt_queue.get()
|
||||
logger.info("Prompting for event %s: %s", event, prompt)
|
||||
event = event_queue.get()
|
||||
logger.info("broadcasting event %s", event.type)
|
||||
|
||||
if client:
|
||||
prompt_task = client.loop.create_task(broadcast_event(prompt))
|
||||
active_tasks.add(prompt_task)
|
||||
prompt_task.add_done_callback(active_tasks.discard)
|
||||
event_task = client.loop.create_task(broadcast_event(event))
|
||||
active_tasks.add(event_task)
|
||||
event_task.add_done_callback(active_tasks.discard)
|
||||
else:
|
||||
logger.warning("no Discord client available")
|
||||
|
||||
bot_thread = Thread(target=bot_main, daemon=True)
|
||||
bot_thread.start()
|
||||
|
||||
prompt_thread = Thread(target=prompt_main, daemon=True)
|
||||
prompt_thread.start()
|
||||
send_thread = Thread(target=send_main, daemon=True)
|
||||
send_thread.start()
|
||||
|
||||
return [bot_thread, prompt_thread]
|
||||
return [bot_thread, send_thread]
|
||||
|
||||
|
||||
def stop_bot():
|
||||
|
@ -253,45 +261,51 @@ def get_active_channels():
|
|||
]
|
||||
|
||||
|
||||
async def broadcast_event(message: str | Embed):
|
||||
def bot_event(event: GameEvent):
|
||||
event_queue.put(event)
|
||||
|
||||
|
||||
async def broadcast_event(message: str | GameEvent):
|
||||
if not client:
|
||||
logger.warning("No Discord client available")
|
||||
logger.warning("no Discord client available")
|
||||
return
|
||||
|
||||
active_channels = get_active_channels()
|
||||
if not active_channels:
|
||||
logger.warning("No active channels")
|
||||
logger.warning("no active channels")
|
||||
return
|
||||
|
||||
for channel in active_channels:
|
||||
if isinstance(message, str):
|
||||
logger.info("Broadcasting to channel %s: %s", channel, message)
|
||||
logger.info("broadcasting to channel %s: %s", channel, message)
|
||||
await channel.send(content=message)
|
||||
elif isinstance(message, Embed):
|
||||
elif isinstance(message, GameEvent):
|
||||
embed = embed_from_event(message)
|
||||
logger.info(
|
||||
"Broadcasting to channel %s: %s - %s",
|
||||
"broadcasting to channel %s: %s - %s",
|
||||
channel,
|
||||
message.title,
|
||||
message.description,
|
||||
embed.title,
|
||||
embed.description,
|
||||
)
|
||||
await channel.send(embed=message)
|
||||
await channel.send(embed=embed)
|
||||
|
||||
|
||||
def bot_event(event: GameEvent):
|
||||
def embed_from_event(event: GameEvent) -> Embed:
|
||||
if isinstance(event, GenerateEvent):
|
||||
bot_generate(event)
|
||||
return embed_from_generate(event)
|
||||
elif isinstance(event, ResultEvent):
|
||||
bot_result(event)
|
||||
return embed_from_result(event)
|
||||
elif isinstance(event, (ActionEvent, ReplyEvent)):
|
||||
bot_action(event)
|
||||
return embed_from_action(event)
|
||||
elif isinstance(event, StatusEvent):
|
||||
pass
|
||||
return embed_from_status(event)
|
||||
elif isinstance(event, PlayerEvent):
|
||||
return embed_from_player(event)
|
||||
else:
|
||||
logger.warning("Unknown event type: %s", event)
|
||||
logger.warning("unknown event type: %s", event)
|
||||
|
||||
|
||||
def bot_action(event: ActionEvent | ReplyEvent):
|
||||
try:
|
||||
def embed_from_action(event: ActionEvent | ReplyEvent):
|
||||
action_embed = Embed(title=event.room.name, description=event.actor.name)
|
||||
|
||||
if isinstance(event, ActionEvent):
|
||||
|
@ -305,20 +319,41 @@ def bot_action(event: ActionEvent | ReplyEvent):
|
|||
else:
|
||||
action_embed.add_field(name="Message", value=event.text)
|
||||
|
||||
prompt_queue.put((event, action_embed))
|
||||
except Exception as e:
|
||||
logger.error("Failed to broadcast action: %s", e)
|
||||
return action_embed
|
||||
|
||||
|
||||
def bot_generate(event: GenerateEvent):
|
||||
prompt_queue.put((event, event.name))
|
||||
def embed_from_generate(event: GenerateEvent) -> Embed:
|
||||
generate_embed = Embed(title="Generating", description=event.name)
|
||||
return generate_embed
|
||||
|
||||
|
||||
def bot_result(event: ResultEvent):
|
||||
def embed_from_result(event: ResultEvent):
|
||||
text = event.result
|
||||
if len(text) > 1000:
|
||||
text = text[:1000] + "..."
|
||||
|
||||
result_embed = Embed(title=event.room.name, description=event.actor.name)
|
||||
result_embed.add_field(name="Result", value=text)
|
||||
prompt_queue.put((event, result_embed))
|
||||
return result_embed
|
||||
|
||||
|
||||
def embed_from_player(event: PlayerEvent):
|
||||
if event.status == "join":
|
||||
title = "New Player"
|
||||
description = f"{event.client} is now playing as {event.character}"
|
||||
else:
|
||||
title = "Player Left"
|
||||
description = f"{event.client} has left the game, {event.character} will be played by the AI"
|
||||
|
||||
player_embed = Embed(title=title, description=description)
|
||||
return player_embed
|
||||
|
||||
|
||||
def embed_from_status(event: StatusEvent):
|
||||
# TODO: add room and actor
|
||||
status_embed = Embed(
|
||||
title=event.room.name if event.room else "",
|
||||
description=event.actor.name if event.actor else "",
|
||||
)
|
||||
status_embed.add_field(name="Status", value=event.text)
|
||||
return status_embed
|
||||
|
|
|
@ -41,6 +41,15 @@ logger = logger_with_colors(__name__, level="DEBUG")
|
|||
load_dotenv(environ.get("ADVENTURE_ENV", ".env"), override=True)
|
||||
|
||||
|
||||
# start the debugger, if needed
|
||||
if environ.get("DEBUG", "false").lower() == "true":
|
||||
import debugpy
|
||||
|
||||
debugpy.listen(5679)
|
||||
logger.info("waiting for debugger to attach...")
|
||||
debugpy.wait_for_client()
|
||||
|
||||
|
||||
# main
|
||||
def parse_args():
|
||||
import argparse
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from json import loads
|
||||
from typing import Callable, Dict, Literal
|
||||
from typing import Any, Callable, Dict, List, Literal
|
||||
|
||||
from .base import dataclass
|
||||
from .entity import Actor, Item, Room, WorldEntity
|
||||
|
@ -11,7 +11,7 @@ class BaseEvent:
|
|||
A base event class.
|
||||
"""
|
||||
|
||||
event: str
|
||||
type: str
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -20,7 +20,7 @@ class GenerateEvent:
|
|||
A new entity has been generated.
|
||||
"""
|
||||
|
||||
event = "generate"
|
||||
type = "generate"
|
||||
name: str
|
||||
entity: WorldEntity | None = None
|
||||
|
||||
|
@ -39,9 +39,9 @@ class ActionEvent:
|
|||
An actor has taken an action.
|
||||
"""
|
||||
|
||||
event = "action"
|
||||
type = "action"
|
||||
action: str
|
||||
parameters: Dict[str, str]
|
||||
parameters: Dict[str, bool | float | int | str]
|
||||
|
||||
room: Room
|
||||
actor: Actor
|
||||
|
@ -65,7 +65,7 @@ class PromptEvent:
|
|||
A prompt for an actor to take an action.
|
||||
"""
|
||||
|
||||
event = "prompt"
|
||||
type = "prompt"
|
||||
prompt: str
|
||||
room: Room
|
||||
actor: Actor
|
||||
|
@ -83,7 +83,7 @@ class ReplyEvent:
|
|||
This is the non-JSON version of an ActionEvent.
|
||||
"""
|
||||
|
||||
event = "text"
|
||||
type = "reply"
|
||||
text: str
|
||||
room: Room
|
||||
actor: Actor
|
||||
|
@ -99,7 +99,7 @@ class ResultEvent:
|
|||
A result of an action.
|
||||
"""
|
||||
|
||||
event = "result"
|
||||
type = "result"
|
||||
result: str
|
||||
room: Room
|
||||
actor: Actor
|
||||
|
@ -111,19 +111,34 @@ class StatusEvent:
|
|||
A status broadcast event with text.
|
||||
"""
|
||||
|
||||
event = "status"
|
||||
type = "status"
|
||||
text: str
|
||||
room: Room | None = None
|
||||
actor: Actor | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class SnapshotEvent:
|
||||
"""
|
||||
A snapshot of the world state.
|
||||
|
||||
This one is slightly unusual, because the world has already been dumped to a JSON-compatible dictionary.
|
||||
That is especially important for the memory, which is a dictionary of actor names to lists of messages.
|
||||
"""
|
||||
|
||||
type = "snapshot"
|
||||
world: Dict[str, Any]
|
||||
memory: Dict[str, List[Any]]
|
||||
step: int
|
||||
|
||||
|
||||
@dataclass
|
||||
class PlayerEvent:
|
||||
"""
|
||||
A player joining or leaving the game.
|
||||
"""
|
||||
|
||||
event = "player"
|
||||
type = "player"
|
||||
status: Literal["join", "leave"]
|
||||
character: str
|
||||
client: str
|
||||
|
|
|
@ -30,7 +30,7 @@ if not has_dungeon_master():
|
|||
|
||||
def action_explore(direction: str) -> str:
|
||||
"""
|
||||
Explore the room in a new direction.
|
||||
Explore the room in a new direction. You can only explore directions that do not already have a portal.
|
||||
|
||||
Args:
|
||||
direction: The direction to explore: north, south, east, or west.
|
||||
|
@ -44,7 +44,7 @@ def action_explore(direction: str) -> str:
|
|||
|
||||
if direction in current_room.portals:
|
||||
dest_room = current_room.portals[direction]
|
||||
return f"You cannot explore {direction} from here, that direction leads to {dest_room}."
|
||||
return f"You cannot explore {direction} from here, that direction already leads to {dest_room}. Please use the move action to go there."
|
||||
|
||||
existing_rooms = [room.name for room in current_world.rooms]
|
||||
new_room = generate_room(
|
||||
|
|
|
@ -7,18 +7,11 @@ from typing import Literal
|
|||
from uuid import uuid4
|
||||
|
||||
import websockets
|
||||
from pydantic import RootModel
|
||||
|
||||
from adventure.context import get_actor_agent_for_name, set_actor_agent
|
||||
from adventure.models.entity import Actor, Room, World
|
||||
from adventure.models.event import (
|
||||
ActionEvent,
|
||||
GameEvent,
|
||||
GenerateEvent,
|
||||
PromptEvent,
|
||||
ReplyEvent,
|
||||
ResultEvent,
|
||||
StatusEvent,
|
||||
)
|
||||
from adventure.context import broadcast, get_actor_agent_for_name, set_actor_agent
|
||||
from adventure.models.entity import Actor, Item, Room, World
|
||||
from adventure.models.event import GameEvent, PlayerEvent, PromptEvent
|
||||
from adventure.player import (
|
||||
RemotePlayer,
|
||||
get_player,
|
||||
|
@ -46,7 +39,7 @@ async def handler(websocket):
|
|||
dumps(
|
||||
{
|
||||
"type": "prompt",
|
||||
"id": id,
|
||||
"client": id,
|
||||
"character": character,
|
||||
"prompt": prompt,
|
||||
"actions": [],
|
||||
|
@ -153,10 +146,7 @@ static_thread = None
|
|||
|
||||
|
||||
def server_json(obj):
|
||||
if isinstance(obj, Actor):
|
||||
return obj.name
|
||||
|
||||
if isinstance(obj, Room):
|
||||
if isinstance(obj, (Actor, Item, Room)):
|
||||
return obj.name
|
||||
|
||||
return world_json(obj)
|
||||
|
@ -191,68 +181,26 @@ def server_system(world: World, step: int):
|
|||
global last_snapshot
|
||||
json_state = {
|
||||
**snapshot_world(world, step),
|
||||
"type": "world",
|
||||
"type": "snapshot",
|
||||
}
|
||||
last_snapshot = send_and_append(json_state)
|
||||
|
||||
|
||||
def server_result(room: Room, actor: Actor, action: str):
|
||||
json_action = {
|
||||
"actor": actor,
|
||||
"result": action,
|
||||
"room": room,
|
||||
"type": "result",
|
||||
}
|
||||
send_and_append(json_action)
|
||||
|
||||
|
||||
def server_action(room: Room, actor: Actor, message: str):
|
||||
json_input = {
|
||||
"actor": actor,
|
||||
"input": message,
|
||||
"room": room,
|
||||
"type": "action",
|
||||
}
|
||||
send_and_append(json_input)
|
||||
|
||||
|
||||
def server_generate(event: GenerateEvent):
|
||||
json_broadcast = {
|
||||
"name": event.name,
|
||||
"type": "generate",
|
||||
}
|
||||
send_and_append(json_broadcast)
|
||||
|
||||
|
||||
def server_event(event: GameEvent):
|
||||
if isinstance(event, GenerateEvent):
|
||||
return server_generate(event)
|
||||
elif isinstance(event, ActionEvent):
|
||||
return server_action(event.room, event.actor, event.action)
|
||||
elif isinstance(event, ReplyEvent):
|
||||
return server_action(event.room, event.actor, event.text)
|
||||
elif isinstance(event, ResultEvent):
|
||||
return server_result(event.room, event.actor, event.result)
|
||||
elif isinstance(event, StatusEvent):
|
||||
pass
|
||||
else:
|
||||
logger.warning("Unknown event type: %s", event)
|
||||
json_event = RootModel[event.__class__](event).model_dump()
|
||||
json_event["type"] = event.type
|
||||
send_and_append(json_event)
|
||||
|
||||
|
||||
def player_event(character: str, id: str, event: Literal["join", "leave"]):
|
||||
json_broadcast = {
|
||||
"type": "player",
|
||||
"character": character,
|
||||
"id": id,
|
||||
"event": event,
|
||||
}
|
||||
send_and_append(json_broadcast)
|
||||
def player_event(character: str, client: str, status: Literal["join", "leave"]):
|
||||
event = PlayerEvent(status=status, character=character, client=client)
|
||||
broadcast(event)
|
||||
|
||||
|
||||
def player_list():
|
||||
players = {value: key for key, value in list_players()}
|
||||
json_broadcast = {
|
||||
"type": "players",
|
||||
"players": players,
|
||||
"players": list_players(),
|
||||
}
|
||||
# TODO: broadcast this
|
||||
send_and_append(json_broadcast)
|
||||
|
|
|
@ -29,6 +29,7 @@ from adventure.models.entity import Attributes, World
|
|||
from adventure.models.event import (
|
||||
ActionEvent,
|
||||
EventCallback,
|
||||
GameEvent,
|
||||
ReplyEvent,
|
||||
ResultEvent,
|
||||
StatusEvent,
|
||||
|
@ -70,9 +71,13 @@ def simulate_world(
|
|||
set_current_world(world)
|
||||
|
||||
# set up a broadcast callback
|
||||
def broadcast_callback(message):
|
||||
def broadcast_callback(message: str | GameEvent):
|
||||
logger.info(message)
|
||||
if isinstance(message, str):
|
||||
event = StatusEvent(text=message)
|
||||
else:
|
||||
event = message
|
||||
|
||||
for callback in callbacks:
|
||||
callback(event)
|
||||
|
||||
|
|
|
@ -119,7 +119,7 @@ export function App(props: AppProps) {
|
|||
|
||||
if (data.type === 'prompt') {
|
||||
// prompts are broadcast to all players
|
||||
if (data.id === clientId) {
|
||||
if (data.client === clientId) {
|
||||
// only notify the active player
|
||||
setActiveTurn(true);
|
||||
} else {
|
||||
|
@ -137,7 +137,7 @@ export function App(props: AppProps) {
|
|||
setHistory((prev) => prev.concat(data));
|
||||
|
||||
// if we get a world event, update the last world state
|
||||
if (data.type === 'world') {
|
||||
if (data.type === 'snapshot') {
|
||||
setWorld(data.world);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,17 +10,17 @@ export interface EventItemProps {
|
|||
focusRef?: MutableRefObject<any>;
|
||||
}
|
||||
|
||||
export function ActionItem(props: EventItemProps) {
|
||||
export function ActionEventItem(props: EventItemProps) {
|
||||
const { event } = props;
|
||||
const { actor, room, type } = event;
|
||||
const content = formatters[type](event);
|
||||
|
||||
return <ListItem alignItems="flex-start" ref={props.focusRef}>
|
||||
<ListItemAvatar>
|
||||
<Avatar alt={actor} src="/static/images/avatar/1.jpg" />
|
||||
<Avatar alt={actor.name} src="/static/images/avatar/1.jpg" />
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={room}
|
||||
primary={room.name}
|
||||
secondary={
|
||||
<React.Fragment>
|
||||
<Typography
|
||||
|
@ -29,7 +29,7 @@ export function ActionItem(props: EventItemProps) {
|
|||
variant="body2"
|
||||
color="text.primary"
|
||||
>
|
||||
{actor}
|
||||
{actor.name}
|
||||
</Typography>
|
||||
{content}
|
||||
</React.Fragment>
|
||||
|
@ -38,7 +38,7 @@ export function ActionItem(props: EventItemProps) {
|
|||
</ListItem>;
|
||||
}
|
||||
|
||||
export function WorldItem(props: EventItemProps) {
|
||||
export function SnapshotEventItem(props: EventItemProps) {
|
||||
const { event } = props;
|
||||
const { step, world } = event;
|
||||
const { theme } = world;
|
||||
|
@ -63,9 +63,9 @@ export function WorldItem(props: EventItemProps) {
|
|||
</ListItem>;
|
||||
}
|
||||
|
||||
export function MessageItem(props: EventItemProps) {
|
||||
export function ReplyEventItem(props: EventItemProps) {
|
||||
const { event } = props;
|
||||
const { message } = event;
|
||||
const { text } = event;
|
||||
|
||||
return <ListItem alignItems="flex-start" ref={props.focusRef}>
|
||||
<ListItemAvatar>
|
||||
|
@ -80,26 +80,26 @@ export function MessageItem(props: EventItemProps) {
|
|||
variant="body2"
|
||||
color="text.primary"
|
||||
>
|
||||
{message}
|
||||
{text}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
</ListItem>;
|
||||
}
|
||||
|
||||
export function PlayerItem(props: EventItemProps) {
|
||||
export function PlayerEventItem(props: EventItemProps) {
|
||||
const { event } = props;
|
||||
const { character, event: innerEvent, id } = event;
|
||||
const { character, status, client } = event;
|
||||
|
||||
let primary = '';
|
||||
let secondary = '';
|
||||
if (innerEvent === 'join') {
|
||||
if (status === 'join') {
|
||||
primary = 'New Player';
|
||||
secondary = `${id} is now playing as ${character}`;
|
||||
secondary = `${client} is now playing as ${character}`;
|
||||
}
|
||||
if (innerEvent === 'leave') {
|
||||
if (status === 'leave') {
|
||||
primary = 'Player Left';
|
||||
secondary = `${id} has left the game. ${character} is now controlled by an LLM`;
|
||||
secondary = `${client} has left the game. ${character} is now controlled by an LLM`;
|
||||
}
|
||||
|
||||
return <ListItem alignItems="flex-start" ref={props.focusRef}>
|
||||
|
@ -129,13 +129,13 @@ export function EventItem(props: EventItemProps) {
|
|||
switch (type) {
|
||||
case 'action':
|
||||
case 'result':
|
||||
return <ActionItem event={event} focusRef={props.focusRef} />;
|
||||
case 'event':
|
||||
return <MessageItem event={event} focusRef={props.focusRef} />;
|
||||
return <ActionEventItem event={event} focusRef={props.focusRef} />;
|
||||
case 'reply':
|
||||
return <ReplyEventItem event={event} focusRef={props.focusRef} />;
|
||||
case 'player':
|
||||
return <PlayerItem event={event} focusRef={props.focusRef} />;
|
||||
case 'world':
|
||||
return <WorldItem event={event} focusRef={props.focusRef} />;
|
||||
return <PlayerEventItem event={event} focusRef={props.focusRef} />;
|
||||
case 'snapshot':
|
||||
return <SnapshotEventItem event={event} focusRef={props.focusRef} />;
|
||||
default:
|
||||
return <ListItem ref={props.focusRef}>
|
||||
<ListItemText primary={`Unknown event type: ${type}`} />
|
||||
|
|
|
@ -6,19 +6,15 @@ export function formatActionName(name: string) {
|
|||
}
|
||||
|
||||
export function formatAction(data: any) {
|
||||
const actionName = formatActionName(data.function);
|
||||
const actionName = formatActionName(data.action);
|
||||
const actionParameters = data.parameters;
|
||||
|
||||
return `Action: ${actionName} - ${Object.entries(actionParameters).map(([key, value]) => `${key}: ${value}`).join(', ')}`;
|
||||
}
|
||||
|
||||
export function formatInput(data: any) {
|
||||
try {
|
||||
const action = formatAction(JSON.parse(data.input));
|
||||
const action = formatAction(data);
|
||||
return `Starting turn: ${action}`;
|
||||
} catch (err) {
|
||||
return `Error parsing input: ${err}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function formatResult(data: any) {
|
||||
|
|
Loading…
Reference in New Issue