vighnesh-shetty-vs commited on
Commit
b9ed0f0
·
1 Parent(s): 17eab7c

Add updated files

Browse files
Files changed (3) hide show
  1. alternatives.py +9 -9
  2. app.py +41 -67
  3. game_data.py +47 -25
alternatives.py CHANGED
@@ -1,18 +1,18 @@
1
  ALTERNATIVES = {
2
  "simple factual question": [
3
- "Use a traditional Search Engine (like Google Search)",
4
- "Check Wikipedia or an offline encyclopedia",
5
- "Consult a subject-matter expert or ask a friend"
6
  ],
7
  "mathematical calculation": [
8
- "Use a dedicated math engine like Wolfram Alpha",
9
- "Fire up Geogebra or a standard calculator app",
10
- "Solve it on good old-fashioned paper"
11
  ],
12
  "creative content generation": [
13
- "Brainstorm with a notebook and pen",
14
- "Use offline writing prompts or a dictionary/thesaurus",
15
- "Listen to a podcast or read a book for inspiration"
16
  ],
17
  "complex research query": []
18
  }
 
1
  ALTERNATIVES = {
2
  "simple factual question": [
3
+ {"text": "Search Engine (Google)", "url": "https://www.google.com"},
4
+ {"text": "Wikipedia Encyclopedia", "url": "https://www.wikipedia.org"},
5
+ {"text": "Ask a Human Expert (Quora)", "url": "https://www.quora.com"}
6
  ],
7
  "mathematical calculation": [
8
+ {"text": "Wolfram Alpha Engine", "url": "https://www.wolframalpha.com"},
9
+ {"text": "Geogebra Calculator", "url": "https://www.geogebra.org"},
10
+ {"text": "Desmos Graphing", "url": "https://www.desmos.com"}
11
  ],
12
  "creative content generation": [
13
+ {"text": "Writing Prompts (Reddit)", "url": "https://www.reddit.com/r/WritingPrompts/"},
14
+ {"text": "Thesaurus/Dictionary", "url": "https://www.thesaurus.com"},
15
+ {"text": "Project Gutenberg Books", "url": "https://www.gutenberg.org"}
16
  ],
17
  "complex research query": []
18
  }
app.py CHANGED
@@ -4,11 +4,10 @@ import random
4
  from gtts import gTTS
5
  import os
6
 
7
- from game_data import calculate_impact
8
  from dialogues import get_dialogue
9
  from alternatives import ALTERNATIVES
10
 
11
- # --- 1. Load AI Models ---
12
  print("Loading classification model...")
13
  classifier = pipeline("zero-shot-classification", model="facebook/bart-large-mnli")
14
 
@@ -19,8 +18,6 @@ CATEGORIES = [
19
  "complex research query"
20
  ]
21
 
22
- # --- 2. Helper Functions & UI Generators ---
23
-
24
  def generate_audio(text):
25
  tts = gTTS(text, lang='en')
26
  filepath = "temp_drip_voice.mp3"
@@ -28,19 +25,18 @@ def generate_audio(text):
28
  return filepath
29
 
30
  def get_stats_html(water, energy, points):
31
- """Generates the glowing dashboard gauges for the top row."""
32
  return f"""
33
  <div class="stats-container">
34
  <div class="stat-card water-card">
35
  <div class="stat-label">WATER TANK</div>
36
- <div class="stat-value">{water:.1f}L</div>
37
  <div class="progress-bar-bg">
38
  <div class="progress-bar-fill" style="width: {water}%;"></div>
39
  </div>
40
  </div>
41
  <div class="stat-card energy-card">
42
  <div class="stat-label">ENERGY WASTED</div>
43
- <div class="stat-value">{energy:.3f} <span style="font-size: 0.5em;">kWh</span></div>
44
  </div>
45
  <div class="stat-card points-card">
46
  <div class="stat-label">SCORE</div>
@@ -50,7 +46,6 @@ def get_stats_html(water, energy, points):
50
  """
51
 
52
  def get_drip_visuals(water_level):
53
- """Generates Drip with a glowing aura based on health."""
54
  if water_level > 70:
55
  emoji, size, anim, glow, status = "💧", "120px", "float 3s ease-in-out infinite", "rgba(56,189,248,0.6)", "Plump & Hydrated"
56
  elif 30 <= water_level <= 70:
@@ -68,35 +63,33 @@ def get_drip_visuals(water_level):
68
  """
69
 
70
  def get_impact_cards(impact, category):
71
- """Generates elegant side-by-side impact cards."""
72
  return f"""
