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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +163 -140
app.py CHANGED
@@ -3,199 +3,222 @@ import openai
3
  import gradio as gr
4
  import json
5
  import plotly.graph_objects as go
6
- from variables import *
 
 
 
 
 
 
 
 
 
 
 
 
 
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(messages)
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(response)
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
- def initial_search(query):
119
- config = self.get_llm_response(query)
120
- temporal_config = config["axis_configurations"]["temporal"]["current_zoom"]
121
- geographical_config = config["axis_configurations"]["geographical"]["current_zoom"]
122
- style_config = config["axis_configurations"]["style"]["current_zoom"]
123
-
124
- map_fig = self.create_map(geographical_config["locations"])
125
-
126
- return {
127
- time_slider: temporal_config["range"],
128
- map_plot: map_fig,
129
- style_select: gr.Dropdown(choices=style_config["options"]),
130
- time_explanation: temporal_config["explanation"],
131
- geo_explanation: geographical_config.get("explanation", ""),
132
- style_explanation: style_config["explanation"]
133
- }
134
-
135
- def zoom_axis(query, axis_name, current_value):
136
- self.current_state["zoom_level"] += 1
137
- config = self.get_llm_response(
138
- query,
139
- zoom_context={axis_name: current_value}
140
- )
141
- axis_config = config["axis_configurations"][axis_name]["current_zoom"]
142
-
143
- if axis_name == "temporal":
144
- return {
145
- time_slider: axis_config["range"],
146
- time_explanation: axis_config["explanation"]
147
- }
148
- elif axis_name == "geographical":
149
- map_fig = self.create_map(axis_config["locations"])
150
- return {
151
- map_plot: map_fig,
152
- geo_explanation: axis_config.get("explanation", "")
153
- }
154
- else: # style
155
- return {
156
- style_select: gr.Dropdown(choices=axis_config["options"]),
157
- style_explanation: axis_config["explanation"]
158
- }
159
-
160
- # Connect event handlers
161
- search_btn.click(
162
- fn=initial_search,
163
- inputs=[query],
164
- outputs=[
165
- time_slider,
166
- map_plot,
167
- style_select,
168
- time_explanation,
169
- geo_explanation,
170
- style_explanation
171
- ]
172
  )
 
 
173
 
174
- time_zoom.click(
175
- fn=lambda q, v: zoom_axis(q, "temporal", v),
176
- inputs=[query, time_slider],
177
- outputs=[time_slider, time_explanation]
178
- )
 
 
 
179
 
180
- geo_zoom.click(
181
- fn=lambda q, v: zoom_axis(q, "geographical", v),
182
- inputs=[query, map_plot],
183
- outputs=[map_plot, geo_explanation]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  )
185
-
186
- style_zoom.click(
187
- fn=lambda q, v: zoom_axis(q, "style", v),
188
- inputs=[query, style_select],
189
- outputs=[style_select, style_explanation]
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
 
192
- return demo
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
 
194
- if __name__ == "__main__":
195
- print("Starting initialization...")
196
  explorer = ArtExplorer()
197
- print("Created ArtExplorer instance")
198
  demo = explorer.create_interface()
199
- print("Created interface")
200
- demo.launch()
201
- print("Launched demo")
 
 
 
 
 
 
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,
220
+ share=False
221
+ )
222
+
223
+ if __name__ == "__main__":
224
+ main()