cognee 0.3.7.dev2__py3-none-any.whl → 0.3.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cognee/base_config.py +7 -0
- cognee/infrastructure/databases/vector/create_vector_engine.py +10 -4
- cognee/infrastructure/files/utils/get_file_metadata.py +3 -3
- cognee/infrastructure/files/utils/guess_file_type.py +19 -88
- cognee/infrastructure/llm/prompts/extract_query_time.txt +13 -15
- cognee/infrastructure/loaders/LoaderEngine.py +2 -1
- cognee/infrastructure/loaders/core/audio_loader.py +1 -0
- cognee/modules/ingestion/data_types/BinaryData.py +1 -1
- cognee/modules/ontology/get_default_ontology_resolver.py +8 -2
- cognee/modules/ontology/rdf_xml/RDFLibOntologyResolver.py +35 -7
- cognee/modules/retrieval/temporal_retriever.py +6 -4
- cognee/modules/visualization/cognee_network_visualization.py +408 -31
- cognee/shared/logging_utils.py +43 -10
- cognee/shared/utils.py +17 -10
- cognee/tasks/ingestion/migrate_relational_database.py +1 -1
- cognee/tests/unit/modules/ontology/test_ontology_adapter.py +151 -0
- {cognee-0.3.7.dev2.dist-info → cognee-0.3.9.dist-info}/METADATA +2 -2
- {cognee-0.3.7.dev2.dist-info → cognee-0.3.9.dist-info}/RECORD +22 -22
- {cognee-0.3.7.dev2.dist-info → cognee-0.3.9.dist-info}/WHEEL +0 -0
- {cognee-0.3.7.dev2.dist-info → cognee-0.3.9.dist-info}/entry_points.txt +0 -0
- {cognee-0.3.7.dev2.dist-info → cognee-0.3.9.dist-info}/licenses/LICENSE +0 -0
- {cognee-0.3.7.dev2.dist-info → cognee-0.3.9.dist-info}/licenses/NOTICE.md +0 -0
|
@@ -16,17 +16,17 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
|
|
16
16
|
|
|
17
17
|
nodes_list = []
|
|
18
18
|
color_map = {
|
|
19
|
-
"Entity": "#
|
|
20
|
-
"EntityType": "#
|
|
21
|
-
"DocumentChunk": "#
|
|
22
|
-
"TextSummary": "#
|
|
23
|
-
"TableRow": "#
|
|
24
|
-
"TableType": "#
|
|
25
|
-
"ColumnValue": "#
|
|
26
|
-
"SchemaTable": "#
|
|
27
|
-
"DatabaseSchema": "#
|
|
28
|
-
"SchemaRelationship": "#
|
|
29
|
-
"default": "#
|
|
19
|
+
"Entity": "#5C10F4",
|
|
20
|
+
"EntityType": "#A550FF",
|
|
21
|
+
"DocumentChunk": "#0DFF00",
|
|
22
|
+
"TextSummary": "#5C10F4",
|
|
23
|
+
"TableRow": "#A550FF",
|
|
24
|
+
"TableType": "#5C10F4",
|
|
25
|
+
"ColumnValue": "#757470",
|
|
26
|
+
"SchemaTable": "#A550FF",
|
|
27
|
+
"DatabaseSchema": "#5C10F4",
|
|
28
|
+
"SchemaRelationship": "#323332",
|
|
29
|
+
"default": "#D8D8D8",
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
for node_id, node_info in nodes_data:
|
|
@@ -98,16 +98,19 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
|
|
98
98
|
<head>
|
|
99
99
|
<meta charset="utf-8">
|
|
100
100
|
<script src="https://d3js.org/d3.v5.min.js"></script>
|
|
101
|
+
<script src="https://d3js.org/d3-contour.v1.min.js"></script>
|
|
101
102
|
<style>
|
|
102
103
|
body, html { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background: linear-gradient(90deg, #101010, #1a1a2e); color: white; font-family: 'Inter', sans-serif; }
|
|
103
104
|
|
|
104
105
|
svg { width: 100vw; height: 100vh; display: block; }
|
|
105
|
-
.links line { stroke: rgba(
|
|
106
|
-
.links line.weighted { stroke: rgba(255, 215, 0, 0.
|
|
107
|
-
.links line.multi-weighted { stroke: rgba(0, 255, 127, 0.
|
|
108
|
-
.nodes circle { stroke: white; stroke-width: 0.5px;
|
|
109
|
-
.node-label { font-size: 5px; font-weight: bold; fill:
|
|
110
|
-
.edge-label { font-size: 3px; fill:
|
|
106
|
+
.links line { stroke: rgba(160, 160, 160, 0.25); stroke-width: 1.5px; stroke-linecap: round; }
|
|
107
|
+
.links line.weighted { stroke: rgba(255, 215, 0, 0.4); }
|
|
108
|
+
.links line.multi-weighted { stroke: rgba(0, 255, 127, 0.45); }
|
|
109
|
+
.nodes circle { stroke: white; stroke-width: 0.5px; }
|
|
110
|
+
.node-label { font-size: 5px; font-weight: bold; fill: #F4F4F4; text-anchor: middle; dominant-baseline: middle; font-family: 'Inter', sans-serif; pointer-events: none; }
|
|
111
|
+
.edge-label { font-size: 3px; fill: #F4F4F4; text-anchor: middle; dominant-baseline: middle; font-family: 'Inter', sans-serif; pointer-events: none; paint-order: stroke; stroke: rgba(50,51,50,0.75); stroke-width: 1px; }
|
|
112
|
+
|
|
113
|
+
.density path { mix-blend-mode: screen; }
|
|
111
114
|
|
|
112
115
|
.tooltip {
|
|
113
116
|
position: absolute;
|
|
@@ -125,11 +128,32 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
|
|
125
128
|
max-width: 300px;
|
|
126
129
|
word-wrap: break-word;
|
|
127
130
|
}
|
|
131
|
+
#info-panel {
|
|
132
|
+
position: fixed;
|
|
133
|
+
left: 12px;
|
|
134
|
+
top: 12px;
|
|
135
|
+
width: 340px;
|
|
136
|
+
max-height: calc(100vh - 24px);
|
|
137
|
+
overflow: auto;
|
|
138
|
+
background: rgba(50, 51, 50, 0.7);
|
|
139
|
+
backdrop-filter: blur(6px);
|
|
140
|
+
border: 1px solid rgba(216, 216, 216, 0.35);
|
|
141
|
+
border-radius: 8px;
|
|
142
|
+
color: #F4F4F4;
|
|
143
|
+
padding: 12px 14px;
|
|
144
|
+
z-index: 1100;
|
|
145
|
+
}
|
|
146
|
+
#info-panel h3 { margin: 0 0 8px 0; font-size: 14px; color: #F4F4F4; }
|
|
147
|
+
#info-panel .kv { font-size: 12px; line-height: 1.4; }
|
|
148
|
+
#info-panel .kv .k { color: #D8D8D8; }
|
|
149
|
+
#info-panel .kv .v { color: #F4F4F4; }
|
|
150
|
+
#info-panel .placeholder { opacity: 0.7; font-size: 12px; }
|
|
128
151
|
</style>
|
|
129
152
|
</head>
|
|
130
153
|
<body>
|
|
131
154
|
<svg></svg>
|
|
132
155
|
<div class="tooltip" id="tooltip"></div>
|
|
156
|
+
<div id="info-panel"><div class="placeholder">Hover a node or edge to inspect details</div></div>
|
|
133
157
|
<script>
|
|
134
158
|
var nodes = {nodes};
|
|
135
159
|
var links = {links};
|
|
@@ -140,19 +164,141 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
|
|
140
164
|
|
|
141
165
|
var container = svg.append("g");
|
|
142
166
|
var tooltip = d3.select("#tooltip");
|
|
167
|
+
var infoPanel = d3.select('#info-panel');
|
|
168
|
+
|
|
169
|
+
function renderInfo(title, entries){
|
|
170
|
+
function esc(s){ return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
|
|
171
|
+
var html = '<h3>' + esc(title) + '</h3>';
|
|
172
|
+
html += '<div class="kv">';
|
|
173
|
+
entries.forEach(function(e){
|
|
174
|
+
html += '<div><span class="k">' + esc(e.k) + ':</span> <span class="v">' + esc(e.v) + '</span></div>';
|
|
175
|
+
});
|
|
176
|
+
html += '</div>';
|
|
177
|
+
infoPanel.html(html);
|
|
178
|
+
}
|
|
179
|
+
function pickDescription(obj){
|
|
180
|
+
if (!obj) return null;
|
|
181
|
+
var keys = ['description','summary','text','content'];
|
|
182
|
+
for (var i=0; i<keys.length; i++){
|
|
183
|
+
var v = obj[keys[i]];
|
|
184
|
+
if (typeof v === 'string' && v.trim()) return v.trim();
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
function truncate(s, n){ if (!s) return s; return s.length > n ? (s.slice(0, n) + '…') : s; }
|
|
189
|
+
function renderNodeInfo(n){
|
|
190
|
+
var entries = [];
|
|
191
|
+
if (n.name) entries.push({k:'Name', v: n.name});
|
|
192
|
+
if (n.type) entries.push({k:'Type', v: n.type});
|
|
193
|
+
if (n.id) entries.push({k:'ID', v: n.id});
|
|
194
|
+
var desc = pickDescription(n) || pickDescription(n.properties);
|
|
195
|
+
if (desc) entries.push({k:'Description', v: truncate(desc.replace(/\s+/g,' ').trim(), 280)});
|
|
196
|
+
if (n.properties) {
|
|
197
|
+
Object.keys(n.properties).slice(0, 12).forEach(function(key){
|
|
198
|
+
var v = n.properties[key];
|
|
199
|
+
if (v !== undefined && v !== null && typeof v !== 'object') entries.push({k: key, v: String(v)});
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
renderInfo(n.name || 'Node', entries);
|
|
203
|
+
}
|
|
204
|
+
function renderEdgeInfo(e){
|
|
205
|
+
var entries = [];
|
|
206
|
+
if (e.relation) entries.push({k:'Relation', v: e.relation});
|
|
207
|
+
if (e.weight !== undefined && e.weight !== null) entries.push({k:'Weight', v: e.weight});
|
|
208
|
+
if (e.all_weights && Object.keys(e.all_weights).length){
|
|
209
|
+
Object.keys(e.all_weights).slice(0, 8).forEach(function(k){ entries.push({k: 'w.'+k, v: e.all_weights[k]}); });
|
|
210
|
+
}
|
|
211
|
+
if (e.relationship_type) entries.push({k:'Type', v: e.relationship_type});
|
|
212
|
+
var edesc = pickDescription(e.edge_info);
|
|
213
|
+
if (edesc) entries.push({k:'Description', v: truncate(edesc.replace(/\s+/g,' ').trim(), 280)});
|
|
214
|
+
renderInfo('Edge', entries);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Basic runtime diagnostics
|
|
218
|
+
console.log('[Cognee Visualization] nodes:', nodes ? nodes.length : 0, 'links:', links ? links.length : 0);
|
|
219
|
+
window.addEventListener('error', function(e){
|
|
220
|
+
try {
|
|
221
|
+
tooltip.html('<strong>Error:</strong> ' + e.message)
|
|
222
|
+
.style('left', '12px')
|
|
223
|
+
.style('top', '12px')
|
|
224
|
+
.style('opacity', 1);
|
|
225
|
+
} catch(_) {}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// Normalize node IDs and link endpoints for robustness
|
|
229
|
+
function resolveId(d){ return (d && (d.id || d.node_id || d.uuid || d.external_id || d.name)) || undefined; }
|
|
230
|
+
if (Array.isArray(nodes)) {
|
|
231
|
+
nodes.forEach(function(n){ var id = resolveId(n); if (id !== undefined) n.id = id; });
|
|
232
|
+
}
|
|
233
|
+
if (Array.isArray(links)) {
|
|
234
|
+
links.forEach(function(l){
|
|
235
|
+
if (typeof l.source === 'object') l.source = resolveId(l.source);
|
|
236
|
+
if (typeof l.target === 'object') l.target = resolveId(l.target);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (!nodes || nodes.length === 0) {
|
|
241
|
+
container.append('text')
|
|
242
|
+
.attr('x', width / 2)
|
|
243
|
+
.attr('y', height / 2)
|
|
244
|
+
.attr('fill', '#fff')
|
|
245
|
+
.attr('font-size', 14)
|
|
246
|
+
.attr('text-anchor', 'middle')
|
|
247
|
+
.text('No graph data available');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Visual defs - reusable glow
|
|
251
|
+
var defs = svg.append("defs");
|
|
252
|
+
var glow = defs.append("filter").attr("id", "glow")
|
|
253
|
+
.attr("x", "-30%")
|
|
254
|
+
.attr("y", "-30%")
|
|
255
|
+
.attr("width", "160%")
|
|
256
|
+
.attr("height", "160%");
|
|
257
|
+
glow.append("feGaussianBlur").attr("stdDeviation", 8).attr("result", "coloredBlur");
|
|
258
|
+
var feMerge = glow.append("feMerge");
|
|
259
|
+
feMerge.append("feMergeNode").attr("in", "coloredBlur");
|
|
260
|
+
feMerge.append("feMergeNode").attr("in", "SourceGraphic");
|
|
261
|
+
|
|
262
|
+
// Stronger glow for hovered adjacency
|
|
263
|
+
var glowStrong = defs.append("filter").attr("id", "glow-strong")
|
|
264
|
+
.attr("x", "-40%")
|
|
265
|
+
.attr("y", "-40%")
|
|
266
|
+
.attr("width", "180%")
|
|
267
|
+
.attr("height", "180%");
|
|
268
|
+
glowStrong.append("feGaussianBlur").attr("stdDeviation", 14).attr("result", "coloredBlur");
|
|
269
|
+
var feMerge2 = glowStrong.append("feMerge");
|
|
270
|
+
feMerge2.append("feMergeNode").attr("in", "coloredBlur");
|
|
271
|
+
feMerge2.append("feMergeNode").attr("in", "SourceGraphic");
|
|
272
|
+
|
|
273
|
+
var currentTransform = d3.zoomIdentity;
|
|
274
|
+
var densityZoomTimer = null;
|
|
275
|
+
var isInteracting = false;
|
|
276
|
+
var labelBaseSize = 10;
|
|
277
|
+
function getGroupKey(d){ return d && (d.type || d.category || d.group || d.color) || 'default'; }
|
|
143
278
|
|
|
144
279
|
var simulation = d3.forceSimulation(nodes)
|
|
145
|
-
.force("link", d3.forceLink(links).id(d
|
|
146
|
-
.force("charge", d3.forceManyBody().strength(-
|
|
280
|
+
.force("link", d3.forceLink(links).id(function(d){ return d.id; }).distance(100).strength(0.2))
|
|
281
|
+
.force("charge", d3.forceManyBody().strength(-180))
|
|
282
|
+
.force("collide", d3.forceCollide().radius(16).iterations(2))
|
|
147
283
|
.force("center", d3.forceCenter(width / 2, height / 2))
|
|
148
|
-
.force("x", d3.forceX().strength(0.
|
|
149
|
-
.force("y", d3.forceY().strength(0.
|
|
284
|
+
.force("x", d3.forceX().strength(0.06).x(width / 2))
|
|
285
|
+
.force("y", d3.forceY().strength(0.06).y(height / 2))
|
|
286
|
+
.alphaDecay(0.06)
|
|
287
|
+
.velocityDecay(0.6);
|
|
288
|
+
|
|
289
|
+
// Density layer (sibling of container to avoid double transforms)
|
|
290
|
+
var densityLayer = svg.append("g")
|
|
291
|
+
.attr("class", "density")
|
|
292
|
+
.style("pointer-events", "none");
|
|
293
|
+
if (densityLayer.lower) densityLayer.lower();
|
|
150
294
|
|
|
151
295
|
var link = container.append("g")
|
|
152
296
|
.attr("class", "links")
|
|
153
297
|
.selectAll("line")
|
|
154
298
|
.data(links)
|
|
155
299
|
.enter().append("line")
|
|
300
|
+
.style("opacity", 0)
|
|
301
|
+
.style("pointer-events", "none")
|
|
156
302
|
.attr("stroke-width", d => {
|
|
157
303
|
if (d.weight) return Math.max(2, d.weight * 5);
|
|
158
304
|
if (d.all_weights && Object.keys(d.all_weights).length > 0) {
|
|
@@ -168,6 +314,7 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
|
|
168
314
|
})
|
|
169
315
|
.on("mouseover", function(d) {
|
|
170
316
|
// Create tooltip content for edge
|
|
317
|
+
renderEdgeInfo(d);
|
|
171
318
|
var content = "<strong>Edge Information</strong><br/>";
|
|
172
319
|
content += "Relationship: " + d.relation + "<br/>";
|
|
173
320
|
|
|
@@ -212,6 +359,7 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
|
|
212
359
|
.data(links)
|
|
213
360
|
.enter().append("text")
|
|
214
361
|
.attr("class", "edge-label")
|
|
362
|
+
.style("opacity", 0)
|
|
215
363
|
.text(d => {
|
|
216
364
|
var label = d.relation;
|
|
217
365
|
if (d.all_weights && Object.keys(d.all_weights).length > 1) {
|
|
@@ -232,21 +380,225 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
|
|
232
380
|
.data(nodes)
|
|
233
381
|
.enter().append("g");
|
|
234
382
|
|
|
383
|
+
// Color fallback by type when d.color is missing
|
|
384
|
+
var colorByType = {
|
|
385
|
+
"Entity": "#5C10F4",
|
|
386
|
+
"EntityType": "#A550FF",
|
|
387
|
+
"DocumentChunk": "#0DFF00",
|
|
388
|
+
"TextSummary": "#5C10F4",
|
|
389
|
+
"TableRow": "#A550FF",
|
|
390
|
+
"TableType": "#5C10F4",
|
|
391
|
+
"ColumnValue": "#757470",
|
|
392
|
+
"SchemaTable": "#A550FF",
|
|
393
|
+
"DatabaseSchema": "#5C10F4",
|
|
394
|
+
"SchemaRelationship": "#323332"
|
|
395
|
+
};
|
|
396
|
+
|
|
235
397
|
var node = nodeGroup.append("circle")
|
|
236
398
|
.attr("r", 13)
|
|
237
|
-
.attr("fill", d
|
|
399
|
+
.attr("fill", function(d){ return d.color || colorByType[d.type] || "#D3D3D3"; })
|
|
400
|
+
.style("filter", "url(#glow)")
|
|
401
|
+
.attr("shape-rendering", "geometricPrecision")
|
|
238
402
|
.call(d3.drag()
|
|
239
403
|
.on("start", dragstarted)
|
|
240
|
-
.on("drag", dragged)
|
|
404
|
+
.on("drag", function(d){ dragged(d); updateDensity(); showAdjacency(d); })
|
|
241
405
|
.on("end", dragended));
|
|
242
406
|
|
|
243
|
-
|
|
407
|
+
// Show links only for hovered node adjacency
|
|
408
|
+
function isAdjacent(linkDatum, nodeId) {
|
|
409
|
+
var sid = linkDatum && linkDatum.source && (linkDatum.source.id || linkDatum.source);
|
|
410
|
+
var tid = linkDatum && linkDatum.target && (linkDatum.target.id || linkDatum.target);
|
|
411
|
+
return sid === nodeId || tid === nodeId;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function showAdjacency(d) {
|
|
415
|
+
var nodeId = d && (d.id || d.node_id || d.uuid || d.external_id || d.name);
|
|
416
|
+
if (!nodeId) return;
|
|
417
|
+
// Build neighbor set
|
|
418
|
+
var neighborIds = {};
|
|
419
|
+
neighborIds[nodeId] = true;
|
|
420
|
+
for (var i = 0; i < links.length; i++) {
|
|
421
|
+
var l = links[i];
|
|
422
|
+
var sid = l && l.source && (l.source.id || l.source);
|
|
423
|
+
var tid = l && l.target && (l.target.id || l.target);
|
|
424
|
+
if (sid === nodeId) neighborIds[tid] = true;
|
|
425
|
+
if (tid === nodeId) neighborIds[sid] = true;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
link
|
|
429
|
+
.style("opacity", function(l){ return isAdjacent(l, nodeId) ? 0.95 : 0; })
|
|
430
|
+
.style("stroke", function(l){ return isAdjacent(l, nodeId) ? "rgba(255,255,255,0.95)" : null; })
|
|
431
|
+
.style("stroke-width", function(l){ return isAdjacent(l, nodeId) ? 2.5 : 1.5; });
|
|
432
|
+
edgeLabels.style("opacity", function(l){ return isAdjacent(l, nodeId) ? 1 : 0; });
|
|
433
|
+
densityLayer.style("opacity", 0.35);
|
|
434
|
+
|
|
435
|
+
// Highlight neighbor nodes and dim others
|
|
436
|
+
node
|
|
437
|
+
.style("opacity", function(n){ return neighborIds[n.id] ? 1 : 0.25; })
|
|
438
|
+
.style("filter", function(n){ return neighborIds[n.id] ? "url(#glow-strong)" : "url(#glow)"; })
|
|
439
|
+
.attr("r", function(n){ return neighborIds[n.id] ? 15 : 13; });
|
|
440
|
+
// Raise highlighted nodes
|
|
441
|
+
node.filter(function(n){ return neighborIds[n.id]; }).raise();
|
|
442
|
+
// Neighbor labels brighter
|
|
443
|
+
nodeGroup.select("text")
|
|
444
|
+
.style("opacity", function(n){ return neighborIds[n.id] ? 1 : 0.2; })
|
|
445
|
+
.style("font-size", function(n){
|
|
446
|
+
var size = neighborIds[n.id] ? Math.min(22, labelBaseSize * 1.25) : labelBaseSize;
|
|
447
|
+
return size + "px";
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function clearAdjacency() {
|
|
452
|
+
link.style("opacity", 0)
|
|
453
|
+
.style("stroke", null)
|
|
454
|
+
.style("stroke-width", 1.5);
|
|
455
|
+
edgeLabels.style("opacity", 0);
|
|
456
|
+
densityLayer.style("opacity", 1);
|
|
457
|
+
node
|
|
458
|
+
.style("opacity", 1)
|
|
459
|
+
.style("filter", "url(#glow)")
|
|
460
|
+
.attr("r", 13);
|
|
461
|
+
nodeGroup.select("text")
|
|
462
|
+
.style("opacity", 1)
|
|
463
|
+
.style("font-size", labelBaseSize + "px");
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
node.on("mouseover", function(d){ showAdjacency(d); })
|
|
467
|
+
.on("mouseout", function(){ clearAdjacency(); });
|
|
468
|
+
node.on("mouseover", function(d){ renderNodeInfo(d); tooltip.style('opacity', 0); });
|
|
469
|
+
// Also bind on the group so labels trigger adjacency too
|
|
470
|
+
nodeGroup.on("mouseover", function(d){ showAdjacency(d); })
|
|
471
|
+
.on("mouseout", function(){ clearAdjacency(); });
|
|
472
|
+
|
|
473
|
+
// Density always on; no hover gating
|
|
474
|
+
|
|
475
|
+
// Add labels sparsely to reduce clutter (every ~50th node), and truncate long text
|
|
476
|
+
nodeGroup
|
|
477
|
+
.filter(function(d, i){ return i % 14 === 0; })
|
|
478
|
+
.append("text")
|
|
244
479
|
.attr("class", "node-label")
|
|
245
480
|
.attr("dy", 4)
|
|
246
481
|
.attr("text-anchor", "middle")
|
|
247
|
-
.text(d
|
|
482
|
+
.text(function(d){
|
|
483
|
+
var s = d && d.name ? String(d.name) : '';
|
|
484
|
+
return s.length > 40 ? (s.slice(0, 40) + "…") : s;
|
|
485
|
+
})
|
|
486
|
+
.style("font-size", labelBaseSize + "px");
|
|
487
|
+
|
|
488
|
+
function applyLabelSize() {
|
|
489
|
+
var k = (currentTransform && currentTransform.k) || 1;
|
|
490
|
+
// Keep labels readable across zoom levels and hide when too small
|
|
491
|
+
labelBaseSize = Math.max(7, Math.min(18, 10 / Math.sqrt(k)));
|
|
492
|
+
nodeGroup.select("text")
|
|
493
|
+
.style("font-size", labelBaseSize + "px")
|
|
494
|
+
.style("display", (k < 0.35 ? "none" : null));
|
|
495
|
+
}
|
|
248
496
|
|
|
249
|
-
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
// Density cloud computation (throttled)
|
|
500
|
+
var densityTick = 0;
|
|
501
|
+
var geoPath = d3.geoPath().projection(null);
|
|
502
|
+
var MAX_POINTS_PER_GROUP = 400;
|
|
503
|
+
function updateDensity() {
|
|
504
|
+
try {
|
|
505
|
+
if (isInteracting) return; // skip during interaction for smoother UX
|
|
506
|
+
if (typeof d3 === 'undefined' || typeof d3.contourDensity !== 'function') {
|
|
507
|
+
return; // d3-contour not available; skip gracefully
|
|
508
|
+
}
|
|
509
|
+
if (!nodes || nodes.length === 0) return;
|
|
510
|
+
var usable = nodes.filter(function(d){ return d && typeof d.x === 'number' && isFinite(d.x) && typeof d.y === 'number' && isFinite(d.y); });
|
|
511
|
+
if (usable.length < 3) return; // not enough positioned points yet
|
|
512
|
+
|
|
513
|
+
var t = currentTransform || d3.zoomIdentity;
|
|
514
|
+
if (t.k && t.k < 0.08) {
|
|
515
|
+
// Skip density at extreme zoom-out to avoid numerical instability/perf issues
|
|
516
|
+
densityLayer.selectAll('*').remove();
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function hexToRgb(hex){
|
|
521
|
+
if (!hex) return {r: 0, g: 200, b: 255};
|
|
522
|
+
var c = hex.replace('#','');
|
|
523
|
+
if (c.length === 3) c = c.split('').map(function(x){ return x+x; }).join('');
|
|
524
|
+
var num = parseInt(c, 16);
|
|
525
|
+
return { r: (num >> 16) & 255, g: (num >> 8) & 255, b: num & 255 };
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Build groups across all nodes
|
|
529
|
+
var groups = {};
|
|
530
|
+
for (var i = 0; i < usable.length; i++) {
|
|
531
|
+
var k = getGroupKey(usable[i]);
|
|
532
|
+
if (!groups[k]) groups[k] = [];
|
|
533
|
+
groups[k].push(usable[i]);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
densityLayer.selectAll('*').remove();
|
|
537
|
+
|
|
538
|
+
Object.keys(groups).forEach(function(key){
|
|
539
|
+
var arr = groups[key];
|
|
540
|
+
if (!arr || arr.length < 3) return;
|
|
541
|
+
|
|
542
|
+
// Transform positions into screen space and sample to cap cost
|
|
543
|
+
var arrT = [];
|
|
544
|
+
var step = Math.max(1, Math.floor(arr.length / MAX_POINTS_PER_GROUP));
|
|
545
|
+
for (var j = 0; j < arr.length; j += step) {
|
|
546
|
+
var nx = t.applyX(arr[j].x);
|
|
547
|
+
var ny = t.applyY(arr[j].y);
|
|
548
|
+
if (isFinite(nx) && isFinite(ny)) {
|
|
549
|
+
arrT.push({ x: nx, y: ny, type: arr[j].type, color: arr[j].color });
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
if (arrT.length < 3) return;
|
|
553
|
+
|
|
554
|
+
// Compute adaptive bandwidth based on group spread
|
|
555
|
+
var cx = 0, cy = 0;
|
|
556
|
+
for (var k = 0; k < arrT.length; k++){ cx += arrT[k].x; cy += arrT[k].y; }
|
|
557
|
+
cx /= arrT.length; cy /= arrT.length;
|
|
558
|
+
var sumR = 0;
|
|
559
|
+
for (var k2 = 0; k2 < arrT.length; k2++){
|
|
560
|
+
var dx = arrT[k2].x - cx, dy = arrT[k2].y - cy;
|
|
561
|
+
sumR += Math.sqrt(dx*dx + dy*dy);
|
|
562
|
+
}
|
|
563
|
+
var avgR = sumR / arrT.length;
|
|
564
|
+
var dynamicBandwidth = Math.max(12, Math.min(80, avgR));
|
|
565
|
+
var densityBandwidth = dynamicBandwidth / (t.k || 1);
|
|
566
|
+
|
|
567
|
+
var contours = d3.contourDensity()
|
|
568
|
+
.x(function(d){ return d.x; })
|
|
569
|
+
.y(function(d){ return d.y; })
|
|
570
|
+
.size([width, height])
|
|
571
|
+
.bandwidth(densityBandwidth)
|
|
572
|
+
.thresholds(8)
|
|
573
|
+
(arrT);
|
|
574
|
+
|
|
575
|
+
if (!contours || contours.length === 0) return;
|
|
576
|
+
var maxVal = d3.max(contours, function(d){ return d.value; }) || 1;
|
|
577
|
+
|
|
578
|
+
// Use the first node color in the group or fallback neon palette
|
|
579
|
+
var baseColor = (arr.find(function(d){ return d && d.color; }) || {}).color || '#00c8ff';
|
|
580
|
+
var rgb = hexToRgb(baseColor);
|
|
581
|
+
|
|
582
|
+
var g = densityLayer.append('g').attr('data-group', key);
|
|
583
|
+
g.selectAll('path')
|
|
584
|
+
.data(contours)
|
|
585
|
+
.enter()
|
|
586
|
+
.append('path')
|
|
587
|
+
.attr('d', geoPath)
|
|
588
|
+
.attr('fill', 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')')
|
|
589
|
+
.attr('stroke', 'none')
|
|
590
|
+
.style('opacity', function(d){
|
|
591
|
+
var v = maxVal ? (d.value / maxVal) : 0;
|
|
592
|
+
var alpha = Math.pow(Math.max(0, Math.min(1, v)), 1.6); // accentuate clusters
|
|
593
|
+
return 0.65 * alpha; // up to 0.65 opacity at peak density
|
|
594
|
+
})
|
|
595
|
+
.style('filter', 'blur(2px)');
|
|
596
|
+
});
|
|
597
|
+
} catch (e) {
|
|
598
|
+
// Reduce impact of any runtime errors during zoom
|
|
599
|
+
console.warn('Density update failed:', e);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
250
602
|
|
|
251
603
|
simulation.on("tick", function() {
|
|
252
604
|
link.attr("x1", d => d.source.x)
|
|
@@ -266,16 +618,29 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
|
|
266
618
|
.attr("y", d => d.y)
|
|
267
619
|
.attr("dy", 4)
|
|
268
620
|
.attr("text-anchor", "middle");
|
|
621
|
+
|
|
622
|
+
densityTick += 1;
|
|
623
|
+
if (densityTick % 24 === 0) updateDensity();
|
|
269
624
|
});
|
|
270
625
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
626
|
+
var zoomBehavior = d3.zoom()
|
|
627
|
+
.on("start", function(){ isInteracting = true; densityLayer.style("opacity", 0.2); })
|
|
628
|
+
.on("zoom", function(){
|
|
629
|
+
currentTransform = d3.event.transform;
|
|
630
|
+
container.attr("transform", currentTransform);
|
|
631
|
+
})
|
|
632
|
+
.on("end", function(){
|
|
633
|
+
if (densityZoomTimer) clearTimeout(densityZoomTimer);
|
|
634
|
+
densityZoomTimer = setTimeout(function(){ isInteracting = false; densityLayer.style("opacity", 1); updateDensity(); }, 140);
|
|
635
|
+
});
|
|
636
|
+
svg.call(zoomBehavior);
|
|
274
637
|
|
|
275
638
|
function dragstarted(d) {
|
|
276
639
|
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
|
|
277
640
|
d.fx = d.x;
|
|
278
641
|
d.fy = d.y;
|
|
642
|
+
isInteracting = true;
|
|
643
|
+
densityLayer.style("opacity", 0.2);
|
|
279
644
|
}
|
|
280
645
|
|
|
281
646
|
function dragged(d) {
|
|
@@ -287,6 +652,8 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
|
|
287
652
|
if (!d3.event.active) simulation.alphaTarget(0);
|
|
288
653
|
d.fx = null;
|
|
289
654
|
d.fy = null;
|
|
655
|
+
if (densityZoomTimer) clearTimeout(densityZoomTimer);
|
|
656
|
+
densityZoomTimer = setTimeout(function(){ isInteracting = false; densityLayer.style("opacity", 1); updateDensity(); }, 140);
|
|
290
657
|
}
|
|
291
658
|
|
|
292
659
|
window.addEventListener("resize", function() {
|
|
@@ -295,7 +662,13 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
|
|
295
662
|
svg.attr("width", width).attr("height", height);
|
|
296
663
|
simulation.force("center", d3.forceCenter(width / 2, height / 2));
|
|
297
664
|
simulation.alpha(1).restart();
|
|
665
|
+
updateDensity();
|
|
666
|
+
applyLabelSize();
|
|
298
667
|
});
|
|
668
|
+
|
|
669
|
+
// Initial density draw
|
|
670
|
+
updateDensity();
|
|
671
|
+
applyLabelSize();
|
|
299
672
|
</script>
|
|
300
673
|
|
|
301
674
|
<svg style="position: fixed; bottom: 10px; right: 10px; width: 150px; height: auto; z-index: 9999;" viewBox="0 0 158 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
@@ -305,8 +678,12 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
|
|
305
678
|
</html>
|
|
306
679
|
"""
|
|
307
680
|
|
|
308
|
-
|
|
309
|
-
|
|
681
|
+
# Safely embed JSON inside <script> by escaping </ to avoid prematurely closing the tag
|
|
682
|
+
def _safe_json_embed(obj):
|
|
683
|
+
return json.dumps(obj).replace("</", "<\\/")
|
|
684
|
+
|
|
685
|
+
html_content = html_template.replace("{nodes}", _safe_json_embed(nodes_list))
|
|
686
|
+
html_content = html_content.replace("{links}", _safe_json_embed(links_list))
|
|
310
687
|
|
|
311
688
|
if not destination_file_path:
|
|
312
689
|
home_dir = os.path.expanduser("~")
|
cognee/shared/logging_utils.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sys
|
|
3
3
|
import logging
|
|
4
|
+
import tempfile
|
|
4
5
|
import structlog
|
|
5
6
|
import traceback
|
|
6
7
|
import platform
|
|
@@ -76,14 +77,38 @@ log_levels = {
|
|
|
76
77
|
# Track if structlog logging has been configured
|
|
77
78
|
_is_structlog_configured = False
|
|
78
79
|
|
|
79
|
-
# Path to logs directory
|
|
80
|
-
LOGS_DIR = Path(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "logs"))
|
|
81
80
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
81
|
+
def resolve_logs_dir():
|
|
82
|
+
"""Resolve a writable logs directory.
|
|
83
|
+
|
|
84
|
+
Priority:
|
|
85
|
+
1) BaseConfig.logs_root_directory (respects COGNEE_LOGS_DIR)
|
|
86
|
+
2) /tmp/cognee_logs (default, best-effort create)
|
|
87
|
+
|
|
88
|
+
Returns a Path or None if none are writable/creatable.
|
|
89
|
+
"""
|
|
90
|
+
from cognee.base_config import get_base_config
|
|
91
|
+
|
|
92
|
+
base_config = get_base_config()
|
|
93
|
+
logs_root_directory = Path(base_config.logs_root_directory)
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
logs_root_directory.mkdir(parents=True, exist_ok=True)
|
|
97
|
+
if os.access(logs_root_directory, os.W_OK):
|
|
98
|
+
return logs_root_directory
|
|
99
|
+
except Exception:
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
tmp_log_path = Path(os.path.join("/tmp", "cognee_logs"))
|
|
104
|
+
tmp_log_path.mkdir(parents=True, exist_ok=True)
|
|
105
|
+
if os.access(tmp_log_path, os.W_OK):
|
|
106
|
+
return tmp_log_path
|
|
107
|
+
except Exception:
|
|
108
|
+
pass
|
|
109
|
+
|
|
110
|
+
return None
|
|
111
|
+
|
|
87
112
|
|
|
88
113
|
# Maximum number of log files to keep
|
|
89
114
|
MAX_LOG_FILES = 10
|
|
@@ -444,15 +469,18 @@ def setup_logging(log_level=None, name=None):
|
|
|
444
469
|
# can define their own levels.
|
|
445
470
|
root_logger.setLevel(logging.NOTSET)
|
|
446
471
|
|
|
472
|
+
# Resolve logs directory with env and safe fallbacks
|
|
473
|
+
logs_dir = resolve_logs_dir()
|
|
474
|
+
|
|
447
475
|
# Check if we already have a log file path from the environment
|
|
448
476
|
# NOTE: environment variable must be used here as it allows us to
|
|
449
477
|
# log to a single file with a name based on a timestamp in a multiprocess setting.
|
|
450
478
|
# Without it, we would have a separate log file for every process.
|
|
451
479
|
log_file_path = os.environ.get("LOG_FILE_NAME")
|
|
452
|
-
if not log_file_path:
|
|
480
|
+
if not log_file_path and logs_dir is not None:
|
|
453
481
|
# Create a new log file name with the cognee start time
|
|
454
482
|
start_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
|
455
|
-
log_file_path =
|
|
483
|
+
log_file_path = str((logs_dir / f"{start_time}.log").resolve())
|
|
456
484
|
os.environ["LOG_FILE_NAME"] = log_file_path
|
|
457
485
|
|
|
458
486
|
try:
|
|
@@ -478,7 +506,8 @@ def setup_logging(log_level=None, name=None):
|
|
|
478
506
|
)
|
|
479
507
|
|
|
480
508
|
# Clean up old log files, keeping only the most recent ones
|
|
481
|
-
|
|
509
|
+
if logs_dir is not None:
|
|
510
|
+
cleanup_old_logs(logs_dir, MAX_LOG_FILES)
|
|
482
511
|
|
|
483
512
|
# Mark logging as configured
|
|
484
513
|
_is_structlog_configured = True
|
|
@@ -502,6 +531,10 @@ def setup_logging(log_level=None, name=None):
|
|
|
502
531
|
|
|
503
532
|
# Get a configured logger and log system information
|
|
504
533
|
logger = structlog.get_logger(name if name else __name__)
|
|
534
|
+
|
|
535
|
+
if logs_dir is not None:
|
|
536
|
+
logger.info(f"Log file created at: {log_file_path}", log_file=log_file_path)
|
|
537
|
+
|
|
505
538
|
# Detailed initialization for regular usage
|
|
506
539
|
logger.info(
|
|
507
540
|
"Logging initialized",
|