Chrisyichuan commited on
Commit
d0dda9e
·
1 Parent(s): bace565

add raw online mode

Browse files
Files changed (2) hide show
  1. app.py +180 -107
  2. mapcrunch_controller.py +10 -0
app.py CHANGED
@@ -2,6 +2,7 @@ import streamlit as st
2
  import json
3
  import os
4
  import time
 
5
  from io import BytesIO
6
  from PIL import Image
7
  from pathlib import Path
@@ -25,6 +26,24 @@ if "HF_TOKEN" in st.secrets:
25
  os.environ["HF_TOKEN"] = st.secrets["HF_TOKEN"]
26
 
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  def get_available_datasets():
29
  datasets_dir = Path("datasets")
30
  if not datasets_dir.exists():
@@ -60,46 +79,84 @@ st.markdown("### *The all-knowing AI that sees everything, knows everything*")
60
  with st.sidebar:
61
  st.header("Configuration")
62
 
63
- # Get available datasets and ensure we have a valid default
64
- available_datasets = get_available_datasets()
65
- default_dataset = available_datasets[0] if available_datasets else "default"
66
-
67
- dataset_choice = st.selectbox("Dataset", available_datasets, index=0)
68
- model_choice = st.selectbox("Model", list(MODELS_CONFIG.keys()), index=list(MODELS_CONFIG.keys()).index(DEFAULT_MODEL))
69
- steps_per_sample = st.slider("Max Steps", 1, 20, 10)
70
- temperature = st.slider(
71
- "Temperature",
72
- 0.0,
73
- 2.0,
74
- DEFAULT_TEMPERATURE,
75
- 0.1,
76
- help="Controls randomness in AI responses. 0.0 = deterministic, higher = more creative",
77
- )
78
-
79
- # Load dataset with error handling
80
- data_paths = get_data_paths(dataset_choice)
81
- try:
82
- with open(data_paths["golden_labels"], "r") as f:
83
- golden_labels = json.load(f).get("samples", [])
84
 
