AgentGraph / frontend /src /lib /graph-data-adapters.ts
wu981526092's picture
πŸš€ Deploy AgentGraph: Complete agent monitoring and knowledge graph system
c2ea5ed
import {
UniversalGraphData,
UniversalNode,
UniversalLink,
GraphDataAdapter,
GraphVisualizationConfig,
GraphVisualizationType,
} from "@/types/graph-visualization";
import { TemporalNode, TemporalLink, GraphFrame } from "@/types/temporal";
import { Entity, Relation } from "@/types";
import {
detectSchemaType,
getSchemaCapabilities,
} from "@/lib/schema-detection";
// Temporal Data Adapter
export class TemporalDataAdapter implements GraphDataAdapter<GraphFrame> {
adapt(data: GraphFrame): UniversalGraphData {
const nodes: UniversalNode[] = data.nodes.map((node: TemporalNode) => ({
id: node.id,
name: node.name || node.id,
label: node.name || node.id,
type: node.type || "entity",
raw_prompt: node.raw_prompt,
importance: (node as any).importance,
x: node.x,
y: node.y,
fx: node.fx,
fy: node.fy,
properties: { ...node },
}));
const links: UniversalLink[] = data.links.map((link: TemporalLink) => ({
id: link.id,
source: typeof link.source === "string" ? link.source : link.source.id,
target: typeof link.target === "string" ? link.target : link.target.id,
type: link.type || "relation",
label: link.type || "relation",
relationship: link.type,
interaction_prompt: link.interaction_prompt,
importance: (link as any).importance,
value: link.value || 1,
weight: link.value || 1,
properties: { ...link },
}));
return {
nodes,
links,
metadata: data.metadata,
};
}
validate(data: GraphFrame): boolean {
return (
data &&
Array.isArray(data.nodes) &&
Array.isArray(data.links) &&
data.nodes.every((node) => node.id) &&
data.links.every((link) => link.id && link.source && link.target)
);
}
getDefaultConfig(): Partial<GraphVisualizationConfig> {
return {
type: GraphVisualizationType.TEMPORAL_GRAPH,
nodeRadius: 8,
linkDistance: 100,
chargeStrength: -300,
showNodeLabels: true,
showLinkLabels: true,
showDirectionalArrows: true,
useCurvedLinks: true,
enableZoom: true,
enablePan: true,
enableDrag: true,
enableSelection: true,
layoutType: "force",
animationDuration: 500,
};
}
}
// Knowledge Graph Data Adapter
export class KnowledgeGraphDataAdapter
implements
GraphDataAdapter<{
entities: Entity[];
relations: Relation[];
metadata?: Record<string, any>;
}>
{
adapt(data: {
entities: Entity[];
relations: Relation[];
metadata?: Record<string, any>;
}): UniversalGraphData {
console.log("πŸ”§ KnowledgeGraphDataAdapter.adapt() called with:", {
entities: data.entities?.length || 0,
relations: data.relations?.length || 0,
hasMetadata: !!data.metadata,
});
const nodes: UniversalNode[] = data.entities.map(
(entity: Entity, index) => {
// Debug logging to check entity data
console.log(`Entity ${index}:`, {
id: entity.id,
name: entity.name,
type: entity.type,
hasName: !!entity.name,
nameLength: entity.name?.length || 0,
});
// Ensure we have a proper label - fallback to ID if name is missing
const label = entity.name || entity.id || `Node ${index}`;
return {
id: entity.id,
name: entity.name,
label: label,
type: entity.type,
raw_prompt: (entity as any).raw_prompt,
raw_prompt_ref: (entity as any).raw_prompt_ref,
raw_text_ref: (entity as any).raw_text_ref,
importance: (entity as any).importance,
group: this.getTypeGroup(entity.type),
properties: entity.properties,
// Simple grid layout for initial positioning
x: 100 + (index % 6) * 120,
y: 100 + Math.floor(index / 6) * 100,
};
}
);
// Create set of valid node IDs for relation validation
const validNodeIds = new Set(nodes.map((node) => node.id));
// Filter relations to only include those with valid source and target nodes
const validRelations = data.relations.filter((relation: Relation) => {
const hasValidSource = validNodeIds.has(relation.source);
const hasValidTarget = validNodeIds.has(relation.target);
if (!hasValidSource || !hasValidTarget) {
console.warn(
`Skipping relation ${relation.id}: ` +
`source '${relation.source}' ${
hasValidSource ? "exists" : "missing"
}, ` +
`target '${relation.target}' ${
hasValidTarget ? "exists" : "missing"
}`
);
return false;
}
return true;
});
// Log filtering stats
const filteredCount = data.relations.length - validRelations.length;
if (filteredCount > 0) {
console.warn(
`Filtered out ${filteredCount} invalid relations out of ${data.relations.length} total`
);
}
const links: UniversalLink[] = validRelations.map((relation: Relation) => ({
id: relation.id,
source: relation.source,
target: relation.target,
type: relation.type,
label: relation.type,
relationship: relation.type,
interaction_prompt: relation.interaction_prompt,
interaction_prompt_ref: (relation as any).interaction_prompt_ref,
importance: (relation as any).importance,
value: 1,
weight: 1,
properties: relation.properties,
}));
// Detect schema type and add capabilities
const schemaType = detectSchemaType(data);
const schemaCapabilities = getSchemaCapabilities(schemaType);
return {
nodes,
links,
failures: (data as any).failures || [],
metadata: {
...data.metadata,
schema_type: schemaType,
schema_capabilities: schemaCapabilities,
},
};
}
validate(data: {
entities: Entity[];
relations: Relation[];
metadata?: Record<string, any>;
}): boolean {
return (
data &&
Array.isArray(data.entities) &&
Array.isArray(data.relations) &&
data.entities.every((entity) => entity.id && entity.name) &&
data.relations.every(
(relation) =>
relation.id && relation.source && relation.target && relation.type
)
);
}
getDefaultConfig(): Partial<GraphVisualizationConfig> {
return {
type: GraphVisualizationType.KNOWLEDGE_GRAPH,
nodeRadius: 15,
linkDistance: 100,
chargeStrength: -300,
showNodeLabels: true,
showLinkLabels: true,
showDirectionalArrows: false,
useCurvedLinks: false,
enableZoom: true,
enablePan: true,
enableDrag: true,
enableSelection: true,
layoutType: "force",
animationDuration: 500,
nodeColorScheme: [
"#ff6b6b",
"#4ecdc4",
"#45b7d1",
"#96ceb4",
"#feca57",
"#ff9ff3",
],
};
}
private getTypeGroup(type: string): number {
const typeGroups: Record<string, number> = {
Person: 1,
Organization: 2,
Location: 3,
Event: 4,
Concept: 5,
Product: 6,
};
return typeGroups[type] || 0;
}
}
// Reference-Based Data Adapter (extends KnowledgeGraphDataAdapter)
export class ReferenceBasedDataAdapter extends KnowledgeGraphDataAdapter {
adapt(data: {
entities: Entity[];
relations: Relation[];
failures?: Array<any>;
metadata?: Record<string, any>;
}): UniversalGraphData {
console.log(
"🎯 ReferenceBasedDataAdapter.adapt() called - will delegate to parent"
);
// Use parent adapter for basic conversion
const result = super.adapt(data);
// Enhance with reference-based specific features
result.failures = data.failures || [];
// Ensure schema type is set to reference_based
if (result.metadata) {
result.metadata.schema_type = "reference_based";
result.metadata.schema_capabilities =
getSchemaCapabilities("reference_based");
}
return result;
}
validate(data: any): boolean {
// Use parent validation plus check for reference-based features
const basicValid = super.validate(data);
if (!basicValid) return false;
// Additional validation for reference-based features
const hasReferenceFeatures = detectSchemaType(data) === "reference_based";
return hasReferenceFeatures;
}
}
// Simple Graph Data Adapter (for backward compatibility)
export class SimpleGraphDataAdapter
implements
GraphDataAdapter<{
nodes: Array<{
id: string;
name: string;
type: string;
[key: string]: any;
}>;
links: Array<{
source: string;
target: string;
type: string;
[key: string]: any;
}>;
}>
{
adapt(data: {
nodes: Array<{
id: string;
name: string;
type: string;
[key: string]: any;
}>;
links: Array<{
source: string;
target: string;
type: string;
[key: string]: any;
}>;
}): UniversalGraphData {
const nodes: UniversalNode[] = data.nodes.map((node, index) => ({
id: node.id,
name: node.name,
label: node.name,
type: node.type,
importance: node.importance,
properties: { ...node },
// Simple grid layout
x: 100 + (index % 4) * 120,
y: 100 + Math.floor(index / 4) * 100,
}));
const links: UniversalLink[] = data.links.map((link, index) => ({
id: `link-${index}`,
source: link.source,
target: link.target,
type: link.type,
label: link.type,
relationship: link.type,
importance: link.importance,
value: 1,
weight: 1,
properties: { ...link },
}));
return {
nodes,
links,
metadata: {
entity_count: nodes.length,
relation_count: links.length,
},
};
}
validate(data: {
nodes: Array<{
id: string;
name: string;
type: string;
[key: string]: any;
}>;
links: Array<{
source: string;
target: string;
type: string;
[key: string]: any;
}>;
}): boolean {
return (
data &&
Array.isArray(data.nodes) &&
Array.isArray(data.links) &&
data.nodes.every((node) => node.id && node.name && node.type) &&
data.links.every((link) => link.source && link.target && link.type)
);
}
getDefaultConfig(): Partial<GraphVisualizationConfig> {
return {
type: GraphVisualizationType.SIMPLE_GRAPH,
nodeRadius: 20,
linkDistance: 80,
chargeStrength: -200,
showNodeLabels: true,
showLinkLabels: true,
showDirectionalArrows: false,
useCurvedLinks: false,
enableZoom: false,
enablePan: false,
enableDrag: false,
enableSelection: false,
layoutType: "grid",
animationDuration: 0,
};
}
}
// Factory function to get appropriate adapter
export function getGraphDataAdapter(
type?: GraphVisualizationType,
data?: any
): GraphDataAdapter {
console.log("🏭 getGraphDataAdapter called with:", { type, hasData: !!data });
// Auto-detect schema if data provided but no type specified
if (!type && data) {
try {
const schemaType = detectSchemaType(data);
console.log(
"πŸ” Auto-detecting adapter type based on schema:",
schemaType
);
if (schemaType === "reference_based") {
type = GraphVisualizationType.KNOWLEDGE_GRAPH;
} else {
type = GraphVisualizationType.SIMPLE_GRAPH;
}
} catch (error) {
console.warn(
"Schema detection failed in adapter factory, using default KNOWLEDGE_GRAPH:",
error
);
type = GraphVisualizationType.KNOWLEDGE_GRAPH;
}
}
// Default to KNOWLEDGE_GRAPH if no type provided
if (!type) {
type = GraphVisualizationType.KNOWLEDGE_GRAPH;
}
switch (type) {
case GraphVisualizationType.TEMPORAL_GRAPH:
return new TemporalDataAdapter();
case GraphVisualizationType.KNOWLEDGE_GRAPH:
// Use ReferenceBasedDataAdapter for reference-based data, otherwise KnowledgeGraphDataAdapter
if (data) {
try {
if (detectSchemaType(data) === "reference_based") {
console.log(
"βœ… Using ReferenceBasedDataAdapter for reference-based schema"
);
return new ReferenceBasedDataAdapter();
}
} catch (error) {
console.warn(
"Schema detection failed during adapter selection, using KnowledgeGraphDataAdapter:",
error
);
}
}
console.log(
"βœ… Using KnowledgeGraphDataAdapter for direct-based or unknown schema"
);
return new KnowledgeGraphDataAdapter();
case GraphVisualizationType.SIMPLE_GRAPH:
console.log("βœ… Using SimpleGraphDataAdapter");
return new SimpleGraphDataAdapter();
default:
console.log("βœ… Using KnowledgeGraphDataAdapter (default fallback)");
return new KnowledgeGraphDataAdapter(); // Default fallback
}
}