Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -22,89 +22,193 @@ except Exception as e:
|
|
22 |
raise
|
23 |
|
24 |
def create_interactive_graph(nodes):
|
25 |
-
"""Create an interactive graph visualization using
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
mode='markers+text',
|
66 |
-
hoverinfo='text',
|
67 |
-
text=[G.nodes[node]['title'] for node in G.nodes()],
|
68 |
-
hovertext=node_text,
|
69 |
-
marker=dict(
|
70 |
-
color=node_color,
|
71 |
-
size=30,
|
72 |
-
line=dict(width=2)
|
73 |
-
)
|
74 |
-
)
|
75 |
-
|
76 |
-
# Create edge traces
|
77 |
-
edge_x = []
|
78 |
-
edge_y = []
|
79 |
-
|
80 |
-
for edge in G.edges():
|
81 |
-
x0, y0 = pos[edge[0]]
|
82 |
-
x1, y1 = pos[edge[1]]
|
83 |
-
edge_x.extend([x0, x1, None])
|
84 |
-
edge_y.extend([y0, y1, None])
|
85 |
-
|
86 |
-
edge_trace = go.Scatter(
|
87 |
-
x=edge_x,
|
88 |
-
y=edge_y,
|
89 |
-
line=dict(width=1, color='#888'),
|
90 |
-
hoverinfo='none',
|
91 |
-
mode='lines'
|
92 |
-
)
|
93 |
-
|
94 |
-
# Create figure
|
95 |
-
fig = go.Figure(
|
96 |
-
data=[edge_trace, node_trace],
|
97 |
-
layout=go.Layout(
|
98 |
-
showlegend=False,
|
99 |
-
hovermode='closest',
|
100 |
-
margin=dict(b=0, l=0, r=0, t=0),
|
101 |
-
xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
|
102 |
-
yaxis=dict(showgrid=False, zeroline=False, showticklabels=False)
|
103 |
-
)
|
104 |
-
)
|
105 |
-
|
106 |
-
return fig
|
107 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
def summarize_node(node_data: Dict[str, Any]) -> str:
|
109 |
"""Generate a summary for a selected node"""
|
110 |
summary = f"""
|
@@ -215,7 +319,7 @@ def create_interface() -> gr.Blocks:
|
|
215 |
with gr.Accordion("Exploration Result", open=False):
|
216 |
text_output = gr.JSON(label="Raw Result")
|
217 |
|
218 |
-
graph_output = gr.
|
219 |
node_summary = gr.Textbox(
|
220 |
label="Node Details",
|
221 |
lines=5,
|
@@ -228,19 +332,13 @@ def create_interface() -> gr.Blocks:
|
|
228 |
outputs=[text_output, graph_output, node_summary]
|
229 |
)
|
230 |
|
231 |
-
|
232 |
-
### Examples
|
233 |
-
Click an example to load it into the interface.
|
234 |
-
""")
|
235 |
-
|
236 |
-
examples = [
|
237 |
-
["Explore the evolution of Renaissance painting techniques", "[]", "{}", 5, "Renaissance"],
|
238 |
-
["Investigate the influence of Japanese art on Impressionism", "[]", "{}", 7, "Impressionism"],
|
239 |
-
["Analyze the development of Cubism through Picasso's work", "[]", "{}", 6, "Cubism"]
|
240 |
-
]
|
241 |
-
|
242 |
gr.Examples(
|
243 |
-
examples=
|
|
|
|
|
|
|
|
|
244 |
inputs=[query_input, path_history, parameters, depth, domain]
|
245 |
)
|
246 |
|
|
|
22 |
raise
|
23 |
|
24 |
def create_interactive_graph(nodes):
|
25 |
+
"""Create an interactive graph visualization using D3.js"""
|
26 |
+
html_content = """
|
27 |
+
<div id="network-container" style="width: 100%; height: 600px; border: 1px solid #ddd; border-radius: 4px; background-color: #ffffff;"></div>
|
28 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
|
29 |
+
<style>
|
30 |
+
.node { cursor: pointer; }
|
31 |
+
.node text {
|
32 |
+
font-size: 12px;
|
33 |
+
font-family: Arial, sans-serif;
|
34 |
+
}
|
35 |
+
.link {
|
36 |
+
stroke: #999;
|
37 |
+
stroke-opacity: 0.6;
|
38 |
+
}
|
39 |
+
.node-tooltip {
|
40 |
+
position: absolute;
|
41 |
+
padding: 8px;
|
42 |
+
background: rgba(0, 0, 0, 0.8);
|
43 |
+
color: #fff;
|
44 |
+
border-radius: 4px;
|
45 |
+
font-size: 12px;
|
46 |
+
pointer-events: none;
|
47 |
+
}
|
48 |
+
</style>
|
49 |
+
<script>
|
50 |
+
(function() {
|
51 |
+
// Convert Python data to JavaScript
|
52 |
+
const data = {
|
53 |
+
nodes: """ + json.dumps([{
|
54 |
+
'id': node['id'],
|
55 |
+
'title': node['title'],
|
56 |
+
'description': node['description'],
|
57 |
+
'depth': node['depth']
|
58 |
+
} for node in nodes]) + """,
|
59 |
+
links: """ + json.dumps([{
|
60 |
+
'source': node['id'],
|
61 |
+
'target': conn['target_id'],
|
62 |
+
'value': conn.get('relevance_score', 1)
|
63 |
+
} for node in nodes for conn in node.get('connections', [])]) + """
|
64 |
+
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
|
66 |
+
// Clear any existing visualization
|
67 |
+
const container = document.getElementById('network-container');
|
68 |
+
container.innerHTML = '';
|
69 |
+
|
70 |
+
const width = container.clientWidth;
|
71 |
+
const height = container.clientHeight;
|
72 |
+
|
73 |
+
const svg = d3.select("#network-container")
|
74 |
+
.append("svg")
|
75 |
+
.attr("width", width)
|
76 |
+
.attr("height", height);
|
77 |
+
|
78 |
+
// Add zoom behavior
|
79 |
+
const g = svg.append("g");
|
80 |
+
svg.call(d3.zoom()
|
81 |
+
.scaleExtent([0.1, 4])
|
82 |
+
.on("zoom", (event) => {
|
83 |
+
g.attr("transform", event.transform);
|
84 |
+
}));
|
85 |
+
|
86 |
+
// Create the force simulation
|
87 |
+
const simulation = d3.forceSimulation(data.nodes)
|
88 |
+
.force("link", d3.forceLink(data.links).id(d => d.id).distance(100))
|
89 |
+
.force("charge", d3.forceManyBody().strength(-300))
|
90 |
+
.force("center", d3.forceCenter(width / 2, height / 2))
|
91 |
+
.force("collision", d3.forceCollide().radius(50));
|
92 |
+
|
93 |
+
// Create the links
|
94 |
+
const link = g.append("g")
|
95 |
+
.selectAll("line")
|
96 |
+
.data(data.links)
|
97 |
+
.join("line")
|
98 |
+
.attr("class", "link")
|
99 |
+
.attr("stroke-width", d => Math.sqrt(d.value || 1));
|
100 |
+
|
101 |
+
// Create the nodes
|
102 |
+
const node = g.append("g")
|
103 |
+
.selectAll(".node")
|
104 |
+
.data(data.nodes)
|
105 |
+
.join("g")
|
106 |
+
.attr("class", "node")
|
107 |
+
.call(d3.drag()
|
108 |
+
.on("start", dragstarted)
|
109 |
+
.on("drag", dragged)
|
110 |
+
.on("end", dragended));
|
111 |
+
|
112 |
+
// Add circles to nodes
|
113 |
+
node.append("circle")
|
114 |
+
.attr("r", 20)
|
115 |
+
.attr("fill", d => ['#FF9999', '#99FF99', '#9999FF'][d.depth % 3]);
|
116 |
+
|
117 |
+
// Add text labels
|
118 |
+
node.append("text")
|
119 |
+
.attr("dy", 30)
|
120 |
+
.attr("text-anchor", "middle")
|
121 |
+
.text(d => d.title)
|
122 |
+
.call(wrap, 60);
|
123 |
+
|
124 |
+
// Create tooltip
|
125 |
+
const tooltip = d3.select("body")
|
126 |
+
.append("div")
|
127 |
+
.attr("class", "node-tooltip")
|
128 |
+
.style("opacity", 0);
|
129 |
+
|
130 |
+
// Add hover effects
|
131 |
+
node.on("mouseover", function(event, d) {
|
132 |
+
tooltip.transition()
|
133 |
+
.duration(200)
|
134 |
+
.style("opacity", .9);
|
135 |
+
tooltip.html(`Title: ${d.title}<br/>Description: ${d.description}`)
|
136 |
+
.style("left", (event.pageX + 10) + "px")
|
137 |
+
.style("top", (event.pageY - 10) + "px");
|
138 |
+
})
|
139 |
+
.on("mouseout", function(d) {
|
140 |
+
tooltip.transition()
|
141 |
+
.duration(500)
|
142 |
+
.style("opacity", 0);
|
143 |
+
});
|
144 |
+
|
145 |
+
// Add click handler
|
146 |
+
node.on("click", function(event, d) {
|
147 |
+
if (window.gradio_client) {
|
148 |
+
window.gradio_client.dispatch("select", d);
|
149 |
+
}
|
150 |
+
});
|
151 |
+
|
152 |
+
// Update positions on each tick
|
153 |
+
simulation.on("tick", () => {
|
154 |
+
link
|
155 |
+
.attr("x1", d => d.source.x)
|
156 |
+
.attr("y1", d => d.source.y)
|
157 |
+
.attr("x2", d => d.target.x)
|
158 |
+
.attr("y2", d => d.target.y);
|
159 |
+
|
160 |
+
node
|
161 |
+
.attr("transform", d => `translate(${d.x},${d.y})`);
|
162 |
+
});
|
163 |
+
|
164 |
+
// Drag functions
|
165 |
+
function dragstarted(event) {
|
166 |
+
if (!event.active) simulation.alphaTarget(0.3).restart();
|
167 |
+
event.subject.fx = event.subject.x;
|
168 |
+
event.subject.fy = event.subject.y;
|
169 |
+
}
|
170 |
+
|
171 |
+
function dragged(event) {
|
172 |
+
event.subject.fx = event.x;
|
173 |
+
event.subject.fy = event.y;
|
174 |
+
}
|
175 |
+
|
176 |
+
function dragended(event) {
|
177 |
+
if (!event.active) simulation.alphaTarget(0);
|
178 |
+
event.subject.fx = null;
|
179 |
+
event.subject.fy = null;
|
180 |
+
}
|
181 |
+
|
182 |
+
// Text wrapping function
|
183 |
+
function wrap(text, width) {
|
184 |
+
text.each(function() {
|
185 |
+
const text = d3.select(this);
|
186 |
+
const words = text.text().split(/\s+/).reverse();
|
187 |
+
let word;
|
188 |
+
let line = [];
|
189 |
+
let lineNumber = 0;
|
190 |
+
const lineHeight = 1.1;
|
191 |
+
const y = text.attr("y");
|
192 |
+
const dy = parseFloat(text.attr("dy"));
|
193 |
+
let tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "px");
|
194 |
+
|
195 |
+
while (word = words.pop()) {
|
196 |
+
line.push(word);
|
197 |
+
tspan.text(line.join(" "));
|
198 |
+
if (tspan.node().getComputedTextLength() > width) {
|
199 |
+
line.pop();
|
200 |
+
tspan.text(line.join(" "));
|
201 |
+
line = [word];
|
202 |
+
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "px").text(word);
|
203 |
+
}
|
204 |
+
}
|
205 |
+
});
|
206 |
+
}
|
207 |
+
})();
|
208 |
+
</script>
|
209 |
+
"""
|
210 |
+
return html_content
|
211 |
+
|
212 |
def summarize_node(node_data: Dict[str, Any]) -> str:
|
213 |
"""Generate a summary for a selected node"""
|
214 |
summary = f"""
|
|
|
319 |
with gr.Accordion("Exploration Result", open=False):
|
320 |
text_output = gr.JSON(label="Raw Result")
|
321 |
|
322 |
+
graph_output = gr.HTML(label="Interactive Exploration Graph")
|
323 |
node_summary = gr.Textbox(
|
324 |
label="Node Details",
|
325 |
lines=5,
|
|
|
332 |
outputs=[text_output, graph_output, node_summary]
|
333 |
)
|
334 |
|
335 |
+
# Add examples
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
336 |
gr.Examples(
|
337 |
+
examples=[
|
338 |
+
["Explore the evolution of Renaissance painting techniques", "[]", "{}", 5, "Renaissance"],
|
339 |
+
["Investigate the influence of Japanese art on Impressionism", "[]", "{}", 7, "Impressionism"],
|
340 |
+
["Analyze the development of Cubism through Picasso's work", "[]", "{}", 6, "Cubism"]
|
341 |
+
],
|
342 |
inputs=[query_input, path_history, parameters, depth, domain]
|
343 |
)
|
344 |
|