85
- st.info(f"Dataset '{dataset_choice}' has {len(golden_labels)} samples")
86
- if len(golden_labels) == 0:
87
- st.error(f"Dataset '{dataset_choice}' contains no samples!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  st.stop()
89
 
90
- except FileNotFoundError:
91
- st.error(
92
- f"❌ Dataset '{dataset_choice}' not found at {data_paths['golden_labels']}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  )
94
- st.info("💡 Available datasets: " + ", ".join(available_datasets))
95
- st.stop()
96
- except Exception as e:
97
- st.error(f"❌ Error loading dataset '{dataset_choice}': {str(e)}")
98
- st.stop()
99
-
100
- num_samples = st.slider(
101
- "Samples to Test", 1, len(golden_labels), min(3, len(golden_labels))
102
- )
103
 
104
  start_button = st.button("🚀 Start", type="primary")
105
 
@@ -109,117 +166,136 @@ if start_button:
109
  config = MODELS_CONFIG[model_choice]
110
  model_class = get_model_class(config["class"])
111
 
112
- benchmark_helper = MapGuesserBenchmark(dataset_name=dataset_choice)
113
  all_results = []
114
 
115
  progress_bar = st.progress(0)
116
 
117
  with GeoBot(
118
- model=model_class,
119
- model_name=config["model_name"],
120
- headless=True,
121
- temperature=temperature,
122
  ) as bot:
123
  for i, sample in enumerate(test_samples):
124
  st.divider()
125
  st.header(f"Sample {i + 1}/{num_samples}")
126
 
127
- # Load the sample location
128
- bot.controller.load_location_from_data(sample)
 
 
 
 
 
 
129
 
130
- # Create containers for UI updates
131
  sample_container = st.container()
132
 
133
- # Initialize UI state for this sample
134
- step_containers = {}
135
- sample_steps_data = []
136
-
137
- def ui_step_callback(step_info):
138
- """Callback function to update UI after each step"""
139
- step_num = step_info["step_num"]
140
 
141
- # Store step data
142
- sample_steps_data.append(step_info)
143
 
144
- with sample_container:
145
- # Create step container if it doesn't exist
146
- if step_num not in step_containers:
147
- step_containers[step_num] = st.container()
148
 
149
- with step_containers[step_num]:
150
- st.subheader(f"Step {step_num}/{step_info['max_steps']}")
 
151
 
152
  col1, col2 = st.columns([1, 2])
153
 
154
  with col1:
155
- # Display screenshot
156
  st.image(
157
- step_info["screenshot_bytes"],
158
  caption=f"What AI sees - Step {step_num}",
159
  use_column_width=True,
160
  )
161
 
162
  with col2:
163
- # Show available actions
164
- st.write("**Available Actions:**")
165
- st.code(
166
- json.dumps(step_info["available_actions"], indent=2)
167
  )
 
 
168
 
169
- # Show history context - use the history from step_info
170
- current_history = step_info.get("history", [])
171
- history_text = bot.generate_history_text(current_history)
 
 
 
172
  st.write("**AI Context:**")
173
  st.text_area(
174
  "History",
175
  history_text,
176
  height=100,
177
  disabled=True,
178
- key=f"history_{i}_{step_num}",
179
- )
180
-
181
- # Show AI reasoning and action
182
- action = step_info.get("action_details", {}).get(
183
- "action", "N/A"
184
  )
185
 
186
- if step_info.get("is_final_step") and action != "GUESS":
 
 
187
  st.warning("Max steps reached. Forcing GUESS.")
 
 
 
 
 
 
 
 
 
 
 
188
 
189
- st.write("**AI Reasoning:**")
190
- st.info(step_info.get("reasoning", "N/A"))
191
 
192
- st.write("**AI Action:**")
193
- if action == "GUESS":
194
- lat = step_info.get("action_details", {}).get("lat")
195
- lon = step_info.get("action_details", {}).get("lon")
196
- st.success(f"`{action}` - {lat:.4f}, {lon:.4f}")
197
- else:
198
- st.success(f"`{action}`")
199
 
200
- # Show decision details for debugging
201
- with st.expander("Decision Details"):
202
- decision_data = {
203
- "reasoning": step_info.get("reasoning"),
204
- "action_details": step_info.get("action_details"),
205
- "remaining_steps": step_info.get("remaining_steps"),
206
- }
207
- st.json(decision_data)
208
 
209
- # Force UI refresh
210
- time.sleep(0.5) # Small delay to ensure UI updates are visible
211
 
212
- # Run the agent loop with UI callback
213
- try:
214
- final_guess = bot.run_agent_loop(
215
- max_steps=steps_per_sample, step_callback=ui_step_callback
216
- )
217
- except Exception as e:
218
- st.error(f"Error during agent execution: {e}")
219
- final_guess = None
220
 
221
- # Sample Results
222
- with sample_container:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  st.subheader("Sample Result")
224
  true_coords = {"lat": sample.get("lat"), "lng": sample.get("lng")}
225
  distance_km = None
@@ -252,9 +328,6 @@ if start_button:
252
  {
253
  "sample_id": sample.get("id"),
254
  "model": model_choice,
255
- "steps_taken": len(sample_steps_data),
256
- "max_steps": steps_per_sample,
257
- "temperature": temperature,
258
  "true_coordinates": true_coords,
259
  "predicted_coordinates": final_guess,
260
  "distance_km": distance_km,
 
2
  import json
3
  import os
4
  import time
5
+ import re
6
  from io import BytesIO
7
  from PIL import Image
8
  from pathlib import Path
 
26
  os.environ["HF_TOKEN"] = st.secrets["HF_TOKEN"]
27
 
28
 
29
+ def convert_google_to_mapcrunch_url(google_url):
30
+ """Convert Google Maps URL to MapCrunch URL format."""
31
+ try:
32
+ # Extract coordinates using regex
33
+ match = re.search(r'@(-?\d+\.\d+),(-?\d+\.\d+)', google_url)
34
+ if not match:
35
+ return None
36
+
37
+ lat, lon = match.groups()
38
+ # MapCrunch format: lat_lon_heading_pitch_zoom
39
+ # Using default values for heading (317.72), pitch (0.86), and zoom (0)
40
+ mapcrunch_url = f"http://www.mapcrunch.com/p/{lat}_{lon}_317.72_0.86_0"
41
+ return mapcrunch_url
42
+ except Exception as e:
43
+ st.error(f"Error converting URL: {str(e)}")
44
+ return None
45
+
46
+
47
  def get_available_datasets():
48
  datasets_dir = Path("datasets")
49
  if not datasets_dir.exists():
 
79
  with st.sidebar:
80
  st.header("Configuration")
81
 
82
+ # Mode selection
83
+ mode = st.radio("Mode", ["Dataset Mode", "Online Mode"], index=0)
84
+
85
+ if mode == "Dataset Mode":
86
+ # Get available datasets and ensure we have a valid default
87
+ available_datasets = get_available_datasets()
88
+ default_dataset = available_datasets[0] if available_datasets else "default"
89
+
90
+ dataset_choice = st.selectbox("Dataset", available_datasets, index=0)
91
+ model_choice = st.selectbox("Model", list(MODELS_CONFIG.keys()), index=list(MODELS_CONFIG.keys()).index(DEFAULT_MODEL))
92
+ steps_per_sample = st.slider("Max Steps", 1, 20, 10)
93
+ temperature = st.slider(
94
+ "Temperature",
95
+ 0.0,
96
+ 2.0,
97
+ DEFAULT_TEMPERATURE,
98
+ 0.1,
99
+ help="Controls randomness in AI responses. 0.0 = deterministic, higher = more creative",
100
+ )
 
 
101
 
102
+ # Load dataset with error handling
103
+ data_paths = get_data_paths(dataset_choice)
104
+ try:
105
+ with open(data_paths["golden_labels"], "r") as f:
106
+ golden_labels = json.load(f).get("samples", [])
107
+
108
+ st.info(f"Dataset '{dataset_choice}' has {len(golden_labels)} samples")
109
+ if len(golden_labels) == 0:
110
+ st.error(f"Dataset '{dataset_choice}' contains no samples!")
111
+ st.stop()
112
+
113
+ except FileNotFoundError:
114
+ st.error(f"❌ Dataset '{dataset_choice}' not found at {data_paths['golden_labels']}")
115
+ st.info("💡 Available datasets: " + ", ".join(available_datasets))
116
+ st.stop()
117
+ except Exception as e:
118
+ st.error(f"❌ Error loading dataset '{dataset_choice}': {str(e)}")
119
  st.stop()
120
 
121
+ num_samples = st.slider(
122
+ "Samples to Test", 1, len(golden_labels), min(3, len(golden_labels))
123
+ )
124
+ else: # Online Mode
125
+ st.info("Enter a Google Maps URL to analyze a specific location")
126
+ google_url = st.text_input(
127
+ "Google Maps URL",
128
+ placeholder="https://www.google.com/maps/@37.5851338,-122.1519467,9z?entry=ttu"
129
+ )
130
+
131
+ if google_url:
132
+ mapcrunch_url = convert_google_to_mapcrunch_url(google_url)
133
+ if mapcrunch_url:
134
+ st.success(f"Converted to MapCrunch URL: {mapcrunch_url}")
135
+ # Create a single sample for online mode
136
+ golden_labels = [{
137
+ "id": "online",
138
+ "lat": float(re.search(r'@(-?\d+\.\d+),(-?\d+\.\d+)', google_url).group(1)),
139
+ "lng": float(re.search(r'@(-?\d+\.\d+),(-?\d+\.\d+)', google_url).group(2)),
140
+ "url": mapcrunch_url
141
+ }]
142
+ num_samples = 1
143
+ else:
144
+ st.error("Invalid Google Maps URL format")
145
+ st.stop()
146
+ else:
147
+ st.warning("Please enter a Google Maps URL")
148
+ st.stop()
149
+
150
+ model_choice = st.selectbox("Model", list(MODELS_CONFIG.keys()), index=list(MODELS_CONFIG.keys()).index(DEFAULT_MODEL))
151
+ steps_per_sample = st.slider("Max Steps", 1, 20, 10)
152
+ temperature = st.slider(
153
+ "Temperature",
154
+ 0.0,
155
+ 2.0,
156
+ DEFAULT_TEMPERATURE,
157
+ 0.1,
158
+ help="Controls randomness in AI responses. 0.0 = deterministic, higher = more creative",
159
  )
 
 
 
 
 
 
 
 
 
160
 
161
  start_button = st.button("🚀 Start", type="primary")
162
 
 
166
  config = MODELS_CONFIG[model_choice]
167
  model_class = get_model_class(config["class"])
168
 
169
+ benchmark_helper = MapGuesserBenchmark(dataset_name=dataset_choice if mode == "Dataset Mode" else "online")
170
  all_results = []
171
 
172
  progress_bar = st.progress(0)
173
 
174
  with GeoBot(
175
+ model=model_class, model_name=config["model_name"], headless=True, temperature=temperature
 
 
 
176
  ) as bot:
177
  for i, sample in enumerate(test_samples):
178
  st.divider()
179
  st.header(f"Sample {i + 1}/{num_samples}")
180
 
181
+ if mode == "Online Mode":
182
+ # Load the MapCrunch URL directly
183
+ bot.controller.load_url(sample["url"])
184
+ else:
185
+ # Load from dataset as before
186
+ bot.controller.load_location_from_data(sample)
187
+
188
+ bot.controller.setup_clean_environment()
189
 
190
+ # Create scrollable container for this sample
191
  sample_container = st.container()
192
 
193
+ with sample_container:
194
+ # Initialize step tracking
195
+ history = bot.init_history()
196
+ final_guess = None
 
 
 
197
 
198
+ for step in range(steps_per_sample):
199
+ step_num = step + 1
200
 
201
+ # Create step container
202
+ with st.container():
203
+ st.subheader(f"Step {step_num}/{steps_per_sample}")
 
204
 
205
+ # Take screenshot and show
206
+ bot.controller.label_arrows_on_screen()
207
+ screenshot_bytes = bot.controller.take_street_view_screenshot()
208
 
209
  col1, col2 = st.columns([1, 2])
210
 
211
  with col1:
 
212
  st.image(
213
+ screenshot_bytes,
214
  caption=f"What AI sees - Step {step_num}",
215
  use_column_width=True,
216
  )
217
 
218
  with col2:
219
+ # Get current screenshot as base64
220
+ current_screenshot_b64 = bot.pil_to_base64(
221
+ Image.open(BytesIO(screenshot_bytes))
 
222
  )
223
+
224
+ available_actions = bot.controller.get_available_actions()
225
 
226
+ # Show AI context
227
+ st.write("**Available Actions:**")
228
+ st.code(json.dumps(available_actions, indent=2))
229
+
230
+ # Generate and display history
231
+ history_text = bot.generate_history_text(history)
232
  st.write("**AI Context:**")
233
  st.text_area(
234
  "History",
235
  history_text,
236
  height=100,
237
  disabled=True,
238
+ key=f"history_{i}_{step}",
 
 
 
 
 
239
  )
240
 
241
+ # Force guess on last step or get AI decision
242
+ if step_num == steps_per_sample:
243
+ action = "GUESS"
244
  st.warning("Max steps reached. Forcing GUESS.")
245
+ # Create a forced decision for consistency
246
+ decision = {
247
+ "reasoning": "Maximum steps reached, forcing final guess with fallback coordinates.",
248
+ "action_details": {"action": "GUESS", "lat": 0.0, "lon": 0.0}
249
+ }
250
+ else:
251
+ # Use the bot's agent step execution
252
+ remaining_steps = steps_per_sample - step
253
+ decision = bot.execute_agent_step(
254
+ history, remaining_steps, current_screenshot_b64, available_actions
255
+ )
256
 
257
+ if decision is None:
258
+ raise ValueError("Failed to get AI decision")
259
 
260
+ action = decision["action_details"]["action"]
 
 
 
 
 
 
261
 
262
+ # Show AI decision
263
+ st.write("**AI Reasoning:**")
264
+ st.info(decision.get("reasoning", "N/A"))
 
 
 
 
 
265
 
266
+ st.write("**AI Action:**")
267
+ st.success(f"`{action}`")
268
 
269
+ # Show raw response for debugging
270
+ with st.expander("Decision Details"):
271
+ st.json(decision)
 
 
 
 
 
272
 
273
+ # Add step to history using the bot's method
274
+ bot.add_step_to_history(history, current_screenshot_b64, decision)
275
+
276
+ # Execute action
277
+ if action == "GUESS":
278
+ if step_num == steps_per_sample:
279
+ # Forced guess - use fallback coordinates
280
+ lat, lon = 0.0, 0.0
281
+ st.error("Forced guess with fallback coordinates")
282
+ else:
283
+ lat = decision.get("action_details", {}).get("lat")
284
+ lon = decision.get("action_details", {}).get("lon")
285
+
286
+ if lat is not None and lon is not None:
287
+ final_guess = (lat, lon)
288
+ st.success(f"Final Guess: {lat:.4f}, {lon:.4f}")
289
+ break
290
+ else:
291
+ # Use bot's execute_action method
292
+ bot.execute_action(action)
293
+
294
+ # Auto scroll to bottom
295
+ st.empty() # Force refresh to show latest content
296
+ time.sleep(1)
297
+
298
+ # Sample Results
299
  st.subheader("Sample Result")
300
  true_coords = {"lat": sample.get("lat"), "lng": sample.get("lng")}
301
  distance_km = None
 
328
  {
329
  "sample_id": sample.get("id"),
330
  "model": model_choice,
 
 
 
331
  "true_coordinates": true_coords,
332
  "predicted_coordinates": final_guess,
333
  "distance_km": distance_km,
mapcrunch_controller.py CHANGED
@@ -291,3 +291,13 @@ class MapCrunchController:
291
 
292
  def __exit__(self, exc_type, exc_val, exc_tb):
293
  self.close()
 
 
 
 
 
 
 
 
 
 
 
291
 
292
  def __exit__(self, exc_type, exc_val, exc_tb):
293
  self.close()
294
+
295
+ def load_url(self, url):
296
+ """Load a specific MapCrunch URL."""
297
+ try:
298
+ self.driver.get(url)
299
+ time.sleep(2) # Wait for the page to load
300
+ return True
301
+ except Exception as e:
302
+ print(f"Error loading URL: {e}")
303
+ return False