baconnier commited on
Commit
de35f25
Β·
verified Β·
1 Parent(s): a7cc45c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +133 -148
app.py CHANGED
@@ -3,217 +3,202 @@ import openai
3
  import gradio as gr
4
  import json
5
  import plotly.graph_objects as go
6
- from dataclasses import dataclass
7
- from typing import Dict, List, Optional, Any
8
-
9
- @dataclass
10
- class Location:
11
- name: str
12
- lat: float
13
- lon: float
14
- relevance: str = ""
15
-
16
- @dataclass
17
- class ZoomState:
18
- level: int
19
- selection: str
20
 
21
  class ArtExplorer:
22
- def __init__(self, api_key: str = None, base_url: str = None):
23
- """Initialize ArtExplorer with configurable API settings."""
24
  self.client = openai.OpenAI(
25
- base_url=base_url or "https://api.groq.com/openai/v1",
26
- api_key=api_key or os.environ.get("GROQ_API_KEY")
27
  )
28
  self.current_state = {
29
  "zoom_level": 0,
30
  "selections": {}
31
  }
32
 
33
- def create_map(self, locations: List[Location]) -> go.Figure:
34
- """Create an interactive map visualization."""
35
- default_location = Location("Paris", 48.8566, 2.3522, "Default location")
36
- locations = locations or [default_location]
37
 
38
  fig = go.Figure(go.Scattermapbox(
39
- lat=[loc.lat for loc in locations],
40
- lon=[loc.lon for loc in locations],
41
  mode='markers',
42
  marker=go.scattermapbox.Marker(size=10),
43
- text=[loc.name for loc in locations]
44
  ))
45
 
46
  fig.update_layout(
47
  mapbox_style="open-street-map",
48
  mapbox=dict(
49
- center=dict(lat=locations[0].lat, lon=locations[0].lon),
50
  zoom=4
51
  ),
52
  margin=dict(r=0, t=0, l=0, b=0)
53
  )
54
  return fig
55
 
56
- def get_llm_response(self, query: str, zoom_context: Optional[Dict] = None) -> Dict:
57
- """Get response from LLM with error handling and logging."""
58
  try:
59
  current_zoom_states = {
60
- axis: ZoomState(self.current_state["zoom_level"],
61
- zoom_context.get(axis, "") if zoom_context else "")
62
- for axis in ["temporal", "geographical", "style", "subject"]
 
63
  }
64
 
 
 
 
 
 
65
  messages = [
66
  {"role": "system", "content": "You are an expert art historian specializing in interactive exploration."},
67
- {"role": "user", "content": self._format_prompt(query, current_zoom_states)}
 
 
 
68
  ]
69
 
70
- print(f"Sending request to LLM with query: {query}")
71
  response = self.client.chat.completions.create(
72
  model="mixtral-8x7b-32768",
73
  messages=messages,
74
  temperature=0.1,
75
  max_tokens=2048
76
  )
77
-
78
- # Clean and parse response
79
- content = response.choices[0].message.content.strip()
80
- print(f"Raw LLM response: {content}")
81
-
82
- result = json.loads(content)
83
- print(f"Parsed response: {json.dumps(result, indent=2)}")
84
-
85
  return result
86
-
87
- except json.JSONDecodeError as e:
88
- print(f"JSON parsing error: {str(e)}")
89
- print(f"Problematic content: {content if 'content' in locals() else 'No content'}")
90
- return self.get_default_response()
91
  except Exception as e:
92
- print(f"Unexpected error: {str(e)}")
93
  return self.get_default_response()
94
 
95
- def _format_prompt(self, query: str, zoom_states: Dict[str, ZoomState]) -> str:
96
- """Format the prompt for LLM with current context."""
97
- return CONTEXTUAL_ZOOM_PROMPT.format(
98
- user_query=query,
99
- current_zoom_states=json.dumps(zoom_states, indent=2)
100
- )
101
 
