Encyclopedia / app.py
baconnier's picture
Update app.py
f58422b verified
raw
history blame
13.5 kB
import os
import openai
import gradio as gr
import json
import plotly.graph_objects as go
CONTEXTUAL_ZOOM_PROMPT = """
You are an expert art historian specializing in interactive exploration. Analyze the query and generate contextually aware zoom configurations with explanations.
###Input###
User Query: {user_query}
Current Zoom States: {
"temporal": {"level": "", "selection": ""},
"geographical": {"level": "", "selection": ""},
"style": {"level": "", "selection": ""},
"subject": {"level": "", "selection": ""}
}
###Output Format###
{
"analysis": {
"query_focus": "main subject",
"historical_context": "brief explanation"
},
"axis_configurations": {
"temporal": {
"component": "st.slider",
"current_zoom": {
"level": "century/decade/year",
"range": [start, end],
"explanation": "Why this time range is relevant"
},
"available_zooms": {
"in": {
"range": [narrower_start, narrower_end],
"explanation": "What focusing here reveals"
},
"out": {
"range": [broader_start, broader_end],
"explanation": "Broader historical context"
}
},
"impacted_by": {
"geographical": "how location affects timeframe",
"style": "how style affects timeframe"
}
},
"geographical": {
"component": "st.map",
"current_zoom": {
"level": "continent/country/city",
"locations": [
{
"name": "",
"lat": 0,
"lon": 0,
"relevance": "why this location matters"
}
]
},
"available_zooms": {
"in": {
"locations": ["more specific locations"],
"explanation": "What focusing here reveals"
},
"out": {
"locations": ["broader regions"],
"explanation": "Broader geographical context"
}
},
"impacted_by": {
"temporal": "how time period affects locations",
"style": "how style affects locations"
}
},
"style": {
"component": "st.multiselect" if current_zoom == "broad" else "st.selectbox",
"current_zoom": {
"level": "movement/sub_movement/specific",
"options": ["list of styles"],
"explanation": "Style context for this period/location"
}
}
},
"streamlit_adaptations": {
"recommended_components": {
"component_name": "reason for recommendation based on zoom level",
"configuration": {}
}
}
}
###Example for "paint during napoleon war"###
{
"temporal": {
"component": "st.slider",
"current_zoom": {
"level": "period",
"range": [1799, 1815],
"explanation": "Napoleon's reign as First Consul and Emperor"[1]
},
"available_zooms": {
"in": {
"range": [1812, 1815],
"explanation": "Focus on final campaigns and artistic responses"[1]
}
}
},
"geographical": {
"component": "st.map",
"current_zoom": {
"level": "continent",
"locations": [
{
"name": "France",
"relevance": "Center of Napoleonic art production"[2]
},
{
"name": "Spain",
"relevance": "Goya's war paintings perspective"[1]
}
]
}
}
}
###Requirements###
1. Explain zoom level changes and their historical significance
2. Adapt Streamlit components based on zoom level
3. Show relationships between different axes
4. Provide historical context for available selections
5. Consider how selections affect other axes
6. Include relevant historical explanations for each zoom level
Generate only the JSON response, maintaining strict JSON format."""
class ArtExplorer:
def __init__(self):
self.client = openai.OpenAI(
base_url="https://api.groq.com/openai/v1",
api_key=os.environ.get("GROQ_API_KEY")
)
self.current_state = {
"zoom_level": 0,
"selections": {}
}
def create_map(self, locations):
"""Create a Plotly map figure from location data"""
if not locations:
locations = [{"name": "Paris", "lat": 48.8566, "lon": 2.3522}]
fig = go.Figure(go.Scattermapbox(
lat=[loc.get('lat') for loc in locations],
lon=[loc.get('lon') for loc in locations],
mode='markers',
marker=go.scattermapbox.Marker(size=10),
text=[loc.get('name') for loc in locations]
))
fig.update_layout(
mapbox_style="open-street-map",
mapbox=dict(
center=dict(lat=48.8566, lon=2.3522),
zoom=4
),
margin=dict(r=0, t=0, l=0, b=0)
)
return fig
def get_llm_response(self, query: str, zoom_context: dict = None) -> dict:
try:
current_zoom_states = {
"temporal": {"level": self.current_state["zoom_level"], "selection": ""},
"geographical": {"level": self.current_state["zoom_level"], "selection": ""},
"style": {"level": self.current_state["zoom_level"], "selection": ""},
"subject": {"level": self.current_state["zoom_level"], "selection": ""}
}
if zoom_context:
for key, value in zoom_context.items():
if key in current_zoom_states:
current_zoom_states[key]["selection"] = value
messages=[
{"role": "system", "content": "You are an expert art historian specializing in interactive exploration."},
{"role": "user", "content": CONTEXTUAL_ZOOM_PROMPT.format(
user_query=query,
current_zoom_states=json.dumps(current_zoom_states, indent=2)
)}
],
print(messages)
response = self.client.chat.completions.create(
model="mixtral-8x7b-32768",
messages=messages,
temperature=0.1,
max_tokens=2048
)
print(response)
result = json.loads(response.choices[0].message.content)
return result
except Exception as e:
print(f"Error in LLM response: {str(e)}")
return self.get_default_response()
def get_default_response(self):
return {
"analysis": {
"query_focus": "Default focus",
"historical_context": "Default historical context"
},
"axis_configurations": {
"temporal": {
"component": "st.slider",
"current_zoom": {
"level": "century",
"range": [1700, 2000],
"explanation": "Default time range"
},
"available_zooms": {
"in": {
"range": [1800, 1900],
"explanation": "Zoom in to see more detail"
},
"out": {
"range": [1500, 2024],
"explanation": "Broader historical context"
}
}
},
"geographical": {
"current_zoom": {
"level": "continent",
"locations": [
{
"name": "Paris",
"lat": 48.8566,
"lon": 2.3522,
"relevance": "Default location"
}
]
}
},
"style": {
"current_zoom": {
"level": "movement",
"options": ["Classical", "Modern"],
"explanation": "Default art movements"
}
}
}
}
def create_interface(self):
with gr.Blocks() as demo:
gr.Markdown("# Art History Explorer")
with gr.Row():
query = gr.Textbox(
label="Enter your art history query",
placeholder="e.g., Napoleon wars, Renaissance Italy"
)
search_btn = gr.Button("Explore")
with gr.Row():
# Temporal axis
with gr.Column():
time_slider = gr.Slider(
minimum=1000,
maximum=2024,
label="Time Period",
interactive=True
)
time_explanation = gr.Markdown()
time_zoom = gr.Button("πŸ” Zoom Time Period")
# Geographical axis
with gr.Column():
map_plot = gr.Plot(label="Geographic Location")
geo_explanation = gr.Markdown()
geo_zoom = gr.Button("πŸ” Zoom Geography")
with gr.Row():
style_select = gr.Dropdown(
multiselect=True,
label="Artistic Styles"
)
style_explanation = gr.Markdown()
style_zoom = gr.Button("πŸ” Zoom Styles")
def initial_search(query):
config = self.get_llm_response(query)
# Access the correct nested structure
temporal_config = config["axis_configurations"]["temporal"]["current_zoom"]
geographical_config = config["axis_configurations"]["geographical"]["current_zoom"]
style_config = config["axis_configurations"]["style"]["current_zoom"]
# Create map figure
map_fig = self.create_map(geographical_config["locations"])
return {
time_slider: temporal_config["range"],
map_plot: map_fig,
style_select: gr.Dropdown(choices=style_config["options"]),
time_explanation: temporal_config["explanation"],
geo_explanation: geographical_config.get("explanation", ""),
style_explanation: style_config["explanation"]
}
def zoom_axis(query, axis_name, current_value):
self.current_state["zoom_level"] += 1
config = self.get_llm_response(
query,
zoom_context={axis_name: current_value}
)
axis_config = config["axis_configurations"][axis_name]["current_zoom"]
if axis_name == "temporal":
return {
time_slider: axis_config["range"],
time_explanation: axis_config["explanation"]
}
elif axis_name == "geographical":
map_fig = self.create_map(axis_config["locations"])
return {
map_plot: map_fig,
geo_explanation: axis_config.get("explanation", "")
}
else: # style
return {
style_select: gr.Dropdown(choices=axis_config["options"]),
style_explanation: axis_config["explanation"]
}
# Connect event handlers
search_btn.click(
fn=initial_search,
inputs=[query],
outputs=[
time_slider,
map_plot,
style_select,
time_explanation,
geo_explanation,
style_explanation
]
)
time_zoom.click(
fn=lambda q, v: zoom_axis(q, "temporal", v),
inputs=[query, time_slider],
outputs=[time_slider, time_explanation]
)
geo_zoom.click(
fn=lambda q, v: zoom_axis(q, "geographical", v),
inputs=[query, map_plot],
outputs=[map_plot, geo_explanation]
)
style_zoom.click(
fn=lambda q, v: zoom_axis(q, "style", v),
inputs=[query, style_select],
outputs=[style_select, style_explanation]
)
return demo
if __name__ == "__main__":
explorer = ArtExplorer()
demo = explorer.create_interface()
demo.launch()