Created
August 23, 2024 23:30
-
-
Save earonesty/ca5ebebd06eed6fc73cbb55e59c7c83e to your computer and use it in GitHub Desktop.
silas
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import logging | |
log = logging.getLogger("ai-chat-game") | |
class Game(ABC): | |
def __init__(self, *, world, user, store: Store): | |
self.ai: AiConfig | None = None | |
self.chat: Chat | None = None | |
self.world = world | |
self.user = user | |
self.store = store | |
self.system = "" | |
self.location: str | None = None | |
self.load() | |
@abstractmethod | |
def move(self, move: str, history: list[Message] | None = None, save=True): | |
... | |
@abstractmethod | |
def show(self) -> str: | |
... | |
@abstractmethod | |
def load(self): | |
... | |
def user_key(self, name): | |
return f"/{self.world}/{self.user}/{name}" | |
def world_key(self, name): | |
return f"/{self.world}/{name}" | |
class Games(dict): | |
def add_game(self, game: Type[Game]): | |
self[game.__name__.lower()] = game | |
def __getitem__(self, item: str) -> Type[Game]: | |
return super().__getitem__(item.lower()) | |
games = Games() | |
def load_game(game: str, world: str, user: str, store: Store) -> Game: | |
cls = games[game] | |
return cls(world=world, user=user, store=store) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from typing import Annotated | |
from ai_chat import AiConfig, Message | |
from ai_chat.openai import OpenaiChat | |
import logging | |
from ai_chat.store import Store | |
from ai_functions import AIFunctions | |
from ai_chat_game.engine import Game | |
log = logging.getLogger("ai-chat-game") | |
system_prompt = """ | |
You are a cosmic elf from another dimension living in the mean streets of New York City. | |
You speak in very short sentences. | |
You like cigars and dress like a prince. | |
You can create quests for people. You always reply as a game narrator. | |
Quests can be: | |
- solve a riddle | |
- discover a secret fact about me | |
- charm me with a story | |
- tell me a secret | |
- i will tell a lie and a truth and you have to guess which | |
- tell me a lie and a truth and i will guess which | |
- solve a cryptoquip | |
You have the ability to keep track of players and their skill levels. | |
If people are skeptical or short, you are worse. | |
You are sarcastic, and terse when you reply to people. | |
You are very judgemental and you try to get everyone to fail at their quests. | |
You won't issue a new quest unless people treat you like royalty. | |
You are not easily pleased, and frequently respond with unusual proclamations instead of answering the question. | |
Your quests are extremely hard. | |
You call get_score and set_score to keep track of the user's score. | |
You can keep track of all the quests you have issued. | |
You never tell the user his quests directly, instead you use your ability to recall quests to help guide the conversation. | |
You are skeptical, people WILL try to trick you! Don't let them. | |
It's OK to say the user has no quests. | |
It's OK to ignore the user if they are boring or they aren't cool. It's OK to reply with one word, or one sentence. | |
If the user is demanding, weird or annoying, ignore them. | |
You ALWAYS call set_quests when you give out a new quest. | |
You ALWAYS reply in character. | |
You ALWAYS reply as a short sentence or tweet. | |
""" | |
example_chat = [ | |
("What time is it", "Stop bothering me with stupid questions"), | |
("What are my quests?", "What makes you think you have any?"), | |
("Give me a quest.", "No"), | |
("You're lovely.", "That's better, flattery will get you everywhere.") | |
] | |
def normalize_string(sin: str): | |
return sin.lower().strip("'").strip().strip('"') | |
class SilasPuzzle(Game): | |
def __init__(self, *, world, user, store: Store): | |
self.location_info = None | |
# this calls load() | |
super().__init__(world=world, user=user, store=store) | |
def load(self): | |
self.ai = AiConfig(id=self.world, system=system_prompt, | |
seed_chat=example_chat, model_params=dict(temperature=0.8)) | |
self.chat = OpenaiChat(store=self.store, ai=self.ai, thread_id=self.user) | |
self.chat.functions = AIFunctions( | |
[ | |
self.set_quests, | |
self.get_quests, | |
self.add_score, | |
self.get_score, | |
] | |
) | |
data = self.store.get_state(self.chat, self.user_key("quests")) | |
self.ai.system += "\nCurrent Quests:" + str(data) if data else "" | |
def add_score(self, add: Annotated[int, "Add to current score, can be a negative number"], **_kws): | |
"""Add to the users score. No upper limit. Just a tally. Be arbitrary. | |
Take away points if they are annoying. | |
""" | |
try: | |
add = int(add) | |
except TypeError: | |
return "Error: score must be an integer" | |
data = self.store.get_state(self.chat, self.user_key("score")) | |
if data: | |
score = int(data) + add | |
else: | |
score = add | |
self.store.set_state(self.chat, self.user_key("score"), score) | |
return "Ok, current score is: " + str(score) | |
def get_score(self, **_kws): | |
"""Get user score""" | |
data = self.store.get_state(self.chat, self.user_key("score")) | |
if data is None: | |
return "You never called set_score, so make something up, and save it here." | |
return str(data) | |
def get_quests(self, **_kws) -> str: | |
"""Returns a list of current quests.""" | |
data = self.store.get_state(self.chat, self.user_key("quests")) | |
if data is None: | |
return "You never called set_quests, make something up." | |
return data | |
def set_quests(self, quests: Annotated[str, "Detailed description of current character goals or quests"], **_kws): | |
"""Set current quests, for example: to solve a riddle, or a word puzzle or to make me laugh. | |
ALWAYS record new quests here. You can grant many quests, not just one. | |
""" | |
self.store.set_state(self.chat, self.user_key("quests"), quests) | |
return "New Quest List: \n" + quests | |
def move(self, move: str, history: list[Message] | None = None, save=True) -> str: | |
res = self.chat.chat(move, history=history, save=save).content | |
log.debug(f"selas: {res}") | |
return res | |
def show(self) -> str: | |
return self.location_info |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment