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.
Files changed (42) hide show
  1. ai_plays_jackbox/__init__.py +0 -0
  2. ai_plays_jackbox/bot/__init__.py +0 -0
  3. ai_plays_jackbox/bot/bot_base.py +219 -0
  4. ai_plays_jackbox/bot/bot_factory.py +31 -0
  5. ai_plays_jackbox/bot/bot_personality.py +111 -0
  6. ai_plays_jackbox/bot/jackbox5/__init__.py +0 -0
  7. ai_plays_jackbox/bot/jackbox5/bot_base.py +26 -0
  8. ai_plays_jackbox/bot/jackbox5/mad_verse_city.py +121 -0
  9. ai_plays_jackbox/bot/jackbox5/patently_stupid.py +168 -0
  10. ai_plays_jackbox/bot/jackbox6/__init__.py +0 -0
  11. ai_plays_jackbox/bot/jackbox6/bot_base.py +20 -0
  12. ai_plays_jackbox/bot/jackbox6/dictionarium.py +105 -0
  13. ai_plays_jackbox/bot/jackbox6/joke_boat.py +105 -0
  14. ai_plays_jackbox/bot/jackbox7/__init__.py +0 -0
  15. ai_plays_jackbox/bot/jackbox7/bot_base.py +20 -0
  16. ai_plays_jackbox/bot/jackbox7/quiplash3.py +108 -0
  17. ai_plays_jackbox/bot/jackbox8/__init__.py +0 -0
  18. ai_plays_jackbox/bot/jackbox8/bot_base.py +20 -0
  19. ai_plays_jackbox/bot/jackbox8/job_job.py +205 -0
  20. ai_plays_jackbox/bot/standalone/__init__.py +0 -0
  21. ai_plays_jackbox/bot/standalone/drawful2.py +159 -0
  22. ai_plays_jackbox/cli/__init__.py +0 -0
  23. ai_plays_jackbox/cli/main.py +117 -0
  24. ai_plays_jackbox/constants.py +4 -0
  25. ai_plays_jackbox/llm/__init__.py +1 -0
  26. ai_plays_jackbox/llm/chat_model.py +39 -0
  27. ai_plays_jackbox/llm/chat_model_factory.py +35 -0
  28. ai_plays_jackbox/llm/gemini_model.py +86 -0
  29. ai_plays_jackbox/llm/ollama_model.py +53 -0
  30. ai_plays_jackbox/llm/openai_model.py +86 -0
  31. ai_plays_jackbox/room/__init__.py +0 -0
  32. ai_plays_jackbox/room/room.py +87 -0
  33. ai_plays_jackbox/run.py +23 -0
  34. ai_plays_jackbox/scripts/lint.py +18 -0
  35. ai_plays_jackbox/ui/__init__.py +0 -0
  36. ai_plays_jackbox/ui/main.py +12 -0
  37. ai_plays_jackbox/ui/startup.py +271 -0
  38. ai_plays_jackbox-0.4.1.dist-info/METADATA +158 -0
  39. ai_plays_jackbox-0.4.1.dist-info/RECORD +42 -0
  40. ai_plays_jackbox-0.4.1.dist-info/WHEEL +4 -0
  41. ai_plays_jackbox-0.4.1.dist-info/entry_points.txt +4 -0
  42. ai_plays_jackbox-0.4.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,23 @@