102
- def create_interface(self) -> gr.Blocks:
103
- """Create the Gradio interface with proper component organization."""
104
- with gr.Blocks(title="Art History Explorer", theme="default") as demo:
105
- self._create_header()
106
 
107
  with gr.Row():
108
- query, search_btn = self._create_search_section()
109
-
 
 
 
 
110
  with gr.Row():
111
- time_controls = self._create_temporal_section()
112
- geo_controls = self._create_geographical_section()
113
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  with gr.Row():
115
- style_controls = self._create_style_section()
116
-
117
- self._setup_event_handlers(
118
- search_btn, query,
119
- time_controls, geo_controls, style_controls
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  )
121
-
122
- return demo
123
 
124
- def _create_header(self):
125
- """Create the header section of the interface."""
126
- gr.Markdown(
127
- """
128
- # Art History Explorer
129
- Explore art history through time, space, and style.
130
- """
131
- )
132
 
133
- def _create_search_section(self):
134
- """Create the search input section."""
135
- query = gr.Textbox(
136
- label="Enter your art history query",
137
- placeholder="e.g., Napoleon wars, Renaissance Italy",
138
- lines=2
139
- )
140
- search_btn = gr.Button("Explore", variant="primary")
141
- return query, search_btn
142
-
143
- def _create_temporal_section(self):
144
- """Create the temporal control section."""
145
- with gr.Column():
146
- slider = gr.Slider(
147
- minimum=1000,
148
- maximum=2024,
149
- label="Time Period",
150
- interactive=True
151
  )
152
- explanation = gr.Markdown()
153
- zoom_btn = gr.Button("πŸ” Zoom Time Period")
154
- return {"slider": slider, "explanation": explanation, "zoom": zoom_btn}
155
-
156
- def _create_geographical_section(self):
157
- """Create the geographical control section."""
158
- with gr.Column():
159
- map_plot = gr.Plot(label="Geographic Location")
160
- explanation = gr.Markdown()
161
- zoom_btn = gr.Button("πŸ” Zoom Geography")
162
- return {"map": map_plot, "explanation": explanation, "zoom": zoom_btn}
163
-
164
- def _create_style_section(self):
165
- """Create the style control section."""
166
- with gr.Column():
167
- select = gr.Dropdown(
168
- multiselect=True,
169
- label="Artistic Styles"
170
  )
171
- explanation = gr.Markdown()
172
- zoom_btn = gr.Button("πŸ” Zoom Styles")
173
- return {"select": select, "explanation": explanation, "zoom": zoom_btn}
174
-
175
- def _setup_event_handlers(self, search_btn, query, time_controls, geo_controls, style_controls):
176
- """Set up all event handlers for the interface."""
177
- search_btn.click(
178
- fn=self._handle_search,
179
- inputs=[query],
180
- outputs=[
181
- time_controls["slider"],
182
- geo_controls["map"],
183
- style_controls["select"],
184
- time_controls["explanation"],
185
- geo_controls["explanation"],
186
- style_controls["explanation"]
187
- ]
188
- )
189
 
190
- self._setup_zoom_handlers(query, time_controls, geo_controls, style_controls)
191
 
192
  def _handle_search(self, query: str) -> Dict[str, Any]:
193
- """Handle the search button click."""
194
  config = self.get_llm_response(query)
195
- return self._process_configuration(config)
196
-
197
- def _process_configuration(self, config: Dict) -> Dict[str, Any]:
198
- """Process the configuration returned by the LLM."""
199
- axis_config = config["axis_configurations"]
200
- temporal = axis_config["temporal"]["current_zoom"]
201
- geographical = axis_config["geographical"]["current_zoom"]
202
- style = axis_config["style"]["current_zoom"]
203
 
204
  return {
205
- "time_slider": temporal["range"],
206
- "map_plot": self.create_map([Location(**loc) for loc in geographical["locations"]]),
207
- "style_select": gr.Dropdown(choices=style["options"]),
208
- "time_explanation": temporal["explanation"],
209
- "geo_explanation": geographical.get("explanation", ""),
210
- "style_explanation": style["explanation"]
211
  }
