Richard commited on
Commit
08cb009
·
1 Parent(s): 38a70b9

Update system prompt

Browse files
Files changed (2) hide show
  1. main.py +19 -15
  2. 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
- categories = [question_set[0].category for question_set in state.board.clues]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  state.gemini_live_api_config = trebek_bot.make_gemini_live_api_config(
30
  system_instructions=trebek_bot.make_system_instruction(
31
- categories, json.dumps(state.board.clues, cls=PydanticJSONEncoder, indent=2, sort_keys=True)
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!. Make sure users follow the rules of the game.
39
 
40
- You have access to the following tools:
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
- Here is the JSON schema of the dataset:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 questions",
51
  "items": {
52
  "type": "array",
53
- "description": "A category of Jeopardy! questions",
54
  "items": {
55
  "type": "object",
56
- "description": "A single Jeopardy! question",
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 question"
77
  },
78
- "question": {
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
- "round": {
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 standardized value of the question"
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": "$200",
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
- When the user tries to answer the clue, explain to the user why their answer is correct
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
- Here is the dataset of clues for this Jeopardy! game:
175
-
176
- <clues-dataset>
177
- [[clue data]]
178
- </clues-dataset>
179
 
 
180
  """.strip()
181
 
182
 
183
- def make_system_instruction(categories: list[str], clue_data: str):
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.3,
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
  },