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 { 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 { 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; }> { adapt(data: { entities: Entity[]; relations: Relation[]; metadata?: Record; }): 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; }): 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 { 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 = { 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; metadata?: Record; }): 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 { 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 } }