Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -136,6 +136,7 @@ Current Zoom States: {
|
|
136 |
Generate only the JSON response, maintaining strict JSON format."""
|
137 |
|
138 |
|
|
|
139 |
class ArtExplorer:
|
140 |
def __init__(self):
|
141 |
self.client = openai.OpenAI(
|
@@ -149,140 +150,108 @@ class ArtExplorer:
|
|
149 |
|
150 |
def create_map(self, locations):
|
151 |
"""Create a Plotly map figure from location data"""
|
|
|
|
|
|
|
152 |
fig = go.Figure(go.Scattermapbox(
|
153 |
lat=[loc.get('lat') for loc in locations],
|
154 |
lon=[loc.get('lon') for loc in locations],
|
155 |
mode='markers',
|
156 |
marker=go.scattermapbox.Marker(size=10),
|
157 |
-
text=[loc.get('name') for loc in locations]
|
158 |
-
hovertemplate='<b>%{text}</b><extra></extra>'
|
159 |
))
|
160 |
|
161 |
fig.update_layout(
|
162 |
mapbox_style="open-street-map",
|
163 |
mapbox=dict(
|
164 |
-
center=dict(lat=48.8566, lon=2.3522),
|
165 |
zoom=4
|
166 |
),
|
167 |
-
margin=dict(r=0, t=0, l=0, b=0)
|
168 |
-
showlegend=False
|
169 |
)
|
170 |
return fig
|
171 |
|
172 |
def get_llm_response(self, query: str, zoom_context: dict = None) -> dict:
|
173 |
try:
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
"selection"
|
185 |
-
|
186 |
-
"style": {
|
187 |
-
"level": self.current_state.get("zoom_level", ""),
|
188 |
-
"selection": zoom_context.get("style") if zoom_context else ""
|
189 |
-
},
|
190 |
-
"subject": {
|
191 |
-
"level": self.current_state.get("zoom_level", ""),
|
192 |
-
"selection": zoom_context.get("subject") if zoom_context else ""
|
193 |
-
}
|
194 |
-
}
|
195 |
-
)
|
196 |
-
|
197 |
response = self.client.chat.completions.create(
|
198 |
model="mixtral-8x7b-32768",
|
199 |
messages=[
|
200 |
-
{
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
"role": "user",
|
206 |
-
"content": formatted_prompt
|
207 |
-
}
|
208 |
],
|
209 |
temperature=0.1,
|
210 |
max_tokens=2048
|
211 |
)
|
212 |
|
213 |
result = json.loads(response.choices[0].message.content)
|
214 |
-
|
215 |
-
# Validate response structure
|
216 |
-
if "axis_configurations" not in result:
|
217 |
-
raise KeyError("Missing axis_configurations in LLM response")
|
218 |
-
|
219 |
return result
|
220 |
|
221 |
except Exception as e:
|
222 |
print(f"Error in LLM response: {str(e)}")
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
"
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
"
|
238 |
-
"in": {
|
239 |
-
"range": [1800, 1900],
|
240 |
-
"explanation": "Zoom in to see more detail"
|
241 |
-
},
|
242 |
-
"out": {
|
243 |
-
"range": [1500, 2024],
|
244 |
-
"explanation": "Broader historical context"
|
245 |
-
}
|
246 |
-
},
|
247 |
-
"impacted_by": {
|
248 |
-
"geographical": "Location affects time period",
|
249 |
-
"style": "Style affects time period"
|
250 |
-
}
|
251 |
},
|
252 |
-
"
|
253 |
-
"
|
254 |
-
|
255 |
-
"
|
256 |
-
"locations": [
|
257 |
-
{
|
258 |
-
"name": "Paris",
|
259 |
-
"lat": 48.8566,
|
260 |
-
"lon": 2.3522,
|
261 |
-
"relevance": "Default location"
|
262 |
-
}
|
263 |
-
]
|
264 |
},
|
265 |
-
"
|
266 |
-
"
|
267 |
-
|
268 |
-
"explanation": "Major art centers"
|
269 |
-
},
|
270 |
-
"out": {
|
271 |
-
"locations": ["Europe", "Americas", "Asia"],
|
272 |
-
"explanation": "Global art movements"
|
273 |
-
}
|
274 |
-
}
|
275 |
-
},
|
276 |
-
"style": {
|
277 |
-
"component": "st.multiselect",
|
278 |
-
"current_zoom": {
|
279 |
-
"level": "movement",
|
280 |
-
"options": ["Classical", "Modern"],
|
281 |
-
"explanation": "Default art movements"
|
282 |
}
|
283 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
284 |
}
|
285 |
}
|
|
|
286 |
|
287 |
def create_interface(self):
|
288 |
with gr.Blocks() as demo:
|
@@ -323,55 +292,62 @@ class ArtExplorer:
|
|
323 |
|
324 |
def initial_search(query):
|
325 |
config = self.get_llm_response(query)
|
326 |
-
self.current_state["selections"] = {}
|
327 |
-
self.current_state["zoom_level"] = 0
|
328 |
|
329 |
-
#
|
330 |
-
|
|
|
|
|
|
|
|
|
|
|
331 |
|
332 |
return {
|
333 |
-
time_slider:
|
334 |
map_plot: map_fig,
|
335 |
-
style_select: gr.Dropdown(choices=
|
336 |
-
time_explanation:
|
337 |
-
geo_explanation:
|
338 |
-
style_explanation:
|
339 |
}
|
340 |
|
341 |
def zoom_axis(query, axis_name, current_value):
|
342 |
self.current_state["zoom_level"] += 1
|
343 |
-
self.current_state["selections"][axis_name] = current_value
|
344 |
-
|
345 |
config = self.get_llm_response(
|
346 |
query,
|
347 |
zoom_context={axis_name: current_value}
|
348 |
)
|
349 |
|
350 |
-
|
|
|
351 |
if axis_name == "temporal":
|
352 |
-
|
353 |
-
time_slider:
|
354 |
-
time_explanation:
|
355 |
-
}
|
356 |
elif axis_name == "geographical":
|
357 |
-
map_fig = self.create_map(
|
358 |
-
|
359 |
map_plot: map_fig,
|
360 |
-
geo_explanation:
|
361 |
-
}
|
362 |
-
|
363 |
-
|
364 |
-
style_select: gr.Dropdown(choices=
|
365 |
-
style_explanation:
|
366 |
-
}
|
367 |
-
return updates
|
368 |
|
369 |
# Connect event handlers
|
370 |
search_btn.click(
|
371 |
fn=initial_search,
|
372 |
inputs=[query],
|
373 |
-
outputs=[
|
374 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
375 |
)
|
376 |
|
377 |
time_zoom.click(
|
|
|
136 |
Generate only the JSON response, maintaining strict JSON format."""
|
137 |
|
138 |
|
139 |
+
|
140 |
class ArtExplorer:
|
141 |
def __init__(self):
|
142 |
self.client = openai.OpenAI(
|
|
|
150 |
|
151 |
def create_map(self, locations):
|
152 |
"""Create a Plotly map figure from location data"""
|
153 |
+
if not locations:
|
154 |
+
locations = [{"name": "Paris", "lat": 48.8566, "lon": 2.3522}]
|
155 |
+
|
156 |
fig = go.Figure(go.Scattermapbox(
|
157 |
lat=[loc.get('lat') for loc in locations],
|
158 |
lon=[loc.get('lon') for loc in locations],
|
159 |
mode='markers',
|
160 |
marker=go.scattermapbox.Marker(size=10),
|
161 |
+
text=[loc.get('name') for loc in locations]
|
|
|
162 |
))
|
163 |
|
164 |
fig.update_layout(
|
165 |
mapbox_style="open-street-map",
|
166 |
mapbox=dict(
|
167 |
+
center=dict(lat=48.8566, lon=2.3522),
|
168 |
zoom=4
|
169 |
),
|
170 |
+
margin=dict(r=0, t=0, l=0, b=0)
|
|
|
171 |
)
|
172 |
return fig
|
173 |
|
174 |
def get_llm_response(self, query: str, zoom_context: dict = None) -> dict:
|
175 |
try:
|
176 |
+
current_zoom_states = {
|
177 |
+
"temporal": {"level": self.current_state["zoom_level"], "selection": ""},
|
178 |
+
"geographical": {"level": self.current_state["zoom_level"], "selection": ""},
|
179 |
+
"style": {"level": self.current_state["zoom_level"], "selection": ""},
|
180 |
+
"subject": {"level": self.current_state["zoom_level"], "selection": ""}
|
181 |
+
}
|
182 |
+
|
183 |
+
if zoom_context:
|
184 |
+
for key, value in zoom_context.items():
|
185 |
+
if key in current_zoom_states:
|
186 |
+
current_zoom_states[key]["selection"] = value
|
187 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
188 |
response = self.client.chat.completions.create(
|
189 |
model="mixtral-8x7b-32768",
|
190 |
messages=[
|
191 |
+
{"role": "system", "content": "You are an expert art historian specializing in interactive exploration."},
|
192 |
+
{"role": "user", "content": CONTEXTUAL_ZOOM_PROMPT.format(
|
193 |
+
user_query=query,
|
194 |
+
current_zoom_states=json.dumps(current_zoom_states, indent=2)
|
195 |
+
)}
|
|
|
|
|
|
|
196 |
],
|
197 |
temperature=0.1,
|
198 |
max_tokens=2048
|
199 |
)
|
200 |
|
201 |
result = json.loads(response.choices[0].message.content)
|
|
|
|
|
|
|
|
|
|
|
202 |
return result
|
203 |
|
204 |
except Exception as e:
|
205 |
print(f"Error in LLM response: {str(e)}")
|
206 |
+
return self.get_default_response()
|
207 |
+
|
208 |
+
def get_default_response(self):
|
209 |
+
return {
|
210 |
+
"analysis": {
|
211 |
+
"query_focus": "Default focus",
|
212 |
+
"historical_context": "Default historical context"
|
213 |
+
},
|
214 |
+
"axis_configurations": {
|
215 |
+
"temporal": {
|
216 |
+
"component": "st.slider",
|
217 |
+
"current_zoom": {
|
218 |
+
"level": "century",
|
219 |
+
"range": [1700, 2000],
|
220 |
+
"explanation": "Default time range"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
221 |
},
|
222 |
+
"available_zooms": {
|
223 |
+
"in": {
|
224 |
+
"range": [1800, 1900],
|
225 |
+
"explanation": "Zoom in to see more detail"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
226 |
},
|
227 |
+
"out": {
|
228 |
+
"range": [1500, 2024],
|
229 |
+
"explanation": "Broader historical context"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
230 |
}
|
231 |
}
|
232 |
+
},
|
233 |
+
"geographical": {
|
234 |
+
"current_zoom": {
|
235 |
+
"level": "continent",
|
236 |
+
"locations": [
|
237 |
+
{
|
238 |
+
"name": "Paris",
|
239 |
+
"lat": 48.8566,
|
240 |
+
"lon": 2.3522,
|
241 |
+
"relevance": "Default location"
|
242 |
+
}
|
243 |
+
]
|
244 |
+
}
|
245 |
+
},
|
246 |
+
"style": {
|
247 |
+
"current_zoom": {
|
248 |
+
"level": "movement",
|
249 |
+
"options": ["Classical", "Modern"],
|
250 |
+
"explanation": "Default art movements"
|
251 |
+
}
|
252 |
}
|
253 |
}
|
254 |
+
}
|
255 |
|
256 |
def create_interface(self):
|
257 |
with gr.Blocks() as demo:
|
|
|
292 |
|
293 |
def initial_search(query):
|
294 |
config = self.get_llm_response(query)
|
|
|
|
|
295 |
|
296 |
+
# Access the correct nested structure
|
297 |
+
temporal_config = config["axis_configurations"]["temporal"]["current_zoom"]
|
298 |
+
geographical_config = config["axis_configurations"]["geographical"]["current_zoom"]
|
299 |
+
style_config = config["axis_configurations"]["style"]["current_zoom"]
|
300 |
+
|
301 |
+
# Create map figure
|
302 |
+
map_fig = self.create_map(geographical_config["locations"])
|
303 |
|
304 |
return {
|
305 |
+
time_slider: temporal_config["range"],
|
306 |
map_plot: map_fig,
|
307 |
+
style_select: gr.Dropdown(choices=style_config["options"]),
|
308 |
+
time_explanation: temporal_config["explanation"],
|
309 |
+
geo_explanation: geographical_config.get("explanation", ""),
|
310 |
+
style_explanation: style_config["explanation"]
|
311 |
}
|
312 |
|
313 |
def zoom_axis(query, axis_name, current_value):
|
314 |
self.current_state["zoom_level"] += 1
|
|
|
|
|
315 |
config = self.get_llm_response(
|
316 |
query,
|
317 |
zoom_context={axis_name: current_value}
|
318 |
)
|
319 |
|
320 |
+
axis_config = config["axis_configurations"][axis_name]["current_zoom"]
|
321 |
+
|
322 |
if axis_name == "temporal":
|
323 |
+
return {
|
324 |
+
time_slider: axis_config["range"],
|
325 |
+
time_explanation: axis_config["explanation"]
|
326 |
+
}
|
327 |
elif axis_name == "geographical":
|
328 |
+
map_fig = self.create_map(axis_config["locations"])
|
329 |
+
return {
|
330 |
map_plot: map_fig,
|
331 |
+
geo_explanation: axis_config.get("explanation", "")
|
332 |
+
}
|
333 |
+
else: # style
|
334 |
+
return {
|
335 |
+
style_select: gr.Dropdown(choices=axis_config["options"]),
|
336 |
+
style_explanation: axis_config["explanation"]
|
337 |
+
}
|
|
|
338 |
|
339 |
# Connect event handlers
|
340 |
search_btn.click(
|
341 |
fn=initial_search,
|
342 |
inputs=[query],
|
343 |
+
outputs=[
|
344 |
+
time_slider,
|
345 |
+
map_plot,
|
346 |
+
style_select,
|
347 |
+
time_explanation,
|
348 |
+
geo_explanation,
|
349 |
+
style_explanation
|
350 |
+
]
|
351 |
)
|
352 |
|
353 |
time_zoom.click(
|