asdfasdfqrqwer commited on
Commit
795e0f4
·
1 Parent(s): d5e201f

Update to pixeltable 0.7.8 / gradio 5.49: UDFs to udfs.py, pinned deps, python 3.11

Browse files
Files changed (4) hide show
  1. README.md +3 -3
  2. app.py +21 -62
  3. requirements.txt +3 -5
  4. udfs.py +57 -0
README.md CHANGED
@@ -4,7 +4,7 @@ emoji: ⚔️
4
  colorFrom: gray
5
  colorTo: blue
6
  sdk: gradio
7
- sdk_version: 5.3.0
8
  app_file: app.py
9
  pinned: false
10
  license: apache-2.0
@@ -23,7 +23,7 @@ An interactive text-based RPG powered by Pixeltable and OpenAI that creates dyna
23
  - Real-time story generation
24
 
25
  ## 🛠️ Requirements
26
- - Python 3.8+
27
  - Pixeltable
28
  - OpenAI API key
29
  - Gradio
@@ -50,4 +50,4 @@ Scenario: Your smuggling ship is docked at a secret trading post in the Andromed
50
  ```
51
 
52
  ## 📝 Credits
53
- Built with [Pixeltable](https://github.com/pixeltable/pixeltable) | Licensed under Apache 2.0
 
4
  colorFrom: gray
5
  colorTo: blue
6
  sdk: gradio
7
+ sdk_version: 5.49.0
8
  app_file: app.py
9
  pinned: false
10
  license: apache-2.0
 
23
  - Real-time story generation
24
 
25
  ## 🛠️ Requirements
26
+ - Python 3.11+
27
  - Pixeltable
28
  - OpenAI API key
29
  - Gradio
 
50
  ```
51
 
52
  ## 📝 Credits
