1
0
Fork 0

use LLM agent as fallback if player does not respond, fix various client bugs

This commit is contained in:
Sean Sube 2024-05-05 14:11:41 -05:00
parent 580076335f
commit f2185344b1
Signed by: ssube
GPG Key ID: 3EED7B957D362AF1
5 changed files with 25 additions and 20 deletions

View File

@ -5,6 +5,7 @@ from readline import add_history
from typing import Any, Callable, Dict, List, Sequence
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage
from packit.agent import Agent
from packit.utils import could_be_json
logger = getLogger(__name__)
@ -111,13 +112,15 @@ class LocalPlayer(BasePlayer):
class RemotePlayer(BasePlayer):
fallback_agent: Agent | None
input_queue: Queue[str]
send_prompt: Callable[[str, str], bool]
def __init__(
self, name: str, backstory: str, send_prompt: Callable[[str, str], bool]
self, name: str, backstory: str, send_prompt: Callable[[str, str], bool], fallback_agent = None
) -> None:
super().__init__(name, backstory)
self.fallback_agent = fallback_agent
self.input_queue = Queue()
self.send_prompt = send_prompt
@ -138,4 +141,7 @@ class RemotePlayer(BasePlayer):
except Exception:
logger.exception("error getting reply from remote player")
if self.fallback_agent:
return self.fallback_agent(prompt, **kwargs)
return ""

View File

@ -7,7 +7,6 @@ from typing import Dict
from uuid import uuid4
import websockets
from packit.agent import Agent
from adventure.context import get_actor_agent_for_name, set_actor_agent_for_name
from adventure.models import Actor, Room, World
@ -18,7 +17,6 @@ logger = getLogger(__name__)
connected = set()
characters: Dict[str, RemotePlayer] = {}
previous_agents: Dict[str, Agent] = {}
recent_events = deque(maxlen=100)
recent_world = None
@ -63,7 +61,7 @@ async def handler(websocket):
try:
# if this socket is attached to a character and that character's turn is active, wait for input
message = await websocket.recv()
logger.info(f"Received message: {message}")
logger.info(f"Received message for {id}: {message}")
try:
data = loads(message)
@ -86,13 +84,12 @@ async def handler(websocket):
continue
# player_name = data["player"]
player = RemotePlayer(actor.name, actor.backstory, sync_turn)
player = RemotePlayer(actor.name, actor.backstory, sync_turn, fallback_agent=llm_agent)
characters[id] = player
logger.info(f"Client {websocket} is now character {character_name}")
# swap out the LLM agent
set_actor_agent_for_name(actor.name, actor, player)
previous_agents[actor.name] = llm_agent
# notify all clients that this character is now active
send_and_append(
@ -117,7 +114,7 @@ async def handler(websocket):
actor, _ = get_actor_agent_for_name(player.name)
if actor:
set_actor_agent_for_name(player.name, actor, previous_agents[player.name])
set_actor_agent_for_name(player.name, actor, player.fallback_agent)
logger.info("Client disconnected")

View File

@ -82,8 +82,7 @@ export function App(props: AppProps) {
const { lastMessage, readyState, sendMessage } = useWebSocket(props.socketUrl);
function setPlayer(actor: Maybe<Actor>) {
setCharacter(actor);
// do not setCharacter until the server confirms the player change
if (doesExist(actor)) {
sendMessage(JSON.stringify({ type: 'player', become: actor.name }));
}
@ -92,6 +91,7 @@ export function App(props: AppProps) {
function sendInput(input: string) {
if (doesExist(character)) {
sendMessage(JSON.stringify({ type: 'input', input }));
setActiveTurn(false);
}
}

View File

@ -25,15 +25,17 @@ export function PlayerPanel(props: PlayerPanelProps) {
return <Card>
<CardContent>
{activeTurn && <Alert severity="warning">It's your turn!</Alert>}
<Typography variant="h6">Playing as: {actor.name}</Typography>
<Typography variant="body1">{actor.backstory}</Typography>
<Stack direction="row" spacing={2}>
<TextField label="Input" variant="outlined" fullWidth value={input} onChange={(event) => setInput(event.target.value)} />
<Button variant="contained" onClick={() => {
setInput('');
sendInput(input);
}}>Send</Button>
<Stack direction="column" spacing={2}>
{activeTurn && <Alert severity="warning">It's your turn!</Alert>}
<Typography variant="h6">Playing as: {actor.name}</Typography>
<Typography variant="body1">{actor.backstory}</Typography>
<Stack direction="row" spacing={2}>
<TextField label="Input" variant="outlined" fullWidth value={input} onChange={(event) => setInput(event.target.value)} />
<Button variant="contained" onClick={() => {
setInput('');
sendInput(input);
}}>Send</Button>
</Stack>
</Stack>
</CardContent>
</Card>;

View File

@ -59,7 +59,7 @@ export function ItemItem(props: { item: Item } & BaseEntityItemProps) {
export function ActorItem(props: { actor: Actor } & BaseEntityItemProps) {
const { actor, activeCharacter, setDetails, setPlayer } = props;
const active = doesExist(activeCharacter) && actor === activeCharacter;
const active = doesExist(activeCharacter) && actor.name === activeCharacter.name;
const label = formatLabel(actor.name, active);
let playButton;
@ -79,7 +79,7 @@ export function ActorItem(props: { actor: Actor } & BaseEntityItemProps) {
export function RoomItem(props: { room: Room } & BaseEntityItemProps) {
const { room, activeCharacter, setDetails, setPlayer } = props;
const active = doesExist(activeCharacter) && room.actors.some((it) => it === activeCharacter);
const active = doesExist(activeCharacter) && room.actors.some((it) => it.name === activeCharacter.name);
const label = formatLabel(room.name, active);
return <TreeItem itemId={room.name} label={label}>