73
  <div class="impact-container">
74
  <div class="impact-card bad-impact">
75
- <h3 style="margin: 0 0 10px 0; color: #fca5a5;">🔥 LLM Impact (Massive Servers)</h3>
76
- <p style="margin:5px 0; font-size:1.2em;"><strong>{impact['water']}L</strong> Water | <strong>{impact['energy']}kWh</strong> Energy</p>
77
- <p style="margin:0; font-size:0.9em; opacity: 0.8;">Drains actual reservoirs to cool data centers.</p>
 
78
  </div>
79
  <div class="impact-card good-impact">
80
  <h3 style="margin: 0 0 10px 0; color: #86efac;">🌿 Eco-Alternative</h3>
81
- <p style="margin:5px 0; font-size:1.2em;"><strong>~0.0L</strong> Water | <strong>~0.001kWh</strong> Energy</p>
82
- <p style="margin:0; font-size:0.9em; opacity: 0.8;">Use local processing or targeted searches.</p>
 
83
  </div>
84
  </div>
85
  """
86
 
87
  def format_alt_button(text):
88
- """Adds formatting to make alternative text look like a card."""
89
  icon = "💡"
90
  if "Search" in text or "Google" in text: icon = "🔍"
91
- elif "Wikipedia" in text or "encyclopedia" in text: icon = "📚"
92
- elif "expert" in text or "friend" in text: icon = "👤"
93
- elif "calculator" in text or "Wolfram" in text: icon = "🧮"
94
- elif "pen" in text or "paper" in text or "notebook" in text: icon = "📝"
95
  return f"{icon}\n{text}"
96
 
97
- # --- 3. Core Game Logic ---
98
-
99
- def process_query(user_input, state):
100
  if state["game_over"] or not user_input.strip():
101
  return [state, gr.update()] + [gr.update()]*9
102
 
@@ -104,32 +97,32 @@ def process_query(user_input, state):
104
  category = result['labels'][0]
105
  confidence = result['scores'][0]
106
 
107
- impact = calculate_impact(category, confidence, user_input)
108
 
109
  state["queries"] += 1
110
- state["water"] = max(0, state["water"] - impact["water"])
111
- state["energy"] += impact["energy"]
112
 
113
  show_alts = False
114
  alt_choices = ["", "", ""]
115
 
116
  if category == "complex research query":
117
  state["points"] += 50
118
- dialogue = "Finally, a real question! Draining my servers, but totally justified."
119
  else:
120
  show_alts = True
121
- raw_alts = ALTERNATIVES.get(category, ["Google", "Wikipedia", "Book"])
122
  state["current_best_alt"] = raw_alts[0]
123
  random.shuffle(raw_alts)
124
- alt_choices = [format_alt_button(a) for a in raw_alts]
125
- state["current_shuffled_alts"] = raw_alts # Store raw text to check answers
126
  dialogue = get_dialogue(state["water"], category)
127
 
128
  stats_ui = get_stats_html(state["water"], state["energy"], state["points"])
129
  drip_ui = get_drip_visuals(state["water"])
130
  impact_ui = get_impact_cards(impact, category)
131
  audio_path = generate_audio(dialogue)
132
- feedback_text = f"### 🧠 **{category.upper()}** Detected!\n*{dialogue}*"
133
 
134
  if state["points"] >= 500 or state["water"] <= 0:
135
  state["game_over"] = True
@@ -142,33 +135,25 @@ def select_alternative(choice_text, state):
142
  if state["game_over"] or not state.get("current_best_alt"):
143
  return state, gr.update(), gr.update(), gr.update()
144
 
145
- # Strip the emoji from the button text to compare with the raw dictionary string
146
  clean_choice = choice_text.split("\n")[-1]
 
147
 
148
- if clean_choice == state["current_best_alt"]:
149
  state["points"] += 10
150
- msg = "✅ **Excellent!** +10 Points. You saved massive server resources."
151
  else:
152
- msg = "⚠️ **Okay choice.** It's better than an LLM, but there was a slightly more optimal tool for this specific task."
153
 
154
  stats_ui = get_stats_html(state["water"], state["energy"], state["points"])
155
 
156
  return state, stats_ui, gr.update(value=msg), gr.update(visible=False)
157
 
158
- # --- 4. Beautiful Frontend Layout ---
159
-
160
  custom_css = """
161
  @import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;500;700&display=swap');
