ai-plays-jackbox 0.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ai_plays_jackbox/__init__.py +0 -0
- ai_plays_jackbox/bot/__init__.py +0 -0
- ai_plays_jackbox/bot/bot_base.py +219 -0
- ai_plays_jackbox/bot/bot_factory.py +31 -0
- ai_plays_jackbox/bot/bot_personality.py +111 -0
- ai_plays_jackbox/bot/jackbox5/__init__.py +0 -0
- ai_plays_jackbox/bot/jackbox5/bot_base.py +26 -0
- ai_plays_jackbox/bot/jackbox5/mad_verse_city.py +121 -0
- ai_plays_jackbox/bot/jackbox5/patently_stupid.py +168 -0
- ai_plays_jackbox/bot/jackbox6/__init__.py +0 -0
- ai_plays_jackbox/bot/jackbox6/bot_base.py +20 -0
- ai_plays_jackbox/bot/jackbox6/dictionarium.py +105 -0
- ai_plays_jackbox/bot/jackbox6/joke_boat.py +105 -0
- ai_plays_jackbox/bot/jackbox7/__init__.py +0 -0
- ai_plays_jackbox/bot/jackbox7/bot_base.py +20 -0
- ai_plays_jackbox/bot/jackbox7/quiplash3.py +108 -0
- ai_plays_jackbox/bot/jackbox8/__init__.py +0 -0
- ai_plays_jackbox/bot/jackbox8/bot_base.py +20 -0
- ai_plays_jackbox/bot/jackbox8/job_job.py +205 -0
- ai_plays_jackbox/bot/standalone/__init__.py +0 -0
- ai_plays_jackbox/bot/standalone/drawful2.py +159 -0
- ai_plays_jackbox/cli/__init__.py +0 -0
- ai_plays_jackbox/cli/main.py +117 -0
- ai_plays_jackbox/constants.py +4 -0
- ai_plays_jackbox/llm/__init__.py +1 -0
- ai_plays_jackbox/llm/chat_model.py +39 -0
- ai_plays_jackbox/llm/chat_model_factory.py +35 -0
- ai_plays_jackbox/llm/gemini_model.py +86 -0
- ai_plays_jackbox/llm/ollama_model.py +53 -0
- ai_plays_jackbox/llm/openai_model.py +86 -0
- ai_plays_jackbox/room/__init__.py +0 -0
- ai_plays_jackbox/room/room.py +87 -0
- ai_plays_jackbox/run.py +23 -0
- ai_plays_jackbox/scripts/lint.py +18 -0
- ai_plays_jackbox/ui/__init__.py +0 -0
- ai_plays_jackbox/ui/main.py +12 -0
- ai_plays_jackbox/ui/startup.py +271 -0
- ai_plays_jackbox-0.4.1.dist-info/METADATA +158 -0
- ai_plays_jackbox-0.4.1.dist-info/RECORD +42 -0
- ai_plays_jackbox-0.4.1.dist-info/WHEEL +4 -0
- ai_plays_jackbox-0.4.1.dist-info/entry_points.txt +4 -0
- ai_plays_jackbox-0.4.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
|
|
3
|
+
from ai_plays_jackbox.bot.bot_base import JackBoxBotBase
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class JackBox6BotBase(JackBoxBotBase, ABC):
|
|
7
|
+
|
|
8
|
+
@property
|
|
9
|
+
def _player_operation_key(self):
|
|
10
|
+
return f"bc:customer:"
|
|
11
|
+
|
|
12
|
+
def _is_player_operation_key(self, operation_key: str) -> bool:
|
|
13
|
+
return self._player_operation_key in operation_key
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def _room_operation_key(self):
|
|
17
|
+
return "bc:room"
|
|
18
|
+
|
|
19
|
+
def _is_room_operation_key(self, operation_key: str) -> bool:
|
|
20
|
+
return operation_key == self._room_operation_key
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import random
|
|
2
|
+
|
|
3
|
+
from loguru import logger
|
|
4
|
+
|
|
5
|
+
from ai_plays_jackbox.bot.jackbox6.bot_base import JackBox6BotBase
|
|
6
|
+
|
|
7
|
+
_DEFINITION_PROMPT_TEMPLATE = """
|
|
8
|
+
You are playing Dictionarium.
|
|
9
|
+
|
|
10
|
+
{prompt}
|
|
11
|
+
|
|
12
|
+
When generating your response, follow these rules:
|
|
13
|
+
- Your personality is: {personality}
|
|
14
|
+
- You response must be {max_length} characters or less.
|
|
15
|
+
- Do not include quotes in your response.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
_SYNONYM_PROMPT_TEMPLATE = """
|
|
19
|
+
You are playing Dictionarium.
|
|
20
|
+
|
|
21
|
+
{prompt}
|
|
22
|
+
|
|
23
|
+
When generating your response, follow these rules:
|
|
24
|
+
- Your personality is: {personality}
|
|
25
|
+
- You response must be {max_length} characters or less.
|
|
26
|
+
- Do not include quotes in your response.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
_SENTENCE_PROMPT_TEMPLATE = """
|
|
30
|
+
You are playing Dictionarium. You need to use a made up word in a sentence.
|
|
31
|
+
|
|
32
|
+
{prompt}
|
|
33
|
+
|
|
34
|
+
When generating your response, follow these rules:
|
|
35
|
+
- Your personality is: {personality}
|
|
36
|
+
- You response must be {max_length} characters or less.
|
|
37
|
+
- Do not include quotes in your response.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class DictionariumBot(JackBox6BotBase):
|
|
42
|
+
def __init__(self, *args, **kwargs):
|
|
43
|
+
super().__init__(*args, **kwargs)
|
|
44
|
+
|
|
45
|
+
def _handle_welcome(self, data: dict):
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
def _handle_player_operation(self, data: dict):
|
|
49
|
+
if not data:
|
|
50
|
+
return
|
|
51
|
+
room_state = data.get("state", None)
|
|
52
|
+
if not room_state:
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
prompt = data.get("prompt", {})
|
|
56
|
+
prompt_html = prompt.get("html", "")
|
|
57
|
+
clean_prompt = self._html_to_text(prompt_html)
|
|
58
|
+
max_length = data.get("maxLength", 150)
|
|
59
|
+
|
|
60
|
+
match room_state:
|
|
61
|
+
case "EnterSingleText":
|
|
62
|
+
entry = data.get("entry", None)
|
|
63
|
+
entry_id = data.get("entryId", "")
|
|
64
|
+
print(data)
|
|
65
|
+
if not entry:
|
|
66
|
+
if entry_id == "Definition":
|
|
67
|
+
logger.info("Generating definition...")
|
|
68
|
+
template = _DEFINITION_PROMPT_TEMPLATE
|
|
69
|
+
elif entry_id == "Synonym":
|
|
70
|
+
logger.info("Generating synonym...")
|
|
71
|
+
template = _SYNONYM_PROMPT_TEMPLATE
|
|
72
|
+
elif entry_id == "Sentence":
|
|
73
|
+
logger.info("Generating sentence...")
|
|
74
|
+
template = _SENTENCE_PROMPT_TEMPLATE
|
|
75
|
+
else:
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
formatted_prompt = template.format(
|
|
79
|
+
personality=self._personality,
|
|
80
|
+
prompt=clean_prompt,
|
|
81
|
+
max_length=max_length,
|
|
82
|
+
)
|
|
83
|
+
submission = self._chat_model.generate_text(
|
|
84
|
+
formatted_prompt,
|
|
85
|
+
"",
|
|
86
|
+
temperature=self._chat_model._chat_model_temperature,
|
|
87
|
+
top_p=self._chat_model._chat_model_top_p,
|
|
88
|
+
)
|
|
89
|
+
submission = submission[: max_length - 1]
|
|
90
|
+
self._client_send({"action": "write", "entry": submission})
|
|
91
|
+
|
|
92
|
+
case "MakeSingleChoice":
|
|
93
|
+
choice_type = data.get("choiceType", "")
|
|
94
|
+
if (
|
|
95
|
+
choice_type == "ChooseDefinition"
|
|
96
|
+
or choice_type == "ChooseSynonym"
|
|
97
|
+
or choice_type == "ChooseSentence"
|
|
98
|
+
):
|
|
99
|
+
choices: list[dict] = data.get("choices", [])
|
|
100
|
+
choice_indexes = [i for i in range(0, len(choices))]
|
|
101
|
+
selected_choice = random.choice(choice_indexes)
|
|
102
|
+
self._client_send({"action": "choose", "choice": selected_choice})
|
|
103
|
+
|
|
104
|
+
def _handle_room_operation(self, data: dict):
|
|
105
|
+
pass
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import random
|
|
2
|
+
|
|
3
|
+
from loguru import logger
|
|
4
|
+
|
|
5
|
+
from ai_plays_jackbox.bot.jackbox6.bot_base import JackBox6BotBase
|
|
6
|
+
|
|
7
|
+
_TOPIC_PROMPT_TEMPLATE = """
|
|
8
|
+
You are playing Joke Boat.
|
|
9
|
+
|
|
10
|
+
You are being asked to come up with a topic that is {placeholder}.
|
|
11
|
+
|
|
12
|
+
When generating your response, follow these rules:
|
|
13
|
+
- Your personality is: {personality}
|
|
14
|
+
- You response must be {max_length} characters or less
|
|
15
|
+
- Your response should be a single word.
|
|
16
|
+
- Do not include quotes in your response or any newlines, just the response itself
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
_PUNCHLINE_INSTRUCTIONS_TEMPLATE = """
|
|
20
|
+
You are playing Joke Boat. You need to fill in the given prompt with a punchline.
|
|
21
|
+
|
|
22
|
+
When generating your response, follow these rules:
|
|
23
|
+
- Your personality is: {personality}
|
|
24
|
+
- You response must be {max_length} characters or less
|
|
25
|
+
- Do not include quotes in your response or any newlines, just the response itself
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class JokeBoatBot(JackBox6BotBase):
|
|
30
|
+
def __init__(self, *args, **kwargs):
|
|
31
|
+
super().__init__(*args, **kwargs)
|
|
32
|
+
|
|
33
|
+
def _handle_welcome(self, data: dict):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
def _handle_player_operation(self, data: dict):
|
|
37
|
+
if not data:
|
|
38
|
+
return
|
|
39
|
+
room_state = data.get("state", None)
|
|
40
|
+
if not room_state:
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
prompt = data.get("prompt", {})
|
|
44
|
+
prompt_html = prompt.get("html", "")
|
|
45
|
+
clean_prompt = self._html_to_text(prompt_html)
|
|
46
|
+
|
|
47
|
+
match room_state:
|
|
48
|
+
case "MakeSingleChoice":
|
|
49
|
+
choices: list[dict] = data.get("choices", [])
|
|
50
|
+
choice_type = data.get("choiceType", "")
|
|
51
|
+
choice_indexes = [i for i in range(0, len(choices))]
|
|
52
|
+
selected_choice = random.choice(choice_indexes)
|
|
53
|
+
|
|
54
|
+
if choice_type == "ChooseAuthorReady":
|
|
55
|
+
selected_choice = 1
|
|
56
|
+
if choice_type == "Skip":
|
|
57
|
+
selected_choice = 0
|
|
58
|
+
|
|
59
|
+
if data.get("chosen", None) is None:
|
|
60
|
+
self._client_send({"action": "choose", "choice": selected_choice})
|
|
61
|
+
|
|
62
|
+
case "EnterSingleText":
|
|
63
|
+
if "Write as many topics as you can." in clean_prompt:
|
|
64
|
+
placeholder = data.get("placeholder", "")
|
|
65
|
+
max_length = data.get("maxLength", 42)
|
|
66
|
+
topic = self._generate_topic(placeholder, max_length)
|
|
67
|
+
self._client_send({"action": "write", "entry": topic})
|
|
68
|
+
if "Write your punchline" in clean_prompt or "Write the punchline to this joke" in clean_prompt:
|
|
69
|
+
max_length = data.get("maxLength", 80)
|
|
70
|
+
punchline = self._generate_punchline(clean_prompt, max_length)
|
|
71
|
+
self._client_send({"action": "write", "entry": punchline})
|
|
72
|
+
|
|
73
|
+
def _handle_room_operation(self, data: dict):
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
def _generate_topic(self, placeholder: str, max_length: int) -> str:
|
|
77
|
+
logger.info("Generating topic...")
|
|
78
|
+
formatted_prompt = _TOPIC_PROMPT_TEMPLATE.format(
|
|
79
|
+
personality=self._personality,
|
|
80
|
+
placeholder=placeholder,
|
|
81
|
+
max_length=max_length,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
topic = self._chat_model.generate_text(
|
|
85
|
+
formatted_prompt,
|
|
86
|
+
"",
|
|
87
|
+
temperature=self._chat_model._chat_model_temperature,
|
|
88
|
+
top_p=self._chat_model._chat_model_top_p,
|
|
89
|
+
)
|
|
90
|
+
return topic[:max_length]
|
|
91
|
+
|
|
92
|
+
def _generate_punchline(self, prompt: str, max_length: int) -> str:
|
|
93
|
+
logger.info("Generating punchline...")
|
|
94
|
+
formatted_instructions = _PUNCHLINE_INSTRUCTIONS_TEMPLATE.format(
|
|
95
|
+
personality=self._personality,
|
|
96
|
+
prompt=prompt,
|
|
97
|
+
max_length=max_length,
|
|
98
|
+
)
|
|
99
|
+
punchline = self._chat_model.generate_text(
|
|
100
|
+
prompt,
|
|
101
|
+
formatted_instructions,
|
|
102
|
+
temperature=self._chat_model._chat_model_temperature,
|
|
103
|
+
top_p=self._chat_model._chat_model_top_p,
|
|
104
|
+
)
|
|
105
|
+
return punchline[:max_length]
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
|
|
3
|
+
from ai_plays_jackbox.bot.bot_base import JackBoxBotBase
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class JackBox7BotBase(JackBoxBotBase, ABC):
|
|
7
|
+
|
|
8
|
+
@property
|
|
9
|
+
def _player_operation_key(self) -> str:
|
|
10
|
+
return f"player:{self._player_id}"
|
|
11
|
+
|
|
12
|
+
def _is_player_operation_key(self, operation_key: str) -> bool:
|
|
13
|
+
return operation_key == self._player_operation_key
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def _room_operation_key(self) -> str:
|
|
17
|
+
return "room"
|
|
18
|
+
|
|
19
|
+
def _is_room_operation_key(self, operation_key: str) -> bool:
|
|
20
|
+
return operation_key == self._room_operation_key
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import random
|
|
2
|
+
|
|
3
|
+
from loguru import logger
|
|
4
|
+
|
|
5
|
+
from ai_plays_jackbox.bot.jackbox7.bot_base import JackBox7BotBase
|
|
6
|
+
|
|
7
|
+
_QUIP_PROMPT_INSTRUCTIONS_TEMPLATE = """
|
|
8
|
+
You are playing Quiplash 3. You need to fill in the given prompt.
|
|
9
|
+
|
|
10
|
+
When generating your response, follow these rules:
|
|
11
|
+
- Your personality is: {personality}
|
|
12
|
+
- You response must be 45 letters or less.
|
|
13
|
+
- Do not include quotes in your response.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
_FINAL_QUIP_PROMPT_INSTRUCTIONS_TEMPLATE = """
|
|
17
|
+
You are playing Quiplash 3 and it is the final round. The prompt will include three blanks, all of which you need to fill in.
|
|
18
|
+
|
|
19
|
+
When generating your response, follow these rules:
|
|
20
|
+
- Your personality is: {personality}
|
|
21
|
+
- Separate your answers by the character '|', for example 'Apple|Orange|Banana'.
|
|
22
|
+
- Each answer must be 45 letters or less.
|
|
23
|
+
- Do not include quotes in your response.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
_QUIP_CHOICE_PROMPT_INSTRUCTIONS_TEMPLATE = """
|
|
27
|
+
You are playing Quiplash 3 and you need to vote for your favorite response to the prompt "{prompt}".
|
|
28
|
+
Choose your favorite by responding with the number next to your choice. Only respond with the number and nothing else.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Quiplash3Bot(JackBox7BotBase):
|
|
33
|
+
_selected_avatar: bool = False
|
|
34
|
+
|
|
35
|
+
def __init__(self, *args, **kwargs):
|
|
36
|
+
super().__init__(*args, **kwargs)
|
|
37
|
+
|
|
38
|
+
def _handle_welcome(self, data: dict):
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
def _handle_player_operation(self, data: dict):
|
|
42
|
+
if not data:
|
|
43
|
+
return
|
|
44
|
+
room_state = data.get("state", None)
|
|
45
|
+
if not room_state:
|
|
46
|
+
return
|
|
47
|
+
prompt = data.get("prompt")
|
|
48
|
+
prompt_text = self._html_to_text(prompt.get("html", "")) if prompt is not None else ""
|
|
49
|
+
text_key = data.get("textKey", "")
|
|
50
|
+
match room_state:
|
|
51
|
+
case "EnterSingleText":
|
|
52
|
+
if not data["entry"]:
|
|
53
|
+
quip = self._generate_quip(prompt_text)
|
|
54
|
+
self._send_ws("text/update", {"key": text_key, "val": quip})
|
|
55
|
+
case "EnterTextList":
|
|
56
|
+
if not data["entries"]:
|
|
57
|
+
quip = self._generate_quip(prompt_text, final_round=True)
|
|
58
|
+
self._send_ws("text/update", {"key": text_key, "val": "\n".join(quip.split("|"))})
|
|
59
|
+
case "MakeSingleChoice":
|
|
60
|
+
choice = self._choose_favorite(prompt_text, data["choices"])
|
|
61
|
+
self._client_send({"action": "choose", "choice": choice})
|
|
62
|
+
|
|
63
|
+
def _handle_room_operation(self, data: dict):
|
|
64
|
+
if self._selected_avatar:
|
|
65
|
+
return
|
|
66
|
+
available_characters = [c["name"] for c in data["characters"] if c["available"]]
|
|
67
|
+
selected_character = random.choice(available_characters)
|
|
68
|
+
self._client_send({"action": "avatar", "name": selected_character})
|
|
69
|
+
self._selected_avatar = True
|
|
70
|
+
|
|
71
|
+
def _generate_quip(self, prompt: str, final_round: bool = False) -> str:
|
|
72
|
+
max_tokens = 10
|
|
73
|
+
instructions = _QUIP_PROMPT_INSTRUCTIONS_TEMPLATE.format(personality=self._personality)
|
|
74
|
+
if final_round:
|
|
75
|
+
max_tokens = 32
|
|
76
|
+
instructions = _FINAL_QUIP_PROMPT_INSTRUCTIONS_TEMPLATE.format(personality=self._personality)
|
|
77
|
+
quip = self._chat_model.generate_text(
|
|
78
|
+
prompt,
|
|
79
|
+
instructions,
|
|
80
|
+
max_tokens=max_tokens,
|
|
81
|
+
temperature=self._chat_model._chat_model_temperature,
|
|
82
|
+
top_p=self._chat_model._chat_model_top_p,
|
|
83
|
+
)
|
|
84
|
+
return quip
|
|
85
|
+
|
|
86
|
+
def _choose_favorite(self, prompt: str, choices: list[dict]) -> int:
|
|
87
|
+
choices_str = "\n".join([f"{i+1}. {v['html']}" for i, v in enumerate(choices)])
|
|
88
|
+
instructions = _QUIP_CHOICE_PROMPT_INSTRUCTIONS_TEMPLATE.format(prompt=prompt)
|
|
89
|
+
response = self._chat_model.generate_text(
|
|
90
|
+
f"Vote for your favorite response. Your options are: {choices_str}",
|
|
91
|
+
instructions,
|
|
92
|
+
max_tokens=1,
|
|
93
|
+
)
|
|
94
|
+
try:
|
|
95
|
+
choosen_prompt = int(response)
|
|
96
|
+
except ValueError:
|
|
97
|
+
logger.warning(f"Can't choose favorite since response was not an int: {response}")
|
|
98
|
+
return self._choose_random_favorite(choices)
|
|
99
|
+
|
|
100
|
+
if choosen_prompt < 1 or choosen_prompt > len(choices):
|
|
101
|
+
logger.warning(f"Can't choose favorite since response was not a valid value: {response}")
|
|
102
|
+
return self._choose_random_favorite(choices)
|
|
103
|
+
else:
|
|
104
|
+
return choosen_prompt - 1
|
|
105
|
+
|
|
106
|
+
def _choose_random_favorite(self, choices: list[dict]) -> int:
|
|
107
|
+
choices_as_ints = [i for i in range(0, len(choices))]
|
|
108
|
+
return random.choice(choices_as_ints)
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
|
|
3
|
+
from ai_plays_jackbox.bot.bot_base import JackBoxBotBase
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class JackBox8BotBase(JackBoxBotBase, ABC):
|
|
7
|
+
|
|
8
|
+
@property
|
|
9
|
+
def _player_operation_key(self) -> str:
|
|
10
|
+
return f"player:{self._player_id}"
|
|
11
|
+
|
|
12
|
+
def _is_player_operation_key(self, operation_key: str) -> bool:
|
|
13
|
+
return operation_key == self._player_operation_key
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def _room_operation_key(self) -> str:
|
|
17
|
+
return "room"
|
|
18
|
+
|
|
19
|
+
def _is_room_operation_key(self, operation_key: str) -> bool:
|
|
20
|
+
return operation_key == self._room_operation_key
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import random
|
|
2
|
+
import string
|
|
3
|
+
|
|
4
|
+
from loguru import logger
|
|
5
|
+
|
|
6
|
+
from ai_plays_jackbox.bot.jackbox8.bot_base import JackBox8BotBase
|
|
7
|
+
|
|
8
|
+
_RESPONSE_PROMPT_TEMPLATE = """
|
|
9
|
+
You are playing Job Job. You need response to the given prompt.
|
|
10
|
+
|
|
11
|
+
When generating your response, follow these rules:
|
|
12
|
+
- Your personality is: {personality}
|
|
13
|
+
- Your response must be {max_length} letters or less.
|
|
14
|
+
- Your response must have a minimum of {min_words} words.
|
|
15
|
+
- Do not include quotes in your response.
|
|
16
|
+
|
|
17
|
+
{instruction}
|
|
18
|
+
|
|
19
|
+
Your prompt is:
|
|
20
|
+
|
|
21
|
+
{prompt}
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
_COMPOSITION_PROMPT_TEMPLATE = """
|
|
25
|
+
You are playing Job Job. You must create a response to a interview question using only specific words given.
|
|
26
|
+
|
|
27
|
+
When generating your response, follow these rules:
|
|
28
|
+
- Your personality is: {personality}
|
|
29
|
+
- Your response must only use the allowed words or characters, nothing else
|
|
30
|
+
- If you decide to use a character, you must have it separated by a space from any words
|
|
31
|
+
- You can select a maximum of {max_words} words
|
|
32
|
+
|
|
33
|
+
Your interview question is:
|
|
34
|
+
|
|
35
|
+
{prompt}
|
|
36
|
+
|
|
37
|
+
Your allowed words or characters are:
|
|
38
|
+
|
|
39
|
+
{all_possible_words_str}
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class JobJobBot(JackBox8BotBase):
|
|
44
|
+
def __init__(self, *args, **kwargs):
|
|
45
|
+
super().__init__(*args, **kwargs)
|
|
46
|
+
|
|
47
|
+
def _handle_welcome(self, data: dict):
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
def _handle_player_operation(self, data: dict):
|
|
51
|
+
if not data:
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
kind = data.get("kind", "")
|
|
55
|
+
has_controls = data.get("hasControls", False)
|
|
56
|
+
response_key = data.get("responseKey", "")
|
|
57
|
+
done_key = data.get("doneKey", "")
|
|
58
|
+
|
|
59
|
+
if has_controls:
|
|
60
|
+
if "skip:" in response_key:
|
|
61
|
+
self._object_update(response_key, {"action": "skip"})
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
match kind:
|
|
65
|
+
case "writing":
|
|
66
|
+
instruction = data.get("instruction", "")
|
|
67
|
+
prompt = data.get("prompt", "")
|
|
68
|
+
max_length = data.get("maxLength", 128)
|
|
69
|
+
min_words = data.get("minWords", 5)
|
|
70
|
+
text_key = data.get("textKey", "")
|
|
71
|
+
response = self._generate_response(instruction, prompt, max_length, min_words)
|
|
72
|
+
self._text_update(text_key, response)
|
|
73
|
+
self._object_update(done_key, {"done": True})
|
|
74
|
+
|
|
75
|
+
case "magnets":
|
|
76
|
+
prompt = data.get("prompt", "")
|
|
77
|
+
answer_key = data.get("answerKey", "")
|
|
78
|
+
stash = data.get("stash", [[]])
|
|
79
|
+
max_words = data.get("maxWords", 12)
|
|
80
|
+
composition_list = self._generate_composition_list(prompt, stash, max_words)
|
|
81
|
+
self._object_update(
|
|
82
|
+
answer_key,
|
|
83
|
+
{
|
|
84
|
+
"final": True,
|
|
85
|
+
"text": composition_list,
|
|
86
|
+
},
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
case "resumagents":
|
|
90
|
+
prompt = data.get("prompt", "")
|
|
91
|
+
answer_key = data.get("answerKey", "")
|
|
92
|
+
stash = data.get("stash", [[]])
|
|
93
|
+
max_words = data.get("maxWords", 12)
|
|
94
|
+
max_words_per_answer = data.get("maxWordsPerAnswer", 8)
|
|
95
|
+
num_answers = data.get("numAnswers", 8)
|
|
96
|
+
resume_composition_list = self._generate_resume_composition_list(
|
|
97
|
+
prompt, stash, max_words, max_words_per_answer, num_answers
|
|
98
|
+
)
|
|
99
|
+
self._object_update(
|
|
100
|
+
answer_key,
|
|
101
|
+
{
|
|
102
|
+
"final": True,
|
|
103
|
+
"text": resume_composition_list,
|
|
104
|
+
},
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
case "voting":
|
|
108
|
+
choices: list[dict] = data.get("choices", [])
|
|
109
|
+
choice_indexes = [i for i in range(0, len(choices))]
|
|
110
|
+
selected_choice = random.choice(choice_indexes)
|
|
111
|
+
self._object_update(response_key, {"action": "choose", "choice": selected_choice})
|
|
112
|
+
|
|
113
|
+
def _handle_room_operation(self, data: dict):
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
def _generate_response(self, instruction: str, prompt: str, max_length: int, min_words: int) -> str:
|
|
117
|
+
formatted_prompt = _RESPONSE_PROMPT_TEMPLATE.format(
|
|
118
|
+
personality=self._personality,
|
|
119
|
+
max_length=max_length,
|
|
120
|
+
min_words=min_words,
|
|
121
|
+
instruction=instruction,
|
|
122
|
+
prompt=prompt,
|
|
123
|
+
)
|
|
124
|
+
response = self._chat_model.generate_text(
|
|
125
|
+
formatted_prompt,
|
|
126
|
+
"",
|
|
127
|
+
temperature=self._chat_model._chat_model_temperature,
|
|
128
|
+
top_p=self._chat_model._chat_model_top_p,
|
|
129
|
+
)
|
|
130
|
+
if len(response) > max_length:
|
|
131
|
+
response = response[: max_length - 1]
|
|
132
|
+
return response
|
|
133
|
+
|
|
134
|
+
def _generate_composition_list(
|
|
135
|
+
self,
|
|
136
|
+
prompt: str,
|
|
137
|
+
stash: list[list[str]],
|
|
138
|
+
max_words: int,
|
|
139
|
+
) -> list[dict]:
|
|
140
|
+
|
|
141
|
+
possible_word_choices = []
|
|
142
|
+
|
|
143
|
+
for stash_entry in stash:
|
|
144
|
+
for word in stash_entry:
|
|
145
|
+
possible_word_choices.append(word)
|
|
146
|
+
|
|
147
|
+
all_possible_words_str = "\n".join([word for word in possible_word_choices])
|
|
148
|
+
formatted_prompt = _COMPOSITION_PROMPT_TEMPLATE.format(
|
|
149
|
+
personality=self._personality,
|
|
150
|
+
all_possible_words_str=all_possible_words_str,
|
|
151
|
+
max_words=max_words,
|
|
152
|
+
prompt=prompt,
|
|
153
|
+
)
|
|
154
|
+
response = self._chat_model.generate_text(
|
|
155
|
+
formatted_prompt,
|
|
156
|
+
"",
|
|
157
|
+
temperature=self._chat_model._chat_model_temperature,
|
|
158
|
+
top_p=self._chat_model._chat_model_top_p,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
## Listen, I know this is isn't the fastest way to search
|
|
162
|
+
## It's 12 words, bite me with your Big O notation
|
|
163
|
+
composition_list = []
|
|
164
|
+
response_list = response.split(" ")
|
|
165
|
+
for response_word in response_list:
|
|
166
|
+
found_word = False
|
|
167
|
+
response_word = response_word.strip()
|
|
168
|
+
if not all(char in string.punctuation for char in response_word):
|
|
169
|
+
response_word = response_word.translate(str.maketrans("", "", string.punctuation)).lower()
|
|
170
|
+
|
|
171
|
+
if not found_word:
|
|
172
|
+
for stash_index, stash_entry in enumerate(stash):
|
|
173
|
+
for check_word_index, check_word in enumerate(stash_entry):
|
|
174
|
+
if response_word == check_word.lower():
|
|
175
|
+
composition_list.append(
|
|
176
|
+
{
|
|
177
|
+
"index": stash_index,
|
|
178
|
+
"word": check_word_index,
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
found_word = True
|
|
182
|
+
break
|
|
183
|
+
if found_word:
|
|
184
|
+
break
|
|
185
|
+
|
|
186
|
+
if not found_word:
|
|
187
|
+
logger.warning(f"Word not found in choices: {response_word}")
|
|
188
|
+
|
|
189
|
+
if len(composition_list) > max_words:
|
|
190
|
+
composition_list = composition_list[: max_words - 1]
|
|
191
|
+
return composition_list
|
|
192
|
+
|
|
193
|
+
def _generate_resume_composition_list(
|
|
194
|
+
self,
|
|
195
|
+
prompt: str,
|
|
196
|
+
stash: list[list[str]],
|
|
197
|
+
max_words: int,
|
|
198
|
+
max_words_per_answers: int,
|
|
199
|
+
num_of_answers: int,
|
|
200
|
+
) -> list[list[dict]]:
|
|
201
|
+
# TODO Figure this out
|
|
202
|
+
resume_composition_list = []
|
|
203
|
+
for _ in range(0, num_of_answers):
|
|
204
|
+
resume_composition_list.append([{"index": 0, "word": 0}])
|
|
205
|
+
return resume_composition_list
|
|
File without changes
|