53
+ Built with [Pixeltable](https://github.com/pixeltable/pixeltable) | [Docs](https://docs.pixeltable.com) | Licensed under Apache 2.0
app.py CHANGED
@@ -1,70 +1,20 @@
 
 
 
1
  import gradio as gr
2
  import pixeltable as pxt
3
- import numpy as np
4
- from datetime import datetime
5
- from pixeltable.functions.huggingface import sentence_transformer
6
  from pixeltable.functions import openai
7
- import os
8
- import getpass
9
- import re
10
 
11
- # Set up OpenAI API key
12
- if 'OPENAI_API_KEY' not in os.environ:
13
- os.environ['OPENAI_API_KEY'] = getpass.getpass('Enter your OpenAI API key: ')
 
 
14
 
15
  # Initialize Pixeltable
16
- pxt.drop_dir('ai_rpg', force=True)
17
  pxt.create_dir('ai_rpg')
18
 
19
- @pxt.udf
20
- def generate_messages(genre: str, player_name: str, initial_scenario: str, player_input: str, turn_number: int) -> list[dict]:
21
- return [
22
- {
23
- 'role': 'system',
24
- 'content': f"""You are the game master for a {genre} RPG. The player's name is {player_name}.
25
- Provide your response in two clearly separated sections using exactly this format:
26
-
27
- STORY: [Your engaging narrative response to the player's action]
28
-
29
- OPTIONS:
30
- 1. [A dialogue option]
31
- 2. [A random action they could take]
32
- 3. [A unique or unexpected choice]"""
33
- },
34
- {
35
- 'role': 'user',
36
- 'content': f"Current scenario: {initial_scenario}\n"
37
- f"Player's action: {player_input}\n"
38
- f"Turn number: {turn_number}\n\n"
39
- "Provide the story response and options:"
40
- }
41
- ]
42
-
43
- @pxt.udf
44
- def get_story(response: str) -> str:
45
- """Extract just the story part from the response"""
46
- parts = response.split("OPTIONS:")
47
- if len(parts) != 2:
48
- return response
49
-
50
- story = parts[0].replace("STORY:", "").strip()
51
- return story
52
-
53
- @pxt.udf
54
- def get_options(response: str) -> list[str]:
55
- """Extract the options from the response"""
56
- parts = response.split("OPTIONS:")
57
- if len(parts) != 2:
58
- return ["Continue...", "Take another action", "Try something else"]
59
-
60
- options = re.findall(r'\d\.\s*(.*?)(?=\d\.|$)', parts[1], re.DOTALL)
61
- options = [opt.strip() for opt in options[:3]]
62
-
63
- while len(options) < 3:
64
- options.append("Take another action...")
65
-
66
- return options
67
-
68
  # Create a single table for all game data
69
  interactions = pxt.create_table(
70
  'ai_rpg.interactions',
@@ -91,8 +41,7 @@ interactions.add_computed_column(messages=generate_messages(
91
  interactions.add_computed_column(ai_response=openai.chat_completions(
92
  messages=interactions.messages,
93
  model='gpt-4o-mini-2024-07-18',
94
- max_tokens=500,
95
- temperature=0.8
96
  ))
97
 
98
  interactions.add_computed_column(full_response=interactions.ai_response.choices[0].message.content)
@@ -176,7 +125,13 @@ def create_interface():
176
  </div>
177
  """
178
  )
179
-
 
 
 
 
 
 
180
  with gr.Row():
181
  with gr.Column():
182
  with gr.Accordion("🎯 What does it do?", open=False):
@@ -273,6 +228,8 @@ def create_interface():
273
  )
274
 
275
  def start_new_game(name, genre_choice, scenario_text):
 
 
276
  if not name or not genre_choice or not scenario_text:
277
  return "Please fill in all fields before starting.", [], []
278
 
@@ -299,6 +256,8 @@ def create_interface():
299
  return f"Error starting game: {str(e)}", [], []
300
 
301
  def process_player_action(action_choice):
 
 
302
  try:
303
  if not action_choice:
304
  return "Please select an action to continue.", [], []
 
1
+ import os
2
+ from datetime import datetime
3
+
4
  import gradio as gr
5
  import pixeltable as pxt
 
 
 
6
  from pixeltable.functions import openai
 
 
 
7
 
8
+ from udfs import generate_messages, get_options, get_story
9
+
10
+ # The OpenAI key arrives as the OPENAI_API_KEY env var (a secret on HF Spaces).
11
+ # Never prompt on stdin: there is no TTY, so getpass() would block startup forever.
12
+ has_api_key = bool(os.environ.get('OPENAI_API_KEY'))
13
 
14
  # Initialize Pixeltable
15
+ pxt.drop_dir('ai_rpg', force=True, if_not_exists='ignore')
16
  pxt.create_dir('ai_rpg')
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  # Create a single table for all game data
19
  interactions = pxt.create_table(
20
  'ai_rpg.interactions',
 
41
  interactions.add_computed_column(ai_response=openai.chat_completions(
42
  messages=interactions.messages,
43
  model='gpt-4o-mini-2024-07-18',
44
+ model_kwargs={'max_tokens': 500, 'temperature': 0.8}
 
45
  ))
46
 
47
  interactions.add_computed_column(full_response=interactions.ai_response.choices[0].message.content)
 
125
  </div>
126
  """
127
  )
128
+
129
+ if not has_api_key:
130
+ gr.Markdown(
131
+ '> **`OPENAI_API_KEY` is not set.** '
132
+ 'Add it in the Space Settings -> Secrets tab, then restart the Space.'
133
+ )
134
+
135
  with gr.Row():
136
  with gr.Column():
137
  with gr.Accordion("🎯 What does it do?", open=False):
 
228
  )
229
 
230
  def start_new_game(name, genre_choice, scenario_text):
231
+ if not has_api_key:
232
+ return 'OPENAI_API_KEY is not set. Add it in Settings -> Secrets and restart the Space.', [], []
233
  if not name or not genre_choice or not scenario_text:
234
  return "Please fill in all fields before starting.", [], []
235
 
 
256
  return f"Error starting game: {str(e)}", [], []
257
 
258
  def process_player_action(action_choice):
259
+ if not has_api_key:
260
+ return 'OPENAI_API_KEY is not set. Add it in Settings -> Secrets and restart the Space.', [], []
261
  try:
262
  if not action_choice:
263
  return "Please select an action to continue.", [], []
requirements.txt CHANGED
@@ -1,5 +1,3 @@
1
- gradio>=4.0.0
2
- pixeltable>=0.2.20
3
- numpy>=1.21.0
4
- openai>=1.0.0
5
- python-dotenv>=0.19.0
 
1
+ pixeltable==0.7.8
2
+ openai==3.16.2
3
+ pandas==2.3.3
 
 
udfs.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+
3
+ import pixeltable as pxt
4
+
5
+
6
+ # UDFs must live in a named module: pixeltable >= 0.6 rejects @pxt.udf defined in
7
+ # the __main__ script, and HF Spaces launches this app with `python app.py`.
8
+ @pxt.udf
9
+ def generate_messages(genre: str, player_name: str, initial_scenario: str, player_input: str, turn_number: int) -> list[dict]:
10
+ return [
11
+ {
12
+ 'role': 'system',
13
+ 'content': f"""You are the game master for a {genre} RPG. The player's name is {player_name}.
14
+ Provide your response in two clearly separated sections using exactly this format:
15
+
16
+ STORY: [Your engaging narrative response to the player's action]
17
+
18
+ OPTIONS:
19
+ 1. [A dialogue option]
20
+ 2. [A random action they could take]
21
+ 3. [A unique or unexpected choice]"""
22
+ },
23
+ {
24
+ 'role': 'user',
25
+ 'content': f"Current scenario: {initial_scenario}\n"
26
+ f"Player's action: {player_input}\n"
27
+ f"Turn number: {turn_number}\n\n"
28
+ "Provide the story response and options:"
29
+ }
30
+ ]
31
+
32
+
33
+ @pxt.udf
34
+ def get_story(response: str) -> str:
35
+ """Extract just the story part from the response"""
36
+ parts = response.split('OPTIONS:')
37
+ if len(parts) != 2:
38
+ return response
39
+
40
+ story = parts[0].replace('STORY:', '').strip()
41
+ return story
42
+
43
+
44
+ @pxt.udf
45
+ def get_options(response: str) -> list[str]:
46
+ """Extract the options from the response"""
47
+ parts = response.split('OPTIONS:')
48
+ if len(parts) != 2:
49
+ return ['Continue...', 'Take another action', 'Try something else']
50
+
51
+ options = re.findall(r'\d\.\s*(.*?)(?=\d\.|$)', parts[1], re.DOTALL)
52
+ options = [opt.strip() for opt in options[:3]]
53
+
54
+ while len(options) < 3:
55
+ options.append('Take another action...')
56
+
57
+ return options