162
-
163
- /* Main Theme */
164
  body, .gradio-container { background-color: #0B1120 !important; font-family: 'Outfit', sans-serif !important; color: #e2e8f0 !important; }
165
  .gradio-container { max-width: 900px !important; margin: auto; padding-top: 20px; }
166
-
167
- /* Typography & Headers */
168
  h1 { text-align: center; color: #38bdf8; text-shadow: 0 0 15px rgba(56, 189, 248, 0.5); font-size: 2.5em !important; margin-bottom: 5px !important;}
169
  .subtitle { text-align: center; color: #94a3b8; margin-bottom: 30px; font-weight: 300; }
170
-
171
- /* Dashboard Stats */
172
  .stats-container { display: flex; gap: 15px; justify-content: space-between; margin-bottom: 20px; }
173
  .stat-card { flex: 1; padding: 15px; border-radius: 16px; text-align: center; background: rgba(30, 41, 59, 0.6); backdrop-filter: blur(10px); border: 1px solid rgba(255,255,255,0.1); }
174
  .water-card { box-shadow: 0 4px 20px rgba(56, 189, 248, 0.15); border-top: 2px solid #38bdf8; }
@@ -178,73 +163,62 @@ h1 { text-align: center; color: #38bdf8; text-shadow: 0 0 15px rgba(56, 189, 248
178
  .stat-value { font-size: 2em; font-weight: 700; color: white; }
179
  .progress-bar-bg { background: #1e293b; height: 8px; border-radius: 4px; margin-top: 10px; overflow: hidden; }
180
  .progress-bar-fill { background: linear-gradient(90deg, #0284c7, #38bdf8); height: 100%; transition: width 0.5s ease; }
181
-
182
- /* Integrated Drip Pod (Character + Audio) */
183
  .drip-pod { background: rgba(15, 23, 42, 0.6); border-radius: 24px; border: 1px solid rgba(56, 189, 248, 0.2); padding: 30px 20px; display: flex; flex-direction: column; align-items: center; justify-content: center; box-shadow: inset 0 0 40px rgba(0,0,0,0.5); }
184
  .drip-character { width: 160px; height: 160px; border-radius: 50%; background: radial-gradient(circle, rgba(56,189,248,0.15) 0%, transparent 70%); display: flex; flex-direction: column; align-items: center; justify-content: center; margin-bottom: 10px; }
185
  @keyframes float { 0% { transform: translateY(0px); } 50% { transform: translateY(-15px); } 100% { transform: translateY(0px); } }
186
-
187
- /* Customize the Audio Player to blend in */
188
- .drip-audio { background: transparent !important; border: none !important; box-shadow: none !important; width: 100%; max-width: 300px; filter: invert(1) hue-rotate(180deg) brightness(1.5); /* Tricks to make default audio player match dark theme */ }
189
-
190
- /* Input Area */
191
- .input-row { align-items: stretch !important; margin: 20px 0; }
192
  .custom-textbox textarea { background: rgba(30, 41, 59, 0.8) !important; border: 1px solid #38bdf8 !important; color: white !important; font-size: 1.1em !important; border-radius: 12px !important; box-shadow: 0 0 15px rgba(56, 189, 248, 0.1) !important; padding: 15px !important; }
193
- .custom-btn { background: linear-gradient(135deg, #0284c7, #0ea5e9) !important; border: none !important; color: white !important; font-weight: bold !important; font-size: 1.1em !important; border-radius: 12px !important; transition: all 0.3s !important; }
194
  .custom-btn:hover { box-shadow: 0 0 20px rgba(56, 189, 248, 0.6) !important; transform: scale(1.02); }
195
-
196
- /* Impact Cards */
197
  .impact-container { display: flex; gap: 20px; width: 100%; }
198
  .impact-card { flex: 1; padding: 20px; border-radius: 16px; border: 1px solid rgba(255,255,255,0.1); }
199
  .bad-impact { background: linear-gradient(180deg, rgba(127, 29, 29, 0.3), rgba(69, 10, 10, 0.5)); border-top: 3px solid #ef4444; }
200
  .good-impact { background: linear-gradient(180deg, rgba(20, 83, 45, 0.3), rgba(6, 78, 59, 0.5)); border-top: 3px solid #10b981; }
201
-
202
- /* Interactive Alternative Cards */
203
  .alt-group { padding: 20px; background: rgba(16, 185, 129, 0.05); border-radius: 16px; border: 1px dashed #10b981; margin-top: 20px;}
204
  .alt-card { background: rgba(30, 41, 59, 0.8) !important; border: 1px solid #10b981 !important; color: #a7f3d0 !important; height: 120px !important; border-radius: 16px !important; font-size: 1.1em !important; white-space: pre-wrap !important; transition: all 0.2s ease !important; cursor: pointer; display: flex; flex-direction: column; justify-content: center; align-items: center; box-shadow: 0 4px 15px rgba(0,0,0,0.3) !important;}
205
  .alt-card:hover { background: rgba(16, 185, 129, 0.2) !important; transform: translateY(-5px) !important; box-shadow: 0 8px 25px rgba(16, 185, 129, 0.4) !important; color: white !important;}
 
 
206
  """
207
 
208
- with gr.Blocks(css=custom_css, title="EcoQueryQuest") as demo:
209
  game_state = gr.State({
210
  "water": 100.0, "energy": 0.0, "points": 0, "queries": 0,
211
  "history": [], "game_over": False, "current_best_alt": None, "current_shuffled_alts": []
212
  })
213
 
214
  gr.Markdown("<h1>🌍 EcoQueryQuest</h1>")
215
- gr.Markdown("<div class='subtitle'>Type a query. Watch Drip react. Reach 500 points to win, but don't run out of water!</div>")
216
 
217
- # TOP: Dashboard Stats
218
  stats_html = gr.HTML(get_stats_html(100.0, 0.0, 0))
219
 
220
  with gr.Row():
221
- # LEFT: Drip Character & Audio Integrated
222
  with gr.Column(scale=1, elem_classes=["drip-pod"]):
223
  drip_html = gr.HTML(get_drip_visuals(100))
224
  drip_audio = gr.Audio(label="Drip's Voice", autoplay=True, interactive=False, elem_classes=["drip-audio"])
225
-
226
- # RIGHT: Interaction & Feedback
227
  with gr.Column(scale=2):
 
 
228
  with gr.Row(elem_classes=["input-row"]):
229
- user_input = gr.Textbox(show_label=False, placeholder="Write an email to my manager asking for sick leave...", elem_classes=["custom-textbox"], scale=4)
230
  submit_btn = gr.Button("Send Query", elem_classes=["custom-btn"], scale=1)
231
 
232
  drip_feedback = gr.Markdown("### Waiting for your first query...")
233
  impact_display = gr.HTML()
234
 
235
- # BOTTOM: Interactive Alternatives Section
236
  with gr.Group(visible=False, elem_classes=["alt-group"]) as alternatives_group:
237
  gr.Markdown("<h3 style='text-align:center; color:#34d399; margin-bottom: 15px;'>🌱 Drip says: 'Quick! Pick a greener tool to earn points!'</h3>")
238
  with gr.Row():
239
  alt_btn_1 = gr.Button("", elem_classes=["alt-card"])
240
  alt_btn_2 = gr.Button("", elem_classes=["alt-card"])
241
  alt_btn_3 = gr.Button("", elem_classes=["alt-card"])
242
- alt_feedback = gr.Markdown(elem_classes=["glow-text"])
243
 
244
- # EVENT WIRING
245
  submit_btn.click(
246
  fn=process_query,
247
- inputs=[user_input, game_state],
248
  outputs=[game_state, stats_html, drip_html, drip_audio, impact_display, drip_feedback, alternatives_group, alt_btn_1, alt_btn_2, alt_btn_3, alt_feedback]
249
  )
250
 
@@ -256,4 +230,4 @@ with gr.Blocks(css=custom_css, title="EcoQueryQuest") as demo:
256
  )
257
 
258
  if __name__ == "__main__":
259
- demo.launch()
 
4
  from gtts import gTTS
5
  import os
6
 
7
+ from game_data import calculate_impact, MODELS
8
  from dialogues import get_dialogue
9
  from alternatives import ALTERNATIVES
10
 
 
11
  print("Loading classification model...")
12
  classifier = pipeline("zero-shot-classification", model="facebook/bart-large-mnli")
13
 
 
18
  "complex research query"
19
  ]
20
 
 
 
21
  def generate_audio(text):
22
  tts = gTTS(text, lang='en')
23
  filepath = "temp_drip_voice.mp3"
 
25
  return filepath
26
 
27
  def get_stats_html(water, energy, points):
 
28
  return f"""
29
  <div class="stats-container">
30
  <div class="stat-card water-card">
31
  <div class="stat-label">WATER TANK</div>
32
+ <div class="stat-value">{water:.2f}L</div>
33
  <div class="progress-bar-bg">
34
  <div class="progress-bar-fill" style="width: {water}%;"></div>
35
  </div>
36
  </div>
37
  <div class="stat-card energy-card">
38
  <div class="stat-label">ENERGY WASTED</div>
39
+ <div class="stat-value">{energy:.4f} <span style="font-size: 0.5em;">kWh</span></div>
40
  </div>
41
  <div class="stat-card points-card">
42
  <div class="stat-label">SCORE</div>
 
46
  """
47
 
48
  def get_drip_visuals(water_level):
 
49
  if water_level > 70:
50
  emoji, size, anim, glow, status = "💧", "120px", "float 3s ease-in-out infinite", "rgba(56,189,248,0.6)", "Plump & Hydrated"
51
  elif 30 <= water_level <= 70:
 
63
  """
64
 
65
  def get_impact_cards(impact, category):
 
66
  return f"""
67
  <div class="impact-container">
68
  <div class="impact-card bad-impact">
69
+ <h3 style="margin: 0 0 10px 0; color: #fca5a5;">🔥 LLM Impact</h3>
70
+ <p style="margin:5px 0; font-size:1.2em;"><strong>{impact['water_ml']} mL</strong> Water</p>
71
+ <p style="margin:5px 0; font-size:1.2em;"><strong>{impact['energy_wh']} Wh</strong> Energy</p>
72
+ <p style="margin:0; font-size:0.9em; opacity: 0.8;">Deducted from global reserves.</p>
73
  </div>
74
  <div class="impact-card good-impact">
75
  <h3 style="margin: 0 0 10px 0; color: #86efac;">🌿 Eco-Alternative</h3>
76
+ <p style="margin:5px 0; font-size:1.2em;"><strong>~0.0 mL</strong> Water</p>
77
+ <p style="margin:5px 0; font-size:1.2em;"><strong>~0.001 Wh</strong> Energy</p>
78
+ <p style="margin:0; font-size:0.9em; opacity: 0.8;">Near-zero footprint.</p>
79
  </div>
80
  </div>
81
  """
82
 
83
  def format_alt_button(text):
 
84
  icon = "💡"
85
  if "Search" in text or "Google" in text: icon = "🔍"
86
+ elif "Wikipedia" in text or "Encyclopedia" in text: icon = "📚"
87
+ elif "Expert" in text or "Quora" in text: icon = "👤"
88
+ elif "Wolfram" in text or "Geogebra" in text or "Desmos" in text: icon = "🧮"
89
+ elif "Reddit" in text or "Gutenberg" in text or "Thesaurus" in text: icon = "📝"
90
  return f"{icon}\n{text}"
91
 
92
+ def process_query(user_input, selected_model, state):
 
 
93
  if state["game_over"] or not user_input.strip():
94
  return [state, gr.update()] + [gr.update()]*9
95
 
 
97
  category = result['labels'][0]
98
  confidence = result['scores'][0]
99
 
100
+ impact = calculate_impact(category, confidence, user_input, selected_model)
101
 
102
  state["queries"] += 1
103
+ state["water"] = max(0, state["water"] - impact["water_l"])
104
+ state["energy"] += impact["energy_kwh"]
105
 
106
  show_alts = False
107
  alt_choices = ["", "", ""]
108
 
109
  if category == "complex research query":
110
  state["points"] += 50
111
+ dialogue = f"Finally, a real question! Draining my {selected_model} servers, but totally justified."
112
  else:
113
  show_alts = True
114
+ raw_alts = ALTERNATIVES.get(category, [{"text": "Google", "url": "https://google.com"}])
115
  state["current_best_alt"] = raw_alts[0]
116
  random.shuffle(raw_alts)
117
+ state["current_shuffled_alts"] = raw_alts
118
+ alt_choices = [format_alt_button(a["text"]) for a in raw_alts]
119
  dialogue = get_dialogue(state["water"], category)
120
 
121
  stats_ui = get_stats_html(state["water"], state["energy"], state["points"])
122
  drip_ui = get_drip_visuals(state["water"])
123
  impact_ui = get_impact_cards(impact, category)
124
  audio_path = generate_audio(dialogue)
125
+ feedback_text = f"### 🧠 **{category.upper()}** Detected on **{selected_model}**!\n*{dialogue}*"
126
 
127
  if state["points"] >= 500 or state["water"] <= 0:
128
  state["game_over"] = True
 
135
  if state["game_over"] or not state.get("current_best_alt"):
136
  return state, gr.update(), gr.update(), gr.update()
137
 
 
138
  clean_choice = choice_text.split("\n")[-1]
139
+ selected_dict = next((item for item in state["current_shuffled_alts"] if item["text"] == clean_choice), None)
140
 
141
+ if clean_choice == state["current_best_alt"]["text"]:
142
  state["points"] += 10
143
+ msg = f"✅ **Excellent!** +10 Points.\n\n<a href='{selected_dict['url']}' target='_blank' style='display:inline-block; margin-top:10px; padding: 10px 20px; background:#10b981; color:white; text-decoration:none; border-radius:8px; font-weight:bold;'>👉 Click here to use {clean_choice}</a>"
144
  else:
145
+ msg = f"⚠️ **Okay choice.** Better than an LLM, but there was a slightly more optimal tool.\n\n<a href='{selected_dict['url']}' target='_blank' style='display:inline-block; margin-top:10px; padding: 10px 20px; background:#38bdf8; color:white; text-decoration:none; border-radius:8px; font-weight:bold;'>👉 Click here to use {clean_choice}</a>"
146
 
147
  stats_ui = get_stats_html(state["water"], state["energy"], state["points"])
148
 
149
  return state, stats_ui, gr.update(value=msg), gr.update(visible=False)
150
 
 
 
151
  custom_css = """
152
  @import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;500;700&display=swap');
 
 
153
  body, .gradio-container { background-color: #0B1120 !important; font-family: 'Outfit', sans-serif !important; color: #e2e8f0 !important; }
154
  .gradio-container { max-width: 900px !important; margin: auto; padding-top: 20px; }
 
 
155
  h1 { text-align: center; color: #38bdf8; text-shadow: 0 0 15px rgba(56, 189, 248, 0.5); font-size: 2.5em !important; margin-bottom: 5px !important;}
156
  .subtitle { text-align: center; color: #94a3b8; margin-bottom: 30px; font-weight: 300; }
 
 
157
  .stats-container { display: flex; gap: 15px; justify-content: space-between; margin-bottom: 20px; }
158
  .stat-card { flex: 1; padding: 15px; border-radius: 16px; text-align: center; background: rgba(30, 41, 59, 0.6); backdrop-filter: blur(10px); border: 1px solid rgba(255,255,255,0.1); }
159
  .water-card { box-shadow: 0 4px 20px rgba(56, 189, 248, 0.15); border-top: 2px solid #38bdf8; }
 
163
  .stat-value { font-size: 2em; font-weight: 700; color: white; }
164
  .progress-bar-bg { background: #1e293b; height: 8px; border-radius: 4px; margin-top: 10px; overflow: hidden; }
165
  .progress-bar-fill { background: linear-gradient(90deg, #0284c7, #38bdf8); height: 100%; transition: width 0.5s ease; }
 
 
166
  .drip-pod { background: rgba(15, 23, 42, 0.6); border-radius: 24px; border: 1px solid rgba(56, 189, 248, 0.2); padding: 30px 20px; display: flex; flex-direction: column; align-items: center; justify-content: center; box-shadow: inset 0 0 40px rgba(0,0,0,0.5); }
167
  .drip-character { width: 160px; height: 160px; border-radius: 50%; background: radial-gradient(circle, rgba(56,189,248,0.15) 0%, transparent 70%); display: flex; flex-direction: column; align-items: center; justify-content: center; margin-bottom: 10px; }
168
  @keyframes float { 0% { transform: translateY(0px); } 50% { transform: translateY(-15px); } 100% { transform: translateY(0px); } }
169
+ .drip-audio { background: transparent !important; border: none !important; box-shadow: none !important; width: 100%; max-width: 300px; filter: invert(1) hue-rotate(180deg) brightness(1.5); }
170
+ .input-row { align-items: stretch !important; margin: 10px 0; }
 
 
 
 
171
  .custom-textbox textarea { background: rgba(30, 41, 59, 0.8) !important; border: 1px solid #38bdf8 !important; color: white !important; font-size: 1.1em !important; border-radius: 12px !important; box-shadow: 0 0 15px rgba(56, 189, 248, 0.1) !important; padding: 15px !important; }
172
+ .custom-btn { background: linear-gradient(135deg, #0284c7, #0ea5e9) !important; border: none !important; color: white !important; font-weight: bold !important; font-size: 1.1em !important; border-radius: 12px !important; transition: all 0.3s !important; height: 100%; }
173
  .custom-btn:hover { box-shadow: 0 0 20px rgba(56, 189, 248, 0.6) !important; transform: scale(1.02); }
 
 
174
  .impact-container { display: flex; gap: 20px; width: 100%; }
175
  .impact-card { flex: 1; padding: 20px; border-radius: 16px; border: 1px solid rgba(255,255,255,0.1); }
176
  .bad-impact { background: linear-gradient(180deg, rgba(127, 29, 29, 0.3), rgba(69, 10, 10, 0.5)); border-top: 3px solid #ef4444; }
177
  .good-impact { background: linear-gradient(180deg, rgba(20, 83, 45, 0.3), rgba(6, 78, 59, 0.5)); border-top: 3px solid #10b981; }
 
 
178
  .alt-group { padding: 20px; background: rgba(16, 185, 129, 0.05); border-radius: 16px; border: 1px dashed #10b981; margin-top: 20px;}
179
  .alt-card { background: rgba(30, 41, 59, 0.8) !important; border: 1px solid #10b981 !important; color: #a7f3d0 !important; height: 120px !important; border-radius: 16px !important; font-size: 1.1em !important; white-space: pre-wrap !important; transition: all 0.2s ease !important; cursor: pointer; display: flex; flex-direction: column; justify-content: center; align-items: center; box-shadow: 0 4px 15px rgba(0,0,0,0.3) !important;}
180
  .alt-card:hover { background: rgba(16, 185, 129, 0.2) !important; transform: translateY(-5px) !important; box-shadow: 0 8px 25px rgba(16, 185, 129, 0.4) !important; color: white !important;}
181
+ /* Dropdown styling for dark theme */
182
+ .custom-dropdown { background: rgba(30, 41, 59, 0.8) !important; border: 1px solid #38bdf8 !important; border-radius: 12px !important; }
183
  """
184
 
185
+ with gr.Blocks(title="EcoQueryQuest") as demo:
186
  game_state = gr.State({
187
  "water": 100.0, "energy": 0.0, "points": 0, "queries": 0,
188
  "history": [], "game_over": False, "current_best_alt": None, "current_shuffled_alts": []
189
  })
190
 
191
  gr.Markdown("<h1>🌍 EcoQueryQuest</h1>")
192
+ gr.Markdown("<div class='subtitle'>Select a model, type a query, and watch Drip react. Reach 500 points to win!</div>")
193
 
 
194
  stats_html = gr.HTML(get_stats_html(100.0, 0.0, 0))
195
 
196
  with gr.Row():
 
197
  with gr.Column(scale=1, elem_classes=["drip-pod"]):
198
  drip_html = gr.HTML(get_drip_visuals(100))
199
  drip_audio = gr.Audio(label="Drip's Voice", autoplay=True, interactive=False, elem_classes=["drip-audio"])
200
+
 
201
  with gr.Column(scale=2):
202
+ model_selector = gr.Dropdown(choices=list(MODELS.keys()), value="GPT-5.4", label="Select Target LLM Backend", elem_classes=["custom-dropdown"])
203
+
204
  with gr.Row(elem_classes=["input-row"]):
205
+ user_input = gr.Textbox(show_label=False, placeholder="Type your query here...", elem_classes=["custom-textbox"], scale=4)
206
  submit_btn = gr.Button("Send Query", elem_classes=["custom-btn"], scale=1)
207
 
208
  drip_feedback = gr.Markdown("### Waiting for your first query...")
209
  impact_display = gr.HTML()
210
 
 
211
  with gr.Group(visible=False, elem_classes=["alt-group"]) as alternatives_group:
212
  gr.Markdown("<h3 style='text-align:center; color:#34d399; margin-bottom: 15px;'>🌱 Drip says: 'Quick! Pick a greener tool to earn points!'</h3>")
213
  with gr.Row():
214
  alt_btn_1 = gr.Button("", elem_classes=["alt-card"])
215
  alt_btn_2 = gr.Button("", elem_classes=["alt-card"])
216
  alt_btn_3 = gr.Button("", elem_classes=["alt-card"])
217
+ alt_feedback = gr.Markdown()
218
 
 
219
  submit_btn.click(
220
  fn=process_query,
221
+ inputs=[user_input, model_selector, game_state],
222
  outputs=[game_state, stats_html, drip_html, drip_audio, impact_display, drip_feedback, alternatives_group, alt_btn_1, alt_btn_2, alt_btn_3, alt_feedback]
223
  )
224
 
 
230
  )
231
 
232
  if __name__ == "__main__":
233
+ demo.launch(css=custom_css)
game_data.py CHANGED
@@ -1,34 +1,56 @@
1
- # Base costs mapping: {"category": {"water": L, "energy": kWh}}
2
- BASE_COSTS = {
3
- "simple factual question": {"water": 0.5, "energy": 0.01},
4
- "mathematical calculation": {"water": 0.3, "energy": 0.005},
5
- "creative content generation": {"water": 5.0, "energy": 0.5},
6
- "complex research query": {"water": 10.0, "energy": 1.0}
 
 
 
 
7
  }
8
 
9
- def get_confidence_multiplier(confidence_score):
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  if confidence_score > 0.8:
11
- return 1.0
12
  elif 0.5 <= confidence_score <= 0.8:
13
- return 1.2 # Add 20% uncertainty buffer
14
  else:
15
- return 1.5 # Add 50% buffer
16
-
17
- def get_length_factor(query_text):
18
  word_count = len(query_text.split())
19
  if word_count < 20:
20
- return 1.0
21
  elif 20 <= word_count <= 50:
22
- return 1.3
23
  else:
24
- return 1.5
25
-
26
- def calculate_impact(category, confidence_score, query_text):
27
- base_cost = BASE_COSTS.get(category, {"water": 0, "energy": 0})
28
- conf_mult = get_confidence_multiplier(confidence_score)
29
- len_factor = get_length_factor(query_text)
30
-
31
- water_impact = base_cost["water"] * conf_mult * len_factor
32
- energy_impact = base_cost["energy"] * conf_mult * len_factor
33
-
34
- return {"water": round(water_impact, 2), "energy": round(energy_impact, 3)}
 
 
 
 
 
 
1
+ # Model Baselines (per 1000 tokens)
2
+ MODELS = {
3
+ "Mistral Large 3": {"energy_wh": 1.98, "water_ml": 3.56},
4
+ "Mistral Medium 3": {"energy_wh": 6.76, "water_ml": 12.17},
5
+ "Claude Sonnet 4.6": {"energy_wh": 19.32, "water_ml": 34.78},
6
+ "Gemini 3 Pro": {"energy_wh": 72.45, "water_ml": 130.41},
7
+ "Gemini 3.1 Pro": {"energy_wh": 77.28, "water_ml": 139.10},
8
+ "Claude Opus 4.6": {"energy_wh": 96.60, "water_ml": 173.88},
9
+ "GPT-5.2": {"energy_wh": 144.90, "water_ml": 260.82},
10
+ "GPT-5.4": {"energy_wh": 169.05, "water_ml": 304.29}
11
  }
12
 
13
+ # Category Token Multipliers
14
+ CATEGORY_MULTIPLIERS = {
15
+ "simple factual question": 0.05, # 50 tokens
16
+ "mathematical calculation": 0.10, # 100 tokens
17
+ "creative content generation": 0.80, # 800 tokens
18
+ "complex research query": 1.50 # 1500 tokens
19
+ }
20
+
21
+ def calculate_impact(category, confidence_score, query_text, model_name):
22
+ # Get base costs
23
+ model_cost = MODELS.get(model_name, MODELS["Mistral Large 3"])
24
+ cat_mult = CATEGORY_MULTIPLIERS.get(category, 0.05)
25
+
26
+ # 1. Confidence Multiplier
27
  if confidence_score > 0.8:
28
+ conf_mult = 1.0
29
  elif 0.5 <= confidence_score <= 0.8:
30
+ conf_mult = 1.2
31
  else:
32
+ conf_mult = 1.5
33
+
34
+ # 2. Length Factor
35
  word_count = len(query_text.split())
36
  if word_count < 20:
37
+ len_factor = 1.0
38
  elif 20 <= word_count <= 50:
39
+ len_factor = 1.3
40
  else:
41
+ len_factor = 1.5
42
+
43
+ # 3. Final Calculation Formula
44
+ water_ml = model_cost["water_ml"] * cat_mult * conf_mult * len_factor
45
+ energy_wh = model_cost["energy_wh"] * cat_mult * conf_mult * len_factor
46
+
47
+ # Convert for main tank UI (Liters and kWh)
48
+ water_l = water_ml / 1000.0
49
+ energy_kwh = energy_wh / 1000.0
50
+
51
+ return {
52
+ "water_l": round(water_l, 4),
53
+ "energy_kwh": round(energy_kwh, 5),
54
+ "water_ml": round(water_ml, 1),
55
+ "energy_wh": round(energy_wh, 2)
56
+ }