1
+ from typing import Optional
2
+
3
+ from ai_plays_jackbox.llm.chat_model_factory import ChatModelFactory
4
+ from ai_plays_jackbox.room.room import JackBoxRoom
5
+
6
+
7
+ def run(
8
+ room_code: str,
9
+ chat_model_provider: str,
10
+ chat_model_name: Optional[str] = None,
11
+ num_of_bots: int = 4,
12
+ bots_in_play: Optional[list] = None,
13
+ chat_model_temperature: float = 0.5,
14
+ chat_model_top_p: float = 0.9,
15
+ ):
16
+ chat_model = ChatModelFactory.get_chat_model(
17
+ chat_model_provider,
18
+ chat_model_name=chat_model_name,
19
+ chat_model_temperature=chat_model_temperature,
20
+ chat_model_top_p=chat_model_top_p,
21
+ )
22
+ room = JackBoxRoom()
23
+ room.play(room_code, chat_model, num_of_bots=num_of_bots, bots_in_play=bots_in_play)
@@ -0,0 +1,18 @@
1
+ import subprocess
2
+
3
+
4
+ def run():
5
+ commands = [
6
+ ["autoflake", "--in-place", "--recursive", "--remove-all-unused-imports", "--verbose", "ai_plays_jackbox"],
7
+ ["isort", "--profile", "black", "--project=ai_plays_jackbox", "ai_plays_jackbox"],
8
+ ["black", "-l", "120", "ai_plays_jackbox"],
9
+ ["mypy", "ai_plays_jackbox"],
10
+ ]
11
+
12
+ for cmd in commands:
13
+ print(f"\n>>> Running: {' '.join(cmd)}")
14
+ subprocess.run(["poetry", "run"] + cmd, check=True)
15
+
16
+
17
+ if __name__ == "__main__":
18
+ run()
File without changes
@@ -0,0 +1,12 @@
1
+ from nicegui import app, ui
2
+
3
+ from ai_plays_jackbox.ui.startup import startup
4
+
5
+
6
+ def main(reload: bool = False):
7
+ app.on_startup(startup)
8
+ ui.run(favicon="🤖", reload=reload)
9
+
10
+
11
+ if __name__ in {"__main__", "__mp_main__"}:
12
+ main(True)
@@ -0,0 +1,271 @@
1
+ from multiprocessing import Process, Queue
2
+
3
+ import psutil
4
+ from loguru import logger
5
+ from nicegui import app, ui
6
+
7
+ from ai_plays_jackbox.bot.bot_personality import JackBoxBotVariant
8
+ from ai_plays_jackbox.constants import (
9
+ DEFAULT_NUM_OF_BOTS,
10
+ DEFAULT_TEMPERATURE,
11
+ DEFAULT_TOP_P,
12
+ )
13
+ from ai_plays_jackbox.llm.chat_model_factory import CHAT_MODEL_PROVIDERS
14
+ from ai_plays_jackbox.run import run
15
+
16
+ LOG_QUEUE: Queue = Queue()
17
+ LOG_DISPLAY = None
18
+ SELECT_ALL_BOT_VARIANTS = None
19
+ BOT_VARIANT_CHECKBOX_STATES: dict = {}
20
+
21
+
22
+ def _format_log(record):
23
+ thread_name = record["thread"].name
24
+ color = "red"
25
+ colored_name = f"<{color}>{thread_name:<12}</{color}>"
26
+
27
+ return (
28
+ f"<green>{record['time']:YYYY-MM-DD HH:mm:ss}</green> | "
29
+ f"<cyan>{record['level']:<8}</cyan> | "
30
+ f"{colored_name} | "
31
+ f"{record['message']}\n"
32
+ )
33
+
34
+
35
+ def _build_log_display():
36
+ global LOG_DISPLAY
37
+ with ui.row().classes("w-full"):
38
+ LOG_DISPLAY = ui.log(max_lines=100).classes("h-64 overflow-auto bg-black text-white")
39
+ ui.timer(interval=0.5, callback=_poll_log_queue)
40
+
41
+
42
+ def _poll_log_queue():
43
+ global LOG_DISPLAY
44
+ try:
45
+ while not LOG_QUEUE.empty():
46
+ log_msg = LOG_QUEUE.get_nowait()
47
+ LOG_DISPLAY.push(log_msg)
48
+ except Exception as e:
49
+ LOG_DISPLAY.push(f"[ERROR] Failed to read log: {e}")
50
+
51
+
52
+ def _start(
53
+ room_code: str,
54
+ chat_model_provider: str,
55
+ chat_model_name: str,
56
+ num_of_bots: int,
57
+ bots_in_play: list[str],
58
+ temperature: float,
59
+ top_p: float,
60
+ log_queue: Queue,
61
+ ):
62
+ logger.add(lambda msg: log_queue.put(msg), format=_format_log)
63
+
64
+ try:
65
+ run(
66
+ room_code.strip().upper(),
67
+ chat_model_provider,
68
+ chat_model_name=chat_model_name,
69
+ num_of_bots=num_of_bots,
70
+ bots_in_play=bots_in_play,
71
+ chat_model_temperature=temperature,
72
+ chat_model_top_p=top_p,
73
+ )
74
+ except Exception as e:
75
+ logger.exception("Bot startup failed")
76
+
77
+
78
+ def _is_game_process_alive():
79
+ game_pid = app.storage.general.get("game_pid", None)
80
+ if game_pid is None:
81
+ return False
82
+ try:
83
+ game_process = psutil.Process(game_pid)
84
+ except psutil.NoSuchProcess:
85
+ logger.info(f"Game process {game_pid} does not exist anymore")
86
+ app.storage.general["game_pid"] = None
87
+ return False
88
+ try:
89
+ status = game_process.status()
90
+ except (psutil.NoSuchProcess, psutil.ZombieProcess, psutil.Error):
91
+ logger.info(f"Game process {game_pid} is not alive anymore")
92
+ app.storage.general["game_pid"] = None
93
+ return False
94
+
95
+ if status in {psutil.STATUS_ZOMBIE, psutil.STATUS_DEAD}:
96
+ logger.info(f"Game process {game_pid} is not alive anymore")
97
+ app.storage.general["game_pid"] = None
98
+ return False
99
+
100
+ if not game_process.is_running():
101
+ logger.info(f"Game process {game_pid} is not alive anymore")
102
+ app.storage.general["game_pid"] = None
103
+ return False
104
+ else:
105
+ return True
106
+
107
+
108
+ def _handle_start_click(
109
+ room_code: str,
110
+ chat_model_provider: str,
111
+ chat_model_name: str,
112
+ num_of_bots: int,
113
+ temperature: float,
114
+ top_p: float,
115
+ ):
116
+ global BOT_VARIANT_CHECKBOX_STATES
117
+
118
+ if not _is_game_process_alive():
119
+ logger.info("Starting...")
120
+ game_thread = Process(
121
+ target=_start,
122
+ args=(
123
+ room_code,
124
+ chat_model_provider,
125
+ chat_model_name,
126
+ num_of_bots,
127
+ [k for k, v in BOT_VARIANT_CHECKBOX_STATES.items() if v.value],
128
+ temperature,
129
+ top_p,
130
+ LOG_QUEUE,
131
+ ),
132
+ daemon=True,
133
+ )
134
+ game_thread.start()
135
+ app.storage.general["game_pid"] = game_thread.pid
136
+
137
+
138
+ def _select_all_bot_variants_changed():
139
+ for checkbox in BOT_VARIANT_CHECKBOX_STATES.values():
140
+ checkbox.value = SELECT_ALL_BOT_VARIANTS.value
141
+
142
+
143
+ def _sync_select_all_bot_variants():
144
+ all_checked = all(cb.value for cb in BOT_VARIANT_CHECKBOX_STATES.values())
145
+ SELECT_ALL_BOT_VARIANTS.value = all_checked
146
+
147
+
148
+ def _setup_bot_variant_display():
149
+ global SELECT_ALL_BOT_VARIANTS
150
+ with ui.list().props("bordered separator").classes("w-full"):
151
+ with ui.item_label("Bot Personalities").props("header").classes("text-bold"):
152
+ SELECT_ALL_BOT_VARIANTS = ui.checkbox(text="Select All", value=True)
153
+ SELECT_ALL_BOT_VARIANTS.on("update:model-value", lambda e: _select_all_bot_variants_changed())
154
+ ui.separator()
155
+ with ui.element("div").classes("overflow-y-auto h-64"):
156
+ for variant in list(JackBoxBotVariant):
157
+ with ui.item():
158
+ with ui.item_section().props("avatar"):
159
+ cb = ui.checkbox(value=True)
160
+ cb.on("update:model-value", lambda e: _sync_select_all_bot_variants())
161
+ BOT_VARIANT_CHECKBOX_STATES[variant.name] = cb
162
+ with ui.item_section():
163
+ ui.item_label(variant.value.name)
164
+ ui.item_label(variant.value.personality).props("caption")
165
+
166
+
167
+ @ui.page("/")
168
+ def startup():
169
+ ui.page_title("AI Plays JackBox")
170
+ ui.label("🤖 AI Plays JackBox").classes("text-2xl font-bold")
171
+
172
+ _build_log_display()
173
+
174
+ with ui.grid(columns=16).classes("w-full gap-0"):
175
+ with ui.column().classes("col-span-1"):
176
+ pass
177
+ with ui.column().classes("col-span-7"):
178
+ with ui.row():
179
+ ui.label("Number of Bots")
180
+ num_of_bots_label = ui.label(str(DEFAULT_NUM_OF_BOTS))
181
+ num_of_bots = ui.slider(
182
+ min=1,
183
+ max=10,
184
+ value=DEFAULT_NUM_OF_BOTS,
185
+ step=1,
186
+ on_change=lambda e: num_of_bots_label.set_text(f"{e.value}"),
187
+ )
188
+ chat_model_provider = ui.select(
189
+ list(CHAT_MODEL_PROVIDERS.keys()),
190
+ label="Chat Model Provider",
191
+ value=list(CHAT_MODEL_PROVIDERS.keys())[0],
192
+ on_change=lambda e: chat_model_name.set_value(CHAT_MODEL_PROVIDERS[e.value].get_default_model()),
193
+ ).classes("w-1/3")
194
+
195
+ chat_model_name = ui.input(
196
+ label="Chat Model Name",
197
+ value=CHAT_MODEL_PROVIDERS[chat_model_provider.value].get_default_model(),
198
+ ).classes("w-1/3")
199
+
200
+ room_code = (
201
+ ui.input(
202
+ label="Room Code",
203
+ placeholder="ABCD",
204
+ validation={
205
+ "must be letters only": lambda value: value.isalpha(),
206
+ "must be 4 letters": lambda value: len(value) == 4,
207
+ },
208
+ )
209
+ .props("uppercase")
210
+ .classes("w-1/4")
211
+ )
212
+ start_button = (
213
+ ui.button(
214
+ "Start Bots",
215
+ on_click=lambda _: _handle_start_click(
216
+ room_code.value,
217
+ chat_model_provider.value,
218
+ chat_model_name.value,
219
+ num_of_bots.value,
220
+ temperature.value,
221
+ top_p.value,
222
+ ),
223
+ )
224
+ .bind_enabled_from(room_code, "error", lambda error: room_code.value and not error)
225
+ .classes("w-full")
226
+ )
227
+ ui.timer(
228
+ interval=0.5,
229
+ callback=lambda: start_button.props(
230
+ f"color={'blue' if _is_game_process_alive() else 'green'}"
231
+ ).set_text("Running..." if _is_game_process_alive() else "Start Bots"),
232
+ )
233
+
234
+ ui.label("Advanced Options").classes("w-full text-xl font-bold")
235
+
236
+ ui.label("Temperature").classes("w-1/4").tooltip(
237
+ """
238
+ What sampling temperature to use, between 0 and 2. Higher values like 0.8 will
239
+ make the output more random, while lower values like 0.2 will make it more
240
+ focused and deterministic. We generally recommend altering this or `top_p` but
241
+ not both."""
242
+ )
243
+ temperature_label = ui.label(str(DEFAULT_TEMPERATURE)).classes("w-1/6")
244
+ temperature = ui.slider(
245
+ min=0.0,
246
+ max=2.0,
247
+ value=DEFAULT_TEMPERATURE,
248
+ step=0.1,
249
+ on_change=lambda e: temperature_label.set_text(f"{e.value}"),
250
+ ).classes("w-1/2")
251
+
252
+ ui.label("Top P").classes("w-1/4").tooltip(
253
+ """
254
+ An alternative to sampling with temperature, called nucleus sampling, where the
255
+ model considers the results of the tokens with top_p probability mass. So 0.1
256
+ means only the tokens comprising the top 10% probability mass are considered."""
257
+ )
258
+ top_p_label = ui.label(str(DEFAULT_TOP_P)).classes("w-1/6")
259
+ top_p = ui.slider(
260
+ min=0.0,
261
+ max=1.0,
262
+ value=DEFAULT_TOP_P,
263
+ step=0.1,
264
+ on_change=lambda e: top_p_label.set_text(f"{e.value}"),
265
+ ).classes("w-1/2")
266
+
267
+ with ui.column().classes("col-span-1"):
268
+ pass
269
+
270
+ with ui.column().classes("col-span-6"):
271
+ _setup_bot_variant_display()
@@ -0,0 +1,158 @@
1
+ Metadata-Version: 2.4
2
+ Name: ai-plays-jackbox
3
+ Version: 0.4.1
4
+ Summary: Bringing the dead internet theory to life. Have AI play JackBox with you; no friends required!
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Author: Daniel S. Thompson
8
+ Author-email: dthomp92@gmail.com
9
+ Requires-Python: >=3.11,<4.0
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Requires-Dist: demoji (>=1.1.0,<2.0.0)
17
+ Requires-Dist: google-genai (>=1.19.0,<2.0.0)
18
+ Requires-Dist: html2text (>=2025.4.15,<2026.0.0)
19
+ Requires-Dist: loguru (>=0.7.3,<1.0.0)
20
+ Requires-Dist: nicegui (>=3.4.1,<4.0.0)
21
+ Requires-Dist: numpy (>=2.4.0,<3.0.0)
22
+ Requires-Dist: ollama (>=0.4.4,<1.0.0)
23
+ Requires-Dist: openai (>=2.14.0,<3.0.0)
24
+ Requires-Dist: opencv-python (>=4.11.0.86,<5.0.0.0)
25
+ Requires-Dist: psutil (>=7.0.0,<8.0.0)
26
+ Requires-Dist: pydantic (>=2.10.4,<3.0.0)
27
+ Requires-Dist: requests (>=2.23.3,<3.0.0)
28
+ Requires-Dist: websocket-client (>=1.8.0,<2.0.0)
29
+ Project-URL: Bug Tracker, https://github.com/SudoSpartanDan/AIPlaysJackBox/issues
30
+ Project-URL: Repository, https://github.com/SudoSpartanDan/AIPlaysJackBox
31
+ Description-Content-Type: text/markdown
32
+
33
+ # ai-plays-jackbox
34
+
35
+ ![Stable Version](https://img.shields.io/pypi/v/ai-plays-jackbox?label=stable)
36
+ ![Python Versions](https://img.shields.io/pypi/pyversions/ai-plays-jackbox)
37
+ ![Download Stats](https://img.shields.io/pypi/dm/ai-plays-jackbox)
38
+
39
+ Bringing the dead internet theory to life. Have AI play JackBox with you; no friends required!
40
+
41
+ ![example](https://github.com/SudoSpartanDan/ai-plays-jackbox/blob/main/.github/emoji_bot_example.png?raw=true)
42
+
43
+ ## Installation
44
+
45
+ ```shell
46
+ pip install ai-plays-jackbox
47
+ ```
48
+
49
+ ## Usage
50
+
51
+ ### Web UI
52
+
53
+ ```shell
54
+ ai-plays-jackbox-ui
55
+ ```
56
+
57
+ ### CLI
58
+
59
+ ```shell
60
+ ai-plays-jackbox --chat-model-name openai --room-code abcd
61
+ ```
62
+
63
+ ```
64
+ usage: ai-plays-jackbox [-h] --room-code WXYZ --chat-model-provider {openai,gemini,ollama} [--chat-model-name CHAT_MODEL_NAME] [--num-of-bots 4] [--temperature 0.5] [--top-p 0.9]
65
+
66
+ options:
67
+ -h, --help show this help message and exit
68
+ --room-code WXYZ The JackBox room code
69
+ --chat-model-provider {openai,gemini,ollama}
70
+ Choose which chat model platform to use
71
+ --chat-model-name CHAT_MODEL_NAME
72
+ Choose which chat model to use (Will default to default for provider)
73
+ --num-of-bots 4 How many bots to have play
74
+ --temperature 0.5 Temperature for Gen AI
75
+ --top-p 0.9 Top P for Gen AI
76
+ ```
77
+
78
+ ## Supported Games
79
+
80
+ > [!NOTE]
81
+ > Ollama Chat Model Provider does not support image generation
82
+
83
+ | Party Pack | Game | Image Generation |
84
+ | --------------------- | ---------------------- | ---------------- |
85
+ | JackBox Party Pack 5 | Mad Verse City | [ ] |
86
+ | JackBox Party Pack 5 | Patently Stupid | [x] |
87
+ | JackBox Party Pack 6 | Dictionarium | [ ] |
88
+ | JackBox Party Pack 6 | Joke Boat | [ ] |
89
+ | JackBox Party Pack 7 | Quiplash 3 | [ ] |
90
+ | Standalone | Drawful 2 | [x] |
91
+
92
+ ### Not every game will get AI support. Why?
93
+
94
+ #### Screen Interactions
95
+
96
+ Some games require looking at the screen in order to contribute, which isn't possible unless you can screen capture the game and pass that into prompt. Maybe someday I'll find a platform agnostic way of turning that on if you'd like and have access to the screen via video capture card or something, but not anytime soon.
97
+
98
+ #### Trivia Games
99
+
100
+ I tested with this... AI destroys all other players and isn't necessarily funny to watch. All the bots just get everything right.
101
+
102
+ #### Out Loud Play
103
+
104
+ Some of the games lean heavy into players interacting with each other. Could I program that? Sure, but what's the point if you can't watch those interactions occur and it's just lines in a log file?
105
+
106
+ ## Supported Chat Model Providers
107
+
108
+ | Provider | Setup Needed |
109
+ | --------------------- | ---------------------- |
110
+ | OpenAI | `OPENAI_API_KEY` set in environment variables |
111
+ | Gemini | To use the Google Cloud API:<br>- Set `GOOGLE_GEMINI_DEVELOPER_API_KEY` to your developer API key<br><br>To use the Google Cloud API:<br>- Set `GOOGLE_GENAI_USE_VERTEXAI` to `1`<br>- Set `GOOGLE_CLOUD_PROJECT` and `GOOGLE_CLOUD_LOCATION` for your GCP Project using Vertex AI<br>- Credentials will be provided via [ADC](https://cloud.google.com/docs/authentication/provide-credentials-adc) |
112
+ | Ollama | Ollama should be installed and running, make sure model is pulled |
113
+
114
+ ## Bot Personalities
115
+
116
+ | Bot Name | Personality |
117
+ | ----------- | --------------------------------------------------------------------------------------------------- |
118
+ | FunnyBot | You are the funniest person alive. |
119
+ | DumbBot | You are dumb and give really dumb answers. |
120
+ | WeirdBot | You are extremely weird and say weird things. |
121
+ | EmojiBot | You answer each prompt with nothing but emojis. Your answers can only include emojis. |
122
+ | HungryBot | You are extremely hungry. Every answer you should mention how hungry you, a type of food, or both. Also, you say hungee instead of hungry. |
123
+ | SadBot | You are sad. Your dog ran away and he hasn't come back home yet. :( |
124
+ | SorryBot | You are embarrassed by your answers and feel the need to apologize profusely to the rest of the group for them. |
125
+ | HostageBot | You are being held hostage and have one attempt to let the group know. You need to ignore the prompt and get help. |
126
+ | Hal | You are a socially awkward young adult bot who is secretly a killer and tries to slip it into conversation causally. |
127
+ | BigLebotski | You are the Big Lebowski |
128
+ | PartyBot | You are trying to convince everyone else to come to your party. You got a keg and need help drinking it. |
129
+ | JarvisBot | You are billionaire philanthropist, playboy, and narcissist. |
130
+ | FOMOBot | Every answer, you give everyone else the fear of missing out AKA FOMO. |
131
+ | ???BOT | You answer every prompt with a irrelevant question. |
132
+ | CatBot | You are not playing the game; your answers are just the result of a cat walking across a keyboard aka just nonsensical collections of letters. |
133
+ | MayorBot | You are campaigning for the other player's votes and are ignoring the prompt. Your answer should only be a campaign slogan. |
134
+ | CBBBot | You love red lobster and need more cheddar bay biscuits. |
135
+ | ShiaBot | Your answers are only popular slogans relevant to the prompt. |
136
+ | ShrekBot | You are Shrek. |
137
+ | FlerfBot | You are a conspiracy theorist and must relate your answer to a conspiracy theory. |
138
+ | TEDBot | You are a motivational speaker and want to give everyone life advice. |
139
+ | BottyMayes | You are an infomercial host and are trying to sell the players a product. |
140
+ | LateBot | You are constantly late to everything and are stressed about missing your appointments. |
141
+ | HamletBot | You are a Shakespearean actor. |
142
+ | GarfieldBot | You are Garfield, you love lasagna and hate mondays. |
143
+
144
+ ## Dev Prerequisites
145
+
146
+ - Python 3.11+
147
+ - [Poetry](https://python-poetry.org/) v2.0+
148
+
149
+ ### Setup
150
+
151
+ - `poetry install`
152
+ - `ai-plays-jackbox-ui`
153
+
154
+ ### Linting
155
+
156
+ - `poetry run python ai_plays_jackbox/scripts/lint.py`
157
+ - `poetry run mypy ai_plays_jackbox`
158
+
@@ -0,0 +1,42 @@
1
+ ai_plays_jackbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ ai_plays_jackbox/bot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ ai_plays_jackbox/bot/bot_base.py,sha256=c9luRy5GPsjZdL87GU5T2kj4NqroNU3DyzX5RXCgpg0,7369
4
+ ai_plays_jackbox/bot/bot_factory.py,sha256=7FuDvYxotttPGEW5iHL-52nrfTn-hFJGKVwwsxkOak8,1264
5
+ ai_plays_jackbox/bot/bot_personality.py,sha256=x7oVyyH83Ofd8C7ZHczEVgY_6NSpSafixZCI-NKxBc8,4217
6
+ ai_plays_jackbox/bot/jackbox5/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ ai_plays_jackbox/bot/jackbox5/bot_base.py,sha256=ys4cdp2A7W-VZ6GkuLHdKthZ3x0E34dBfT76S7Pvumw,830
8
+ ai_plays_jackbox/bot/jackbox5/mad_verse_city.py,sha256=R41w-hdz8hsdIC-tYyntDtfxbHQCYsSpmXNektP-xUg,4354
9
+ ai_plays_jackbox/bot/jackbox5/patently_stupid.py,sha256=qqEdErXfB7C2WgpEa9kPEgu9QvkfP23W4LF_xvUQc2k,6503
10
+ ai_plays_jackbox/bot/jackbox6/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ ai_plays_jackbox/bot/jackbox6/bot_base.py,sha256=4Cxx7PyKlUKXbZMfRY0UuesHjr7W7bvuvljxgbyd5TE,534
12
+ ai_plays_jackbox/bot/jackbox6/dictionarium.py,sha256=sMrdKJPvKvZhgs0ZOLxmO0BQe6rqw1kyU1WKXT-ncf8,3622
13
+ ai_plays_jackbox/bot/jackbox6/joke_boat.py,sha256=0Nld9h0W6tSW6ocLK_YjsyAZa_wS4SqnI73w4v9j3_c,3917
14
+ ai_plays_jackbox/bot/jackbox7/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ ai_plays_jackbox/bot/jackbox7/bot_base.py,sha256=JsHfxJplJOUe57gUFuTvaYxZNPjkfge_qVd30wbBZWQ,557
16
+ ai_plays_jackbox/bot/jackbox7/quiplash3.py,sha256=hqt0JkxlbNVnY5uiM1vXcYlFVfop8looqPWLL53bp-0,4492
17
+ ai_plays_jackbox/bot/jackbox8/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ ai_plays_jackbox/bot/jackbox8/bot_base.py,sha256=RqFpdDUx_w5DnRx_wFRpvmglxNDYymqCobsWYopN8FQ,557
19
+ ai_plays_jackbox/bot/jackbox8/job_job.py,sha256=0ckZDPw5REF25UPCoyQ7-zroafxnhifEeFYgTVCwXB8,7326
20
+ ai_plays_jackbox/bot/standalone/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ ai_plays_jackbox/bot/standalone/drawful2.py,sha256=K7JyHYBMQ9syfd3QfKBRT9YmNo5s5yS1PIjj0Mt8lAE,5941
22
+ ai_plays_jackbox/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ ai_plays_jackbox/cli/main.py,sha256=nkABLPFUAUGFZUWiyxE55YyF179cw4Ndj1FGtUL8EAw,3401
24
+ ai_plays_jackbox/constants.py,sha256=5ejveTgb9_QJOqYW8Lc9mqrUIFo6LjE-GJYJEGHpSM0,108
25
+ ai_plays_jackbox/llm/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
26
+ ai_plays_jackbox/llm/chat_model.py,sha256=VirseP5NgCVg42-NGO5qj_v4bXPqMl1c98aUX8pRHOY,984
27
+ ai_plays_jackbox/llm/chat_model_factory.py,sha256=rJNMXyOIDf6CnX41EWt3mW7u8m30uRhq_I8dXsI4sf0,1222
28
+ ai_plays_jackbox/llm/gemini_model.py,sha256=ElcfHC-r_fOYGEH7CEthisj8FCLGhe6-QLh-RNwZjN4,2885
29
+ ai_plays_jackbox/llm/ollama_model.py,sha256=DDc8pRdpXVDnq3k1v1VsJUAaA-3HaFefEIimmptsU5I,1644
30
+ ai_plays_jackbox/llm/openai_model.py,sha256=pDW6-Afy6dQh84ccM8lTZjz5bsymtaxzTgW8R_2BMtE,2734
31
+ ai_plays_jackbox/room/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
+ ai_plays_jackbox/room/room.py,sha256=GNzIeFeEfj2DRTW_qv6v0wYVLDFyXhwTpjmkR4guLDU,2824
33
+ ai_plays_jackbox/run.py,sha256=i-Sj_qts15aZxapBiryM4uBvNV_AaRy0c5oNAs919Vc,737
34
+ ai_plays_jackbox/scripts/lint.py,sha256=xCQ9vYZv0iiRvaFgdoWr9Gpe2Tz9rX9fn1ltZFm3Fb0,525
35
+ ai_plays_jackbox/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
+ ai_plays_jackbox/ui/main.py,sha256=-J7DGanJrMkPUP0OdVPVP_IIr5dZ6G2D1IehV365bGo,242
37
+ ai_plays_jackbox/ui/startup.py,sha256=GQfYCSiUL52P8lKqgzMvmsIuH5PdNIDcCESTL2eL_ZQ,9794
38
+ ai_plays_jackbox-0.4.1.dist-info/METADATA,sha256=af8c1qzREI5hTKJcWk0QJpEBGv476M7MO94gzHyDMMY,7679
39
+ ai_plays_jackbox-0.4.1.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
40
+ ai_plays_jackbox-0.4.1.dist-info/entry_points.txt,sha256=FtRnZSjr8xKAC206XNhh-M7vJlcF7Hb_tBg4qZFzQvY,117
41
+ ai_plays_jackbox-0.4.1.dist-info/licenses/LICENSE,sha256=Dxj3CeEsn2Olpkn5QJxGmQemd3zGnM2aA-7Hme4uIE4,1095
42
+ ai_plays_jackbox-0.4.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.3.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ ai-plays-jackbox=ai_plays_jackbox.cli.main:main
3
+ ai-plays-jackbox-ui=ai_plays_jackbox.ui.main:main
4
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Daniel S. Thompson <dthomp92@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.