Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,18 +1,12 @@
|
|
1 |
import os
|
2 |
import json
|
3 |
import gradio as gr
|
4 |
-
import logging
|
5 |
from datetime import datetime
|
6 |
from dotenv import load_dotenv
|
7 |
from openai import OpenAI
|
8 |
-
from typing import Optional, Dict, Any, Tuple
|
9 |
|
10 |
-
#
|
11 |
-
|
12 |
-
level=logging.INFO,
|
13 |
-
format='%(asctime)s - %(levelname)s - %(message)s'
|
14 |
-
)
|
15 |
-
logger = logging.getLogger(__name__)
|
16 |
|
17 |
class ExplorationPathGenerator:
|
18 |
def __init__(self, api_key: str):
|
@@ -20,13 +14,8 @@ class ExplorationPathGenerator:
|
|
20 |
api_key=api_key,
|
21 |
base_url="https://api.groq.com/openai/v1"
|
22 |
)
|
23 |
-
|
24 |
-
def generate_exploration_path(
|
25 |
-
self,
|
26 |
-
query: str,
|
27 |
-
selected_path: Optional[list] = None,
|
28 |
-
exploration_parameters: Optional[dict] = None
|
29 |
-
) -> Dict[str, Any]:
|
30 |
try:
|
31 |
if selected_path is None:
|
32 |
selected_path = []
|
@@ -54,7 +43,7 @@ Response must be valid JSON with this structure:
|
|
54 |
"connections": [
|
55 |
{{
|
56 |
"target_id": "connected_node_id",
|
57 |
-
"
|
58 |
}}
|
59 |
]
|
60 |
}}
|
@@ -70,182 +59,78 @@ Response must be valid JSON with this structure:
|
|
70 |
temperature=0.7,
|
71 |
max_tokens=4000
|
72 |
)
|
73 |
-
|
74 |
result = json.loads(response.choices[0].message.content)
|
75 |
return result
|
76 |
-
|
77 |
except Exception as e:
|
78 |
-
|
79 |
return {"error": str(e)}
|
80 |
|
81 |
-
|
82 |
-
|
83 |
-
"
|
84 |
-
nodes_data = [{
|
85 |
-
'id': node['id'],
|
86 |
-
'title': node['title'],
|
87 |
-
'description': node['description'],
|
88 |
-
'depth': node['depth']
|
89 |
-
} for node in nodes]
|
90 |
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
background: rgba(0, 0, 0, 0.8);
|
124 |
-
color: white;
|
125 |
-
border-radius: 4px;
|
126 |
-
font-size: 12px;
|
127 |
-
pointer-events: none;
|
128 |
-
}}
|
129 |
-
</style>
|
130 |
-
</head>
|
131 |
-
<body>
|
132 |
-
<div id="graph-container"></div>
|
133 |
-
<script>
|
134 |
-
// Data
|
135 |
-
const data = {{
|
136 |
-
nodes: {json.dumps(nodes_data)},
|
137 |
-
links: {json.dumps(links_data)}
|
138 |
-
}};
|
139 |
-
|
140 |
-
// Set up the SVG container
|
141 |
-
const container = document.getElementById('graph-container');
|
142 |
-
const width = container.clientWidth;
|
143 |
-
const height = container.clientHeight;
|
144 |
-
const svg = d3.select("#graph-container")
|
145 |
-
.append("svg")
|
146 |
-
.attr("width", width)
|
147 |
-
.attr("height", height);
|
148 |
-
|
149 |
-
// Add zoom capabilities
|
150 |
-
const g = svg.append("g");
|
151 |
-
const zoom = d3.zoom()
|
152 |
-
.scaleExtent([0.1, 4])
|
153 |
-
.on("zoom", (event) => g.attr("transform", event.transform));
|
154 |
-
svg.call(zoom);
|
155 |
-
|
156 |
-
// Create a force simulation
|
157 |
-
const simulation = d3.forceSimulation(data.nodes)
|
158 |
-
.force("link", d3.forceLink(data.links).id(d => d.id))
|
159 |
-
.force("charge", d3.forceManyBody().strength(-400))
|
160 |
-
.force("center", d3.forceCenter(width / 2, height / 2));
|
161 |
-
|
162 |
-
// Create the links
|
163 |
-
const link = g.append("g")
|
164 |
-
.selectAll("line")
|
165 |
-
.data(data.links)
|
166 |
-
.join("line")
|
167 |
-
.attr("stroke", "#999")
|
168 |
-
.attr("stroke-width", 1);
|
169 |
-
|
170 |
-
// Create the nodes
|
171 |
-
const node = g.append("g")
|
172 |
-
.selectAll("g")
|
173 |
-
.data(data.nodes)
|
174 |
-
.join("g")
|
175 |
-
.call(d3.drag()
|
176 |
-
.on("start", dragstarted)
|
177 |
-
.on("drag", dragged)
|
178 |
-
.on("end", dragended));
|
179 |
-
|
180 |
-
// Add circles to nodes
|
181 |
-
node.append("circle")
|
182 |
-
.attr("r", 20)
|
183 |
-
.attr("fill", d => ['#FF9999', '#99FF99', '#9999FF'][d.depth % 3]);
|
184 |
-
|
185 |
-
// Add labels to nodes
|
186 |
-
node.append("text")
|
187 |
-
.text(d => d.title)
|
188 |
-
.attr("x", 25)
|
189 |
-
.attr("y", 5);
|
190 |
-
|
191 |
-
// Add tooltip
|
192 |
-
const tooltip = d3.select("body").append("div")
|
193 |
-
.attr("class", "tooltip")
|
194 |
-
.style("opacity", 0);
|
195 |
-
|
196 |
-
// Add hover effects
|
197 |
-
node.on("mouseover", function(event, d) {{
|
198 |
-
tooltip.transition()
|
199 |
-
.duration(200)
|
200 |
-
.style("opacity", .9);
|
201 |
-
tooltip.html(`<strong>${{d.title}}</strong><br/>${{d.description}}`)
|
202 |
-
.style("left", (event.pageX + 10) + "px")
|
203 |
-
.style("top", (event.pageY - 10) + "px");
|
204 |
-
}})
|
205 |
-
.on("mouseout", function() {{
|
206 |
-
tooltip.transition()
|
207 |
-
.duration(500)
|
208 |
-
.style("opacity", 0);
|
209 |
-
}});
|
210 |
-
|
211 |
-
// Add click handler
|
212 |
-
node.on("click", function(event, d) {{
|
213 |
-
if (window.gradio) {{
|
214 |
-
window.gradio.dispatch("select", d);
|
215 |
-
}}
|
216 |
-
}});
|
217 |
-
|
218 |
-
// Update positions on each tick
|
219 |
-
simulation.on("tick", () => {{
|
220 |
-
link
|
221 |
-
.attr("x1", d => d.source.x)
|
222 |
-
.attr("y1", d => d.source.y)
|
223 |
-
.attr("x2", d => d.target.x)
|
224 |
-
.attr("y2", d => d.target.y);
|
225 |
-
node.attr("transform", d => `translate(${{d.x}},${{d.y}})`);
|
226 |
-
}});
|
227 |
-
|
228 |
-
// Drag functions
|
229 |
-
function dragstarted(event, d) {{
|
230 |
-
if (!event.active) simulation.alphaTarget(0.3).restart();
|
231 |
-
d.fx = d.x;
|
232 |
-
d.fy = d.y;
|
233 |
-
}}
|
234 |
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
239 |
|
240 |
-
|
241 |
-
if (!event.active) simulation.alphaTarget(0);
|
242 |
-
d.fx = null;
|
243 |
-
d.fy = null;
|
244 |
-
}}
|
245 |
-
</script>
|
246 |
-
</body>
|
247 |
-
</html>
|
248 |
-
"""
|
249 |
return html_content
|
250 |
|
251 |
def explore(query: str, path_history: str = "[]", parameters: str = "{}", depth: int = 5, domain: str = "") -> tuple:
|
@@ -265,9 +150,11 @@ def explore(query: str, path_history: str = "[]", parameters: str = "{}", depth:
|
|
265 |
except json.JSONDecodeError as e:
|
266 |
raise ValueError(f"Invalid JSON input: {str(e)}")
|
267 |
|
268 |
-
# Add
|
269 |
-
|
270 |
-
|
|
|
|
|
271 |
|
272 |
# Generate result
|
273 |
result = generator.generate_exploration_path(
|
@@ -276,11 +163,10 @@ def explore(query: str, path_history: str = "[]", parameters: str = "{}", depth:
|
|
276 |
exploration_parameters=exploration_parameters
|
277 |
)
|
278 |
|
279 |
-
# Create visualization
|
280 |
-
graph_html = generator.
|
281 |
|
282 |
-
|
283 |
-
summary = "Click on nodes in the graph to see detailed information"
|
284 |
|
285 |
return json.dumps(result), graph_html, summary
|
286 |
|
@@ -297,13 +183,11 @@ def create_interface() -> gr.Blocks:
|
|
297 |
"""Create and configure the Gradio interface"""
|
298 |
with gr.Blocks(
|
299 |
title="Art History Exploration Path Generator",
|
300 |
-
theme=gr.themes.Soft()
|
301 |
-
css="#graph-visualization {min-height: 600px;}"
|
302 |
) as interface:
|
303 |
gr.Markdown("""
|
304 |
# Art History Exploration Path Generator
|
305 |
Generate interactive exploration paths through art history topics.
|
306 |
-
Drag nodes to rearrange, zoom with mouse wheel, and click nodes for details.
|
307 |
""")
|
308 |
|
309 |
with gr.Row():
|
@@ -314,20 +198,6 @@ def create_interface() -> gr.Blocks:
|
|
314 |
lines=2
|
315 |
)
|
316 |
|
317 |
-
path_history = gr.Textbox(
|
318 |
-
label="Path History (JSON)",
|
319 |
-
placeholder="[]",
|
320 |
-
lines=3,
|
321 |
-
value="[]"
|
322 |
-
)
|
323 |
-
|
324 |
-
parameters = gr.Textbox(
|
325 |
-
label="Additional Parameters (JSON)",
|
326 |
-
placeholder="{}",
|
327 |
-
lines=3,
|
328 |
-
value="{}"
|
329 |
-
)
|
330 |
-
|
331 |
depth = gr.Slider(
|
332 |
label="Exploration Depth",
|
333 |
minimum=1,
|
@@ -345,40 +215,31 @@ def create_interface() -> gr.Blocks:
|
|
345 |
generate_btn = gr.Button("Generate Exploration Path", variant="primary")
|
346 |
|
347 |
with gr.Column(scale=2):
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
label="Interactive Exploration Graph",
|
352 |
-
value="<div>Generate a path to see the visualization</div>",
|
353 |
-
elem_id="graph-visualization"
|
354 |
-
)
|
355 |
-
node_summary = gr.Textbox(
|
356 |
-
label="Node Details",
|
357 |
-
lines=5,
|
358 |
-
placeholder="Click on nodes to see details"
|
359 |
-
)
|
360 |
|
361 |
generate_btn.click(
|
362 |
fn=explore,
|
363 |
-
inputs=[query_input,
|
364 |
-
|
|
|
365 |
)
|
366 |
|
367 |
gr.Examples(
|
368 |
examples=[
|
369 |
-
["Explore the evolution of Renaissance painting techniques",
|
370 |
-
["Investigate the influence of Japanese art on Impressionism",
|
371 |
-
["Analyze the development of Cubism through Picasso's work",
|
372 |
],
|
373 |
-
inputs=[query_input,
|
374 |
)
|
375 |
|
376 |
return interface
|
377 |
|
378 |
if __name__ == "__main__":
|
379 |
try:
|
380 |
-
|
381 |
-
load_dotenv()
|
382 |
demo = create_interface()
|
383 |
demo.launch(
|
384 |
server_name="0.0.0.0",
|
@@ -386,4 +247,4 @@ if __name__ == "__main__":
|
|
386 |
share=True
|
387 |
)
|
388 |
except Exception as e:
|
389 |
-
|
|
|
1 |
import os
|
2 |
import json
|
3 |
import gradio as gr
|
|
|
4 |
from datetime import datetime
|
5 |
from dotenv import load_dotenv
|
6 |
from openai import OpenAI
|
|
|
7 |
|
8 |
+
# Load environment variables
|
9 |
+
load_dotenv()
|
|
|
|
|
|
|
|
|
10 |
|
11 |
class ExplorationPathGenerator:
|
12 |
def __init__(self, api_key: str):
|
|
|
14 |
api_key=api_key,
|
15 |
base_url="https://api.groq.com/openai/v1"
|
16 |
)
|
17 |
+
|
18 |
+
def generate_exploration_path(self, query: str, selected_path=None, exploration_parameters=None):
|
|
|
|
|
|
|
|
|
|
|
19 |
try:
|
20 |
if selected_path is None:
|
21 |
selected_path = []
|
|
|
43 |
"connections": [
|
44 |
{{
|
45 |
"target_id": "connected_node_id",
|
46 |
+
"relationship": "description of relationship"
|
47 |
}}
|
48 |
]
|
49 |
}}
|
|
|
59 |
temperature=0.7,
|
60 |
max_tokens=4000
|
61 |
)
|
62 |
+
|
63 |
result = json.loads(response.choices[0].message.content)
|
64 |
return result
|
65 |
+
|
66 |
except Exception as e:
|
67 |
+
print(f"Error generating exploration path: {e}")
|
68 |
return {"error": str(e)}
|
69 |
|
70 |
+
def create_visualization_html(self, nodes):
|
71 |
+
"""Create a simple HTML visualization"""
|
72 |
+
html_content = "<div style='padding: 20px; font-family: Arial, sans-serif;'>"
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
|
74 |
+
# Create a style for the nodes
|
75 |
+
html_content += """
|
76 |
+
<style>
|
77 |
+
.node-card {
|
78 |
+
border: 1px solid #ddd;
|
79 |
+
border-radius: 8px;
|
80 |
+
padding: 15px;
|
81 |
+
margin: 10px 0;
|
82 |
+
background-color: #f9f9f9;
|
83 |
+
}
|
84 |
+
.node-title {
|
85 |
+
font-weight: bold;
|
86 |
+
color: #2c3e50;
|
87 |
+
margin-bottom: 8px;
|
88 |
+
}
|
89 |
+
.node-description {
|
90 |
+
color: #34495e;
|
91 |
+
margin-bottom: 8px;
|
92 |
+
}
|
93 |
+
.node-connections {
|
94 |
+
font-size: 0.9em;
|
95 |
+
color: #7f8c8d;
|
96 |
+
}
|
97 |
+
.depth-indicator {
|
98 |
+
display: inline-block;
|
99 |
+
padding: 3px 8px;
|
100 |
+
border-radius: 12px;
|
101 |
+
font-size: 0.8em;
|
102 |
+
margin-bottom: 5px;
|
103 |
+
}
|
104 |
+
</style>
|
105 |
+
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
|
107 |
+
# Create nodes visualization
|
108 |
+
for node in nodes:
|
109 |
+
depth_color = ['#FF9999', '#99FF99', '#9999FF'][node['depth'] % 3]
|
110 |
+
|
111 |
+
html_content += f"""
|
112 |
+
<div class='node-card'>
|
113 |
+
<div class='depth-indicator' style='background-color: {depth_color}'>
|
114 |
+
Depth: {node['depth']}
|
115 |
+
</div>
|
116 |
+
<div class='node-title'>{node['title']}</div>
|
117 |
+
<div class='node-description'>{node['description']}</div>
|
118 |
+
<div class='node-connections'>
|
119 |
+
"""
|
120 |
+
|
121 |
+
# Add connections
|
122 |
+
if node.get('connections'):
|
123 |
+
html_content += "<strong>Connections:</strong><ul>"
|
124 |
+
for conn in node['connections']:
|
125 |
+
html_content += f"<li>Connected to: {conn['target_id']}"
|
126 |
+
if 'relationship' in conn:
|
127 |
+
html_content += f" - {conn['relationship']}"
|
128 |
+
html_content += "</li>"
|
129 |
+
html_content += "</ul>"
|
130 |
+
|
131 |
+
html_content += "</div></div>"
|
132 |
|
133 |
+
html_content += "</div>"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
return html_content
|
135 |
|
136 |
def explore(query: str, path_history: str = "[]", parameters: str = "{}", depth: int = 5, domain: str = "") -> tuple:
|
|
|
150 |
except json.JSONDecodeError as e:
|
151 |
raise ValueError(f"Invalid JSON input: {str(e)}")
|
152 |
|
153 |
+
# Add parameters
|
154 |
+
exploration_parameters.update({
|
155 |
+
"domain": domain,
|
156 |
+
"depth": depth
|
157 |
+
})
|
158 |
|
159 |
# Generate result
|
160 |
result = generator.generate_exploration_path(
|
|
|
163 |
exploration_parameters=exploration_parameters
|
164 |
)
|
165 |
|
166 |
+
# Create visualization using the simplified HTML method
|
167 |
+
graph_html = generator.create_visualization_html(result.get('nodes', []))
|
168 |
|
169 |
+
summary = f"Generated {len(result.get('nodes', []))} nodes for your query"
|
|
|
170 |
|
171 |
return json.dumps(result), graph_html, summary
|
172 |
|
|
|
183 |
"""Create and configure the Gradio interface"""
|
184 |
with gr.Blocks(
|
185 |
title="Art History Exploration Path Generator",
|
186 |
+
theme=gr.themes.Soft()
|
|
|
187 |
) as interface:
|
188 |
gr.Markdown("""
|
189 |
# Art History Exploration Path Generator
|
190 |
Generate interactive exploration paths through art history topics.
|
|
|
191 |
""")
|
192 |
|
193 |
with gr.Row():
|
|
|
198 |
lines=2
|
199 |
)
|
200 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
201 |
depth = gr.Slider(
|
202 |
label="Exploration Depth",
|
203 |
minimum=1,
|
|
|
215 |
generate_btn = gr.Button("Generate Exploration Path", variant="primary")
|
216 |
|
217 |
with gr.Column(scale=2):
|
218 |
+
text_output = gr.JSON(label="Raw Result")
|
219 |
+
graph_output = gr.HTML(label="Visualization")
|
220 |
+
summary_output = gr.Textbox(label="Summary", lines=2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
221 |
|
222 |
generate_btn.click(
|
223 |
fn=explore,
|
224 |
+
inputs=[query_input, gr.Textbox(value="[]", visible=False),
|
225 |
+
gr.Textbox(value="{}", visible=False), depth, domain],
|
226 |
+
outputs=[text_output, graph_output, summary_output]
|
227 |
)
|
228 |
|
229 |
gr.Examples(
|
230 |
examples=[
|
231 |
+
["Explore the evolution of Renaissance painting techniques", 5, "Renaissance"],
|
232 |
+
["Investigate the influence of Japanese art on Impressionism", 7, "Impressionism"],
|
233 |
+
["Analyze the development of Cubism through Picasso's work", 6, "Cubism"]
|
234 |
],
|
235 |
+
inputs=[query_input, depth, domain]
|
236 |
)
|
237 |
|
238 |
return interface
|
239 |
|
240 |
if __name__ == "__main__":
|
241 |
try:
|
242 |
+
print(f"===== Application Startup at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} =====")
|
|
|
243 |
demo = create_interface()
|
244 |
demo.launch(
|
245 |
server_name="0.0.0.0",
|
|
|
247 |
share=True
|
248 |
)
|
249 |
except Exception as e:
|
250 |
+
print(f"Failed to launch interface: {e}")
|