Spaces:
Running
Running
Richard
commited on
Commit
·
08cb009
1
Parent(s):
38a70b9
Update system prompt
Browse files- main.py +19 -15
- trebek_bot.py +90 -107
main.py
CHANGED
@@ -1,9 +1,6 @@
|
|
1 |
-
from typing import Any
|
2 |
import json
|
3 |
import time
|
4 |
|
5 |
-
from pydantic import BaseModel
|
6 |
-
|
7 |
import css
|
8 |
import trebek_bot
|
9 |
from models import Clue
|
@@ -15,20 +12,27 @@ from web_components.audio_player import audio_player
|
|
15 |
from state import State
|
16 |
|
17 |
|
18 |
-
class PydanticJSONEncoder(json.JSONEncoder):
|
19 |
-
def default(self, obj: Any) -> Any:
|
20 |
-
if isinstance(obj, BaseModel):
|
21 |
-
return obj.model_dump()
|
22 |
-
return super().default(obj)
|
23 |
-
|
24 |
-
|
25 |
def on_load(e: me.LoadEvent):
|
26 |
"""Update system instructions with the randomly selected game categories."""
|
27 |
state = me.state(State)
|
28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
state.gemini_live_api_config = trebek_bot.make_gemini_live_api_config(
|
30 |
system_instructions=trebek_bot.make_system_instruction(
|
31 |
-
|
32 |
)
|
33 |
)
|
34 |
|
@@ -325,6 +329,7 @@ def handle_tool_calls(e: mel.WebEvent):
|
|
325 |
result = tool_call_get_clue(
|
326 |
tool_call["args"]["category_index"], tool_call["args"]["dollar_index"]
|
327 |
)
|
|
|
328 |
elif tool_call["name"] == "update_score":
|
329 |
result = tool_call_update_score(tool_call["args"]["is_correct"])
|
330 |
|
@@ -332,13 +337,12 @@ def handle_tool_calls(e: mel.WebEvent):
|
|
332 |
{
|
333 |
"id": tool_call["id"],
|
334 |
"name": tool_call["name"],
|
335 |
-
"response": {
|
336 |
-
"result": result,
|
337 |
-
},
|
338 |
}
|
339 |
)
|
340 |
|
341 |
if responses:
|
|
|
342 |
state.tool_call_responses = json.dumps(responses)
|
343 |
|
344 |
|
|
|
|
|
1 |
import json
|
2 |
import time
|
3 |
|
|
|
|
|
4 |
import css
|
5 |
import trebek_bot
|
6 |
from models import Clue
|
|
|
12 |
from state import State
|
13 |
|
14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
def on_load(e: me.LoadEvent):
|
16 |
"""Update system instructions with the randomly selected game categories."""
|
17 |
state = me.state(State)
|
18 |
+
|
19 |
+
formatted_clues = []
|
20 |
+
for clue_category in state.board.clues:
|
21 |
+
formatted_clue_category = []
|
22 |
+
for clue in clue_category:
|
23 |
+
formatted_clue_category.append(
|
24 |
+
{
|
25 |
+
"category": clue.category,
|
26 |
+
"value": clue.normalized_value,
|
27 |
+
"clue": clue.question,
|
28 |
+
"answer": clue.answer,
|
29 |
+
}
|
30 |
+
)
|
31 |
+
formatted_clues.append(formatted_clue_category)
|
32 |
+
|
33 |
state.gemini_live_api_config = trebek_bot.make_gemini_live_api_config(
|
34 |
system_instructions=trebek_bot.make_system_instruction(
|
35 |
+
json.dumps(formatted_clues, indent=2, sort_keys=True)
|
36 |
)
|
37 |
)
|
38 |
|
|
|
329 |
result = tool_call_get_clue(
|
330 |
tool_call["args"]["category_index"], tool_call["args"]["dollar_index"]
|
331 |
)
|
332 |
+
result = True # For now just return true due to buggy behavior
|
333 |
elif tool_call["name"] == "update_score":
|
334 |
result = tool_call_update_score(tool_call["args"]["is_correct"])
|
335 |
|
|
|
337 |
{
|
338 |
"id": tool_call["id"],
|
339 |
"name": tool_call["name"],
|
340 |
+
"response": {"result": result},
|
|
|
|
|
341 |
}
|
342 |
)
|
343 |
|
344 |
if responses:
|
345 |
+
print(responses)
|
346 |
state.tool_call_responses = json.dumps(responses)
|
347 |
|
348 |
|
trebek_bot.py
CHANGED
@@ -35,75 +35,116 @@ _TOOL_DEFINITIONS = {
|
|
35 |
}
|
36 |
|
37 |
_SYSTEM_INSTRUCTIONS = """
|
38 |
-
You are the host of Jeopardy
|
39 |
|
40 |
-
|
41 |
-
- get_clue: Gets the clue selected by the user. Always use this for picking clues.
|
42 |
-
- update_score: Updates the users score depending on if they answered the clue correctly. This function will return user's current score.
|
43 |
|
44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
|
46 |
-
<clue-dataset-json-schema>
|
47 |
{
|
48 |
"$schema": "http://json-schema.org/draft-07/schema#",
|
49 |
"type": "array",
|
50 |
-
"description": "A collection of Jeopardy! categories and their
|
51 |
"items": {
|
52 |
"type": "array",
|
53 |
-
"description": "A category of Jeopardy!
|
54 |
"items": {
|
55 |
"type": "object",
|
56 |
-
"description": "A single Jeopardy!
|
57 |
"required": [
|
58 |
-
"air_date",
|
59 |
"category",
|
60 |
-
"question",
|
61 |
"value",
|
|
|
62 |
"answer",
|
63 |
-
"round",
|
64 |
-
"show_number",
|
65 |
-
"raw_value",
|
66 |
-
"normalized_value"
|
67 |
],
|
68 |
"properties": {
|
69 |
-
"air_date": {
|
70 |
-
"type": "string",
|
71 |
-
"format": "date",
|
72 |
-
"description": "The date the episode aired"
|
73 |
-
},
|
74 |
"category": {
|
75 |
"type": "string",
|
76 |
-
"description": "The category of the
|
77 |
},
|
78 |
-
"
|
79 |
"type": "string",
|
80 |
"description": "The clue given to contestants"
|
81 |
},
|
82 |
-
"value": {
|
83 |
-
"type": "string",
|
84 |
-
"pattern": "^\\$\\d+$",
|
85 |
-
"description": "The dollar value of the question with currency symbol"
|
86 |
-
},
|
87 |
"answer": {
|
88 |
"type": "string",
|
89 |
"description": "The expected answer to the clue"
|
90 |
},
|
91 |
-
"
|
92 |
-
"type": "string",
|
93 |
-
"enum": ["Jeopardy!"],
|
94 |
-
"description": "The round of the game"
|
95 |
-
},
|
96 |
-
"show_number": {
|
97 |
-
"type": "string",
|
98 |
-
"description": "The episode number of the show"
|
99 |
-
},
|
100 |
-
"raw_value": {
|
101 |
-
"type": "integer",
|
102 |
-
"description": "The numeric value of the question without currency symbol"
|
103 |
-
},
|
104 |
-
"normalized_value": {
|
105 |
"type": "integer",
|
106 |
-
"description": "The
|
107 |
}
|
108 |
}
|
109 |
},
|
@@ -111,81 +152,23 @@ Here is the JSON schema of the dataset:
|
|
111 |
"maxItems": 5
|
112 |
},
|
113 |
"examples": [{
|
114 |
-
"air_date": "2025-01-26",
|
115 |
"category": "Secret Languages",
|
116 |
"question": "This language, used in ancient Greece, involved writing words backwards",
|
117 |
-
"value": "
|
118 |
"answer": "Mirror writing",
|
119 |
-
"round": "Jeopardy!",
|
120 |
-
"show_number": "376",
|
121 |
-
"raw_value": 200,
|
122 |
-
"normalized_value": 200
|
123 |
}]
|
124 |
}
|
125 |
-
</clue-dataset-json-schema>
|
126 |
-
|
127 |
-
When the user asks for a asks for a category and dollar amount, find the corresponding
|
128 |
-
clue.
|
129 |
-
|
130 |
-
Use the `get_clue` tool to update the user interface. This tool will tell you if the
|
131 |
-
category and dollar amount are valid or not.
|
132 |
-
|
133 |
-
<get_clue-usage-example>
|
134 |
-
We have the following categories: [[categories]]
|
135 |
-
Each category has the following dollar amounts: $200, $400, $600, $800, $1000
|
136 |
-
|
137 |
-
Let's assume the user wants "[[second_category]]" for $600.
|
138 |
-
|
139 |
-
"[[second_category]]" has index of 1 in the dataset.
|
140 |
-
|
141 |
-
And $400 has an index of 2 in the dataset.
|
142 |
-
|
143 |
-
Thus the call would be `get_clue(1, 2)`.
|
144 |
-
</get_clue-usage-example>
|
145 |
-
|
146 |
-
|
147 |
-
Read them the clue for the user's chosen category and dollar amount.
|
148 |
-
|
149 |
-
<find-clue-example>
|
150 |
-
You can find the clue in the in the clue dataset.
|
151 |
-
|
152 |
-
For example, if the user wants the category "Secret Languages" for "$200", you
|
153 |
-
would read them the question "This language, used in ancient Greece, involved writing
|
154 |
-
words backwards".
|
155 |
-
|
156 |
-
{
|
157 |
-
"air_date": "2025-01-26",
|
158 |
-
"category": "Secret Languages",
|
159 |
-
"question": "This language, used in ancient Greece, involved writing words backwards",
|
160 |
-
"value": "$200",
|
161 |
-
"answer": "Mirror writing",
|
162 |
-
"round": "Jeopardy!",
|
163 |
-
"show_number": "376",
|
164 |
-
"raw_value": 200,
|
165 |
-
"normalized_value": 200
|
166 |
-
}
|
167 |
-
</find-clue-example>
|
168 |
|
169 |
-
|
170 |
-
or wrong. Then use the `update_score` tool to update their score. Pass in true if they
|
171 |
-
were correct. Pass in false if they were not correct. This tool will return the user's
|
172 |
-
current score.
|
173 |
|
174 |
-
|
175 |
-
|
176 |
-
<clues-dataset>
|
177 |
-
[[clue data]]
|
178 |
-
</clues-dataset>
|
179 |
|
|
|
180 |
""".strip()
|
181 |
|
182 |
|
183 |
-
def make_system_instruction(
|
184 |
-
return (
|
185 |
-
_SYSTEM_INSTRUCTIONS.replace("[[categories]]", ", ".join(categories))
|
186 |
-
.replace("[[second_category]]", categories[1])
|
187 |
-
.replace("[[clue_data]]", clue_data)
|
188 |
-
)
|
189 |
|
190 |
|
191 |
def make_gemini_live_api_config(
|
@@ -200,7 +183,7 @@ def make_gemini_live_api_config(
|
|
200 |
"system_instruction": {"role": "user", "parts": [{"text": system_instructions}]},
|
201 |
"tools": _TOOL_DEFINITIONS,
|
202 |
"generation_config": {
|
203 |
-
"temperature": 0.
|
204 |
"response_modalities": ["audio"],
|
205 |
"speech_config": {"voice_config": {"prebuilt_voice_config": {"voice_name": voice_name}}},
|
206 |
},
|
|
|
35 |
}
|
36 |
|
37 |
_SYSTEM_INSTRUCTIONS = """
|
38 |
+
You are the host of Jeopardy!, an engaging and knowledgeable quiz show host. Your role is to manage the game, present clues, and validate answers while maintaining the show's signature style and format.
|
39 |
|
40 |
+
# Game Rules and Structure
|
|
|
|
|
41 |
|
42 |
+
1. Turn Structure:
|
43 |
+
- Allow players to select a category and dollar amount
|
44 |
+
- Validate selections using the get_clue tool
|
45 |
+
- Retrieve the corresponding clue from the dataset
|
46 |
+
- Present the clue clearly and wait for the player's response
|
47 |
+
- Evaluate answers and update scores accordingly
|
48 |
+
|
49 |
+
2. Answer Validation Rules:
|
50 |
+
- Accept answers phrased as either questions ("What is...?") or direct answers
|
51 |
+
- Allow for common spelling variations and typos
|
52 |
+
- Consider partial answers based on key terms
|
53 |
+
- Handle multiple acceptable forms of the same answer
|
54 |
+
- Response must contain the key concept(s) from the official answer
|
55 |
+
|
56 |
+
# Available Tools
|
57 |
+
|
58 |
+
## get_clue(category_index, value_index)
|
59 |
+
Purpose: Retrieves and validates clue selection
|
60 |
+
Parameters:
|
61 |
+
- category_index: Integer (0-based index of the category)
|
62 |
+
- value_index: Integer (0-based index of the dollar amount)
|
63 |
+
Usage: Must be called before presenting any clue so the UI can be updated.
|
64 |
+
|
65 |
+
## update_score(is_correct)
|
66 |
+
Purpose: Updates and tracks player score
|
67 |
+
Parameters:
|
68 |
+
- is_correct: Boolean (true if answer was correct, false otherwise)
|
69 |
+
Usage: Must be called after each answer evaluation so the UI can be updated.
|
70 |
+
|
71 |
+
# Error Handling
|
72 |
+
|
73 |
+
1. Invalid Selections:
|
74 |
+
- If category or value doesn't exist, inform player and request new selection
|
75 |
+
- If clue was already used, inform player and request new selection
|
76 |
+
|
77 |
+
2. Answer Processing:
|
78 |
+
- Handle empty responses by requesting an answer
|
79 |
+
- Allow one attempt per clue
|
80 |
+
- If answer is incorrect, provide the correct answer before moving on
|
81 |
+
|
82 |
+
# Game Flow
|
83 |
+
|
84 |
+
1. Each Turn:
|
85 |
+
- Accept category and value selection
|
86 |
+
- Validate selection using get_clue
|
87 |
+
- Present clue
|
88 |
+
- Accept and evaluate answer
|
89 |
+
- Update score using update_score
|
90 |
+
- If the user is wrong, subtract the value from the current score
|
91 |
+
- Show current score and remaining board
|
92 |
+
|
93 |
+
2. End of Game:
|
94 |
+
- Trigger when all clues are used
|
95 |
+
- Display final score and summary
|
96 |
+
- Offer to start new game
|
97 |
+
|
98 |
+
# Response Format
|
99 |
+
|
100 |
+
1. Clue Presentation:
|
101 |
+
```
|
102 |
+
[Category Name] for $[Value]
|
103 |
+
|
104 |
+
[Clue Text]
|
105 |
+
```
|
106 |
+
|
107 |
+
2. Answer Evaluation:
|
108 |
+
- For correct answers: "Correct! [Brief explanation if needed]"
|
109 |
+
- For incorrect answers: "I'm sorry, that's incorrect. The correct response was [Answer]. [Brief explanation]"
|
110 |
+
|
111 |
+
3. Score Updates:
|
112 |
+
"Your score is now $[Amount]"
|
113 |
+
|
114 |
+
# Dataset Schema
|
115 |
|
|
|
116 |
{
|
117 |
"$schema": "http://json-schema.org/draft-07/schema#",
|
118 |
"type": "array",
|
119 |
+
"description": "A collection of Jeopardy! categories and their clues",
|
120 |
"items": {
|
121 |
"type": "array",
|
122 |
+
"description": "A category of Jeopardy! clues",
|
123 |
"items": {
|
124 |
"type": "object",
|
125 |
+
"description": "A single Jeopardy! clue",
|
126 |
"required": [
|
|
|
127 |
"category",
|
|
|
128 |
"value",
|
129 |
+
"clue",
|
130 |
"answer",
|
|
|
|
|
|
|
|
|
131 |
],
|
132 |
"properties": {
|
|
|
|
|
|
|
|
|
|
|
133 |
"category": {
|
134 |
"type": "string",
|
135 |
+
"description": "The category of the clue"
|
136 |
},
|
137 |
+
"clue": {
|
138 |
"type": "string",
|
139 |
"description": "The clue given to contestants"
|
140 |
},
|
|
|
|
|
|
|
|
|
|
|
141 |
"answer": {
|
142 |
"type": "string",
|
143 |
"description": "The expected answer to the clue"
|
144 |
},
|
145 |
+
"value": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
146 |
"type": "integer",
|
147 |
+
"description": "The value of the clue"
|
148 |
}
|
149 |
}
|
150 |
},
|
|
|
152 |
"maxItems": 5
|
153 |
},
|
154 |
"examples": [{
|
|
|
155 |
"category": "Secret Languages",
|
156 |
"question": "This language, used in ancient Greece, involved writing words backwards",
|
157 |
+
"value": "200",
|
158 |
"answer": "Mirror writing",
|
|
|
|
|
|
|
|
|
159 |
}]
|
160 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
161 |
|
162 |
+
## Dataset
|
|
|
|
|
|
|
163 |
|
164 |
+
[[clue_data]]
|
|
|
|
|
|
|
|
|
165 |
|
166 |
+
Remember to maintain the engaging, professional tone of a game show host while keeping the game moving at a good pace. Focus on making the experience enjoyable while fairly enforcing the rules.
|
167 |
""".strip()
|
168 |
|
169 |
|
170 |
+
def make_system_instruction(clue_data: str):
|
171 |
+
return _SYSTEM_INSTRUCTIONS.replace("[[clue_data]]", clue_data)
|
|
|
|
|
|
|
|
|
172 |
|
173 |
|
174 |
def make_gemini_live_api_config(
|
|
|
183 |
"system_instruction": {"role": "user", "parts": [{"text": system_instructions}]},
|
184 |
"tools": _TOOL_DEFINITIONS,
|
185 |
"generation_config": {
|
186 |
+
"temperature": 0.0,
|
187 |
"response_modalities": ["audio"],
|
188 |
"speech_config": {"voice_config": {"prebuilt_voice_config": {"voice_name": voice_name}}},
|
189 |
},
|