212
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  def main():
214
- """Main entry point for the application."""
215
  explorer = ArtExplorer()
 
216
  demo = explorer.create_interface()
 
217
  demo.launch(
218
  server_name="0.0.0.0",
219
  server_port=7860,
 
3
  import gradio as gr
4
  import json
5
  import plotly.graph_objects as go
6
+ from typing import Dict, Any
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
  class ArtExplorer:
9
+ def __init__(self):
 
10
  self.client = openai.OpenAI(
11
+ base_url="https://api.groq.com/openai/v1",
12
+ api_key=os.environ.get("GROQ_API_KEY")
13
  )
14
  self.current_state = {
15
  "zoom_level": 0,
16
  "selections": {}
17
  }
18
 
19
+ def create_map(self, locations):
20
+ """Create a Plotly map figure from location data"""
21
+ if not locations:
22
+ locations = [{"name": "Paris", "lat": 48.8566, "lon": 2.3522}]
23
 
24
  fig = go.Figure(go.Scattermapbox(
25
+ lat=[loc.get('lat') for loc in locations],
26
+ lon=[loc.get('lon') for loc in locations],
27
  mode='markers',
28
  marker=go.scattermapbox.Marker(size=10),
29
+ text=[loc.get('name') for loc in locations]
30
  ))
31
 
32
  fig.update_layout(
33
  mapbox_style="open-street-map",
34
  mapbox=dict(
35
+ center=dict(lat=48.8566, lon=2.3522),
36
  zoom=4
37
  ),
38
  margin=dict(r=0, t=0, l=0, b=0)
39
  )
40
  return fig
41
 
42
+ def get_llm_response(self, query: str, zoom_context: dict = None) -> dict:
 
43
  try:
44
  current_zoom_states = {
45
+ "temporal": {"level": self.current_state["zoom_level"], "selection": ""},
46
+ "geographical": {"level": self.current_state["zoom_level"], "selection": ""},
47
+ "style": {"level": self.current_state["zoom_level"], "selection": ""},
48
+ "subject": {"level": self.current_state["zoom_level"], "selection": ""}
49
  }
50
 
51
+ if zoom_context:
52
+ for key, value in zoom_context.items():
53
+ if key in current_zoom_states:
54
+ current_zoom_states[key]["selection"] = value
55
+
56
  messages = [
57
  {"role": "system", "content": "You are an expert art historian specializing in interactive exploration."},
58
+ {"role": "user", "content": CONTEXTUAL_ZOOM_PROMPT.format(
59
+ user_query=query,
60
+ current_zoom_states=json.dumps(current_zoom_states, indent=2)
61
+ )}
62
  ]
63
 
64
+ print("Sending request to LLM...")
65
  response = self.client.chat.completions.create(
66
  model="mixtral-8x7b-32768",
67
  messages=messages,
68
  temperature=0.1,
69
  max_tokens=2048
70
  )
71
+ print("Received response from LLM")
72
+ result = json.loads(response.choices[0].message.content)
 
 
 
 
 
 
73
  return result
 
 
 
 
 
74
  except Exception as e:
75
+ print(f"Error in LLM response: {str(e)}")
76
  return self.get_default_response()
77
 
78
+ def get_default_response(self):
79
+ return CONTEXTUAL_ZOOM_default_response
 
 
 
 
80
 
81
+ def create_interface(self):
82
+ with gr.Blocks() as demo:
83
+ gr.Markdown("# Art History Explorer")
 
84
 
85
  with gr.Row():
86
+ query = gr.Textbox(
87
+ label="Enter your art history query",
88
+ placeholder="e.g., Napoleon wars, Renaissance Italy"
89
+ )
90
+ search_btn = gr.Button("Explore")
91
+
92
  with gr.Row():
93
+ # Temporal axis
94
+ with gr.Column():
95
+ time_slider = gr.Slider(
96
+ minimum=1000,
97
+ maximum=2024,
98
+ label="Time Period",
99
+ interactive=True
100
+ )
101
+ time_explanation = gr.Markdown()
102
+ time_zoom = gr.Button("πŸ” Zoom Time Period")
103
+
104
+ # Geographical axis
105
+ with gr.Column():
106
+ map_plot = gr.Plot(label="Geographic Location")
107
+ geo_explanation = gr.Markdown()
108
+ geo_zoom = gr.Button("πŸ” Zoom Geography")
109
+
110
  with gr.Row():
111
+ style_select = gr.Dropdown(
112
+ multiselect=True,
113
+ label="Artistic Styles"
114
+ )
115
+ style_explanation = gr.Markdown()
116
+ style_zoom = gr.Button("πŸ” Zoom Styles")
117
+
118
+ # Set up event handlers
119
+ search_btn.click(
120
+ fn=self._handle_search,
121
+ inputs=[query],
122
+ outputs=[
123
+ time_slider,
124
+ map_plot,
125
+ style_select,
126
+ time_explanation,
127
+ geo_explanation,
128
+ style_explanation
129
+ ]
130
  )
 
 
131
 
132
+ time_zoom.click(
133
+ fn=lambda q, v: self._handle_zoom("temporal", q, v),
134
+ inputs=[query, time_slider],
135
+ outputs=[time_slider, time_explanation]
136
+ )
 
 
 
137
 
138
+ geo_zoom.click(
139
+ fn=lambda q, v: self._handle_zoom("geographical", q, v),
140
+ inputs=[query, map_plot],
141
+ outputs=[map_plot, geo_explanation]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  )
143
+
144
+ style_zoom.click(
145
+ fn=lambda q, v: self._handle_zoom("style", q, v),
146
+ inputs=[query, style_select],
147
+ outputs=[style_select, style_explanation]
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
 
150
+ return demo
151
 
152
  def _handle_search(self, query: str) -> Dict[str, Any]:
153
+ """Handle the initial search query"""
154
  config = self.get_llm_response(query)
155
+ temporal_config = config["axis_configurations"]["temporal"]["current_zoom"]
156
+ geographical_config = config["axis_configurations"]["geographical"]["current_zoom"]
157
+ style_config = config["axis_configurations"]["style"]["current_zoom"]
158
+
159
+ map_fig = self.create_map(geographical_config["locations"])
 
 
 
160
 
161
  return {
162
+ "time_slider": temporal_config["range"],
163
+ "map_plot": map_fig,
164
+ "style_select": gr.Dropdown(choices=style_config["options"]),
165
+ "time_explanation": temporal_config["explanation"],
166
+ "geo_explanation": geographical_config.get("explanation", ""),
167
+ "style_explanation": style_config["explanation"]
168
  }
169
 
170
+ def _handle_zoom(self, axis_name: str, query: str, current_value: Any) -> Dict[str, Any]:
171
+ """Handle zoom events for any axis"""
172
+ self.current_state["zoom_level"] += 1
173
+ config = self.get_llm_response(
174
+ query,
175
+ zoom_context={axis_name: current_value}
176
+ )
177
+ axis_config = config["axis_configurations"][axis_name]["current_zoom"]
178
+
179
+ if axis_name == "temporal":
180
+ return {
181
+ "time_slider": axis_config["range"],
182
+ "time_explanation": axis_config["explanation"]
183
+ }
184
+ elif axis_name == "geographical":
185
+ map_fig = self.create_map(axis_config["locations"])
186
+ return {
187
+ "map_plot": map_fig,
188
+ "geo_explanation": axis_config.get("explanation", "")
189
+ }
190
+ else: # style
191
+ return {
192
+ "style_select": gr.Dropdown(choices=axis_config["options"]),
193
+ "style_explanation": axis_config["explanation"]
194
+ }
195
+
196
  def main():
197
+ print("Starting initialization...")
198
  explorer = ArtExplorer()
199
+ print("Created ArtExplorer instance")
200
  demo = explorer.create_interface()
201
+ print("Created interface")
202
  demo.launch(
203
  server_name="0.0.0.0",
204
  server_port=7860,