feat: Universal Import with intelligent type matching (v2.1.0)

 ONE universal import method for everything
- Auto-detects files, URLs, and raw data
- Intelligent noun/verb type matching using embeddings
- Support for JSON, CSV, YAML, and text formats
- Zero configuration required

🧠 Intelligent Type Matching
- Uses semantic embeddings to match 31 noun types
- Automatically detects 40 verb relationship types
- Confidence scores for type predictions
- Caching for improved performance

📦 Import Manager
- Centralized import logic with lazy loading
- Integrates NeuralImportAugmentation for AI processing
- Proper CSV parsing with quote handling
- Basic YAML support

🎯 Simplified API
- brain.import() - ONE method that handles everything
- Auto-detection of URLs and file paths
- Backwards compatible with existing code
- Clean, modern, delightful developer experience

📚 Documentation
- Comprehensive import guide in docs/guides/import-anything.md
- Examples for every format and use case
- Philosophy of simplicity and zero config

 Tests
- Full unit test coverage for import functionality
- Type matching tests for all 31 nouns and 40 verbs
- Tests for CSV, YAML, JSON, and text formats

BREAKING CHANGES: None - fully backward compatible
This commit is contained in:
David Snelling 2025-08-27 12:11:05 -07:00
parent 492267b509
commit 1ef01f394a
10 changed files with 1833 additions and 102 deletions

View file

@ -11,6 +11,8 @@ import { BaseAugmentation, AugmentationContext } from './brainyAugmentation.js'
import { NounType, VerbType } from '../types/graphTypes.js'
import * as fs from '../universal/fs.js'
import * as path from '../universal/path.js'
import { IntelligentTypeMatcher, getTypeMatcher } from './typeMatching/intelligentTypeMatcher.js'
import { prodLog } from '../utils/logger.js'
// Neural Import Analysis Types
export interface NeuralAnalysisResult {
@ -68,6 +70,7 @@ export class NeuralImportAugmentation extends BaseAugmentation {
private config: NeuralImportConfig
private analysisCache = new Map<string, NeuralAnalysisResult>()
private typeMatcher: IntelligentTypeMatcher | null = null
constructor(config: Partial<NeuralImportConfig> = {}) {
super()
@ -81,7 +84,12 @@ export class NeuralImportAugmentation extends BaseAugmentation {
}
protected async onInitialize(): Promise<void> {
this.log('🧠 Neural Import augmentation initialized')
try {
this.typeMatcher = await getTypeMatcher()
this.log('🧠 Neural Import augmentation initialized with intelligent type matching')
} catch (error) {
this.log('⚠️ Failed to initialize type matcher, falling back to heuristics', 'warn')
}
}
protected async onShutdown(): Promise<void> {
@ -195,12 +203,7 @@ export class NeuralImportAugmentation extends BaseAugmentation {
case 'yaml':
case 'yml':
// For now, basic YAML support - in full implementation would use yaml parser
try {
return JSON.parse(content) // Placeholder
} catch {
return [{ text: content }]
}
return this.parseYAML(content)
case 'txt':
case 'text':
@ -214,26 +217,179 @@ export class NeuralImportAugmentation extends BaseAugmentation {
}
/**
* Parse CSV data
* Parse CSV data - handles quoted values, escaped quotes, and edge cases
*/
private parseCSV(content: string): any[] {
const lines = content.split('\n').filter(line => line.trim())
const lines = content.split('\n')
if (lines.length === 0) return []
const headers = lines[0].split(',').map(h => h.trim())
// Parse a CSV line handling quotes
const parseLine = (line: string): string[] => {
const result: string[] = []
let current = ''
let inQuotes = false
let i = 0
while (i < line.length) {
const char = line[i]
const nextChar = line[i + 1]
if (char === '"') {
if (inQuotes && nextChar === '"') {
// Escaped quote
current += '"'
i += 2
} else {
// Toggle quote mode
inQuotes = !inQuotes
i++
}
} else if (char === ',' && !inQuotes) {
// Field separator
result.push(current.trim())
current = ''
i++
} else {
current += char
i++
}
}
// Add last field
result.push(current.trim())
return result
}
// Parse headers
const headers = parseLine(lines[0])
const data = []
// Parse data rows
for (let i = 1; i < lines.length; i++) {
const values = lines[i].split(',').map(v => v.trim())
const line = lines[i].trim()
if (!line) continue // Skip empty lines
const values = parseLine(line)
const row: any = {}
headers.forEach((header, index) => {
row[header] = values[index] || ''
const value = values[index] || ''
// Try to parse numbers
const num = Number(value)
row[header] = !isNaN(num) && value !== '' ? num : value
})
data.push(row)
}
return data
}
/**
* Parse YAML data
*/
private parseYAML(content: string): any[] {
try {
// Simple YAML parser for basic structures
// For full YAML support, we'd use js-yaml library
const lines = content.split('\n')
const result: any[] = []
let currentObject: any = null
let currentIndent = 0
for (const line of lines) {
const trimmed = line.trim()
if (!trimmed || trimmed.startsWith('#')) continue // Skip empty lines and comments
// Calculate indentation
const indent = line.length - line.trimStart().length
// Check for array item
if (trimmed.startsWith('- ')) {
const value = trimmed.substring(2).trim()
if (indent === 0) {
// Top-level array item
if (value.includes(':')) {
// Object in array
currentObject = {}
result.push(currentObject)
const [key, val] = value.split(':').map(s => s.trim())
currentObject[key] = this.parseYAMLValue(val)
} else {
result.push(this.parseYAMLValue(value))
}
} else if (currentObject) {
// Nested array
const lastKey = Object.keys(currentObject).pop()
if (lastKey) {
if (!Array.isArray(currentObject[lastKey])) {
currentObject[lastKey] = []
}
currentObject[lastKey].push(this.parseYAMLValue(value))
}
}
} else if (trimmed.includes(':')) {
// Key-value pair
const colonIndex = trimmed.indexOf(':')
const key = trimmed.substring(0, colonIndex).trim()
const value = trimmed.substring(colonIndex + 1).trim()
if (indent === 0) {
// Top-level object
if (!currentObject) {
currentObject = {}
result.push(currentObject)
}
currentObject[key] = this.parseYAMLValue(value)
currentIndent = 0
} else if (currentObject) {
// Nested object
if (indent > currentIndent && !value) {
// Start of nested object
const lastKey = Object.keys(currentObject).pop()
if (lastKey) {
currentObject[lastKey] = { [key]: '' }
}
} else {
currentObject[key] = this.parseYAMLValue(value)
}
currentIndent = indent
}
}
}
// If we built a single object and not an array, wrap it
if (result.length === 0 && currentObject) {
result.push(currentObject)
}
return result.length > 0 ? result : [{ text: content }]
} catch (error) {
prodLog.warn('YAML parsing failed, treating as text:', error)
return [{ text: content }]
}
}
/**
* Parse a YAML value (handle strings, numbers, booleans, null)
*/
private parseYAMLValue(value: string): any {
if (!value || value === '~' || value === 'null') return null
if (value === 'true') return true
if (value === 'false') return false
// Remove quotes if present
if ((value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))) {
return value.slice(1, -1)
}
// Try to parse as number
const num = Number(value)
if (!isNaN(num) && value !== '') return num
return value
}
/**
* Perform neural analysis on parsed data
@ -251,7 +407,7 @@ export class NeuralImportAugmentation extends BaseAugmentation {
detectedEntities.push({
originalData: item,
nounType: this.inferNounType(item),
nounType: await this.inferNounType(item),
confidence: 0.85,
suggestedId: String(entityId),
reasoning: 'Detected from structured data',
@ -259,7 +415,7 @@ export class NeuralImportAugmentation extends BaseAugmentation {
})
// Detect relationships from references
this.detectRelationships(item, entityId, detectedRelationships)
await this.detectRelationships(item, entityId, detectedRelationships)
}
}
@ -295,32 +451,35 @@ export class NeuralImportAugmentation extends BaseAugmentation {
}
/**
* Infer noun type from object structure
* Infer noun type from object structure using intelligent type matching
*/
private inferNounType(obj: any): string {
// Simple heuristics for type detection
if (obj.email || obj.username) return 'Person'
if (obj.title && obj.content) return 'Document'
if (obj.price || obj.product) return 'Product'
if (obj.date || obj.timestamp) return 'Event'
if (obj.url || obj.link) return 'Resource'
if (obj.lat || obj.longitude) return 'Location'
private async inferNounType(obj: any): Promise<string> {
if (!this.typeMatcher) {
// Initialize type matcher if not available
this.typeMatcher = await getTypeMatcher()
}
// Default fallback
return 'Entity'
const result = await this.typeMatcher.matchNounType(obj)
// Log if confidence is low for debugging
if (result.confidence < 0.5) {
this.log(`Low confidence (${result.confidence.toFixed(2)}) for noun type: ${result.type}`, 'warn')
}
return result.type
}
/**
* Detect relationships from object references
*/
private detectRelationships(obj: any, sourceId: string, relationships: DetectedRelationship[]): void {
private async detectRelationships(obj: any, sourceId: string, relationships: DetectedRelationship[]): Promise<void> {
// Look for reference patterns
for (const [key, value] of Object.entries(obj)) {
if (key.endsWith('Id') || key.endsWith('_id') || key === 'parentId' || key === 'userId') {
relationships.push({
sourceId,
targetId: String(value),
verbType: this.inferVerbType(key),
verbType: await this.inferVerbType(key, obj, { id: value }),
confidence: 0.75,
weight: 1,
reasoning: `Reference detected in field: ${key}`,
@ -335,7 +494,7 @@ export class NeuralImportAugmentation extends BaseAugmentation {
relationships.push({
sourceId,
targetId: String(targetId),
verbType: this.inferVerbType(key),
verbType: await this.inferVerbType(key, obj, { id: targetId }),
confidence: 0.7,
weight: 1,
reasoning: `Array reference in field: ${key}`,
@ -348,21 +507,22 @@ export class NeuralImportAugmentation extends BaseAugmentation {
}
/**
* Infer verb type from field name
* Infer verb type from field name using intelligent type matching
*/
private inferVerbType(fieldName: string): string {
const normalized = fieldName.toLowerCase()
private async inferVerbType(fieldName: string, sourceObj?: any, targetObj?: any): Promise<string> {
if (!this.typeMatcher) {
// Initialize type matcher if not available
this.typeMatcher = await getTypeMatcher()
}
if (normalized.includes('parent')) return 'childOf'
if (normalized.includes('user')) return 'belongsTo'
if (normalized.includes('author')) return 'authoredBy'
if (normalized.includes('owner')) return 'ownedBy'
if (normalized.includes('creator')) return 'createdBy'
if (normalized.includes('member')) return 'memberOf'
if (normalized.includes('tag')) return 'taggedWith'
if (normalized.includes('category')) return 'categorizedAs'
const result = await this.typeMatcher.matchVerbType(sourceObj, targetObj, fieldName)
return 'relatedTo'
// Log if confidence is low for debugging
if (result.confidence < 0.5) {
this.log(`Low confidence (${result.confidence.toFixed(2)}) for verb type: ${result.type}`, 'warn')
}
return result.type
}
/**

View file

@ -0,0 +1,534 @@
/**
* Intelligent Type Matcher - Uses embeddings for semantic type detection
*
* This module uses our existing TransformerEmbedding and similarity functions
* to intelligently match data to our 31 noun types and 40 verb types.
*
* Features:
* - Semantic similarity matching using embeddings
* - Context-aware type detection
* - Confidence scoring
* - Caching for performance
*/
import { NounType, VerbType } from '../../types/graphTypes.js'
import { TransformerEmbedding } from '../../utils/embedding.js'
import { cosineDistance } from '../../utils/distance.js'
import { Vector } from '../../coreTypes.js'
/**
* Type descriptions for semantic matching
* These descriptions are used to generate embeddings for each type
*/
const NOUN_TYPE_DESCRIPTIONS: Record<string, string> = {
// Core Entity Types
[NounType.Person]: 'person human individual user employee customer citizen member author creator agent actor participant',
[NounType.Organization]: 'organization company business corporation institution agency department team group committee board',
[NounType.Location]: 'location place address city country region area zone coordinate position site venue building',
[NounType.Thing]: 'thing object item product device equipment tool instrument asset artifact material physical tangible',
[NounType.Concept]: 'concept idea theory principle philosophy belief value abstract intangible notion thought',
[NounType.Event]: 'event occurrence incident activity happening meeting conference celebration milestone timestamp date',
// Digital/Content Types
[NounType.Document]: 'document file report article paper text pdf word contract agreement record documentation',
[NounType.Media]: 'media image photo video audio music podcast multimedia graphic visualization animation',
[NounType.File]: 'file digital data binary code script program software archive package bundle',
[NounType.Message]: 'message email chat communication notification alert announcement broadcast transmission',
[NounType.Content]: 'content information data text material resource publication post blog webpage',
// Collection Types
[NounType.Collection]: 'collection group set list array category folder directory catalog inventory database',
[NounType.Dataset]: 'dataset data table spreadsheet database records statistics metrics measurements analysis',
// Business/Application Types
[NounType.Product]: 'product item merchandise offering service feature application software solution package',
[NounType.Service]: 'service offering subscription support maintenance utility function capability',
[NounType.User]: 'user account profile member subscriber customer client participant identity credentials',
[NounType.Task]: 'task action todo item job assignment duty responsibility activity step procedure',
[NounType.Project]: 'project initiative program campaign effort endeavor plan scheme venture undertaking',
// Descriptive Types
[NounType.Process]: 'process workflow procedure method algorithm sequence pipeline operation routine protocol',
[NounType.State]: 'state status condition phase stage mode situation circumstance configuration setting',
[NounType.Role]: 'role position title function responsibility duty job capacity designation authority',
[NounType.Topic]: 'topic subject theme category tag keyword area domain field discipline specialty',
[NounType.Language]: 'language dialect locale tongue vernacular communication speech linguistics vocabulary',
[NounType.Currency]: 'currency money dollar euro pound yen bitcoin payment financial monetary unit',
[NounType.Measurement]: 'measurement metric quantity value amount size dimension weight height volume distance',
// Scientific/Research Types
[NounType.Hypothesis]: 'hypothesis theory proposition thesis assumption premise conjecture speculation prediction',
[NounType.Experiment]: 'experiment test trial study research investigation analysis observation examination',
// Legal/Regulatory Types
[NounType.Contract]: 'contract agreement deal treaty pact covenant license terms conditions policy',
[NounType.Regulation]: 'regulation law rule policy standard compliance requirement guideline ordinance statute',
// Technical Infrastructure Types
[NounType.Interface]: 'interface API endpoint protocol specification contract schema definition connection',
[NounType.Resource]: 'resource infrastructure server database storage compute memory bandwidth capacity asset'
}
const VERB_TYPE_DESCRIPTIONS: Record<string, string> = {
// Core Relationship Types
[VerbType.RelatedTo]: 'related connected associated linked correlated relevant pertinent applicable',
[VerbType.Contains]: 'contains includes holds stores encompasses comprises consists incorporates',
[VerbType.PartOf]: 'part component element member piece portion section segment constituent',
[VerbType.LocatedAt]: 'located situated positioned placed found exists resides occupies',
[VerbType.References]: 'references cites mentions points links refers quotes sources',
// Temporal/Causal Types
[VerbType.Precedes]: 'precedes before earlier prior previous antecedent preliminary foregoing',
[VerbType.Succeeds]: 'succeeds follows after later subsequent next ensuing succeeding',
[VerbType.Causes]: 'causes triggers induces produces generates results influences affects',
[VerbType.DependsOn]: 'depends requires needs relies necessitates contingent prerequisite',
[VerbType.Requires]: 'requires needs demands necessitates mandates obliges compels entails',
// Creation/Transformation Types
[VerbType.Creates]: 'creates makes produces generates builds constructs forms establishes',
[VerbType.Transforms]: 'transforms converts changes modifies alters transitions morphs evolves',
[VerbType.Becomes]: 'becomes turns evolves transforms changes transitions develops grows',
[VerbType.Modifies]: 'modifies changes updates alters edits revises adjusts adapts',
[VerbType.Consumes]: 'consumes uses utilizes depletes expends absorbs takes processes',
// Ownership/Attribution Types
[VerbType.Owns]: 'owns possesses holds controls manages administers governs maintains',
[VerbType.AttributedTo]: 'attributed credited assigned ascribed authored written composed',
[VerbType.CreatedBy]: 'created made produced generated built developed authored written',
[VerbType.BelongsTo]: 'belongs property possession part member affiliate associated owned',
// Social/Organizational Types
[VerbType.MemberOf]: 'member participant affiliate associate belongs joined enrolled registered',
[VerbType.WorksWith]: 'works collaborates cooperates partners teams assists helps supports',
[VerbType.FriendOf]: 'friend companion buddy pal acquaintance associate connection relationship',
[VerbType.Follows]: 'follows subscribes tracks monitors watches observes trails pursues',
[VerbType.Likes]: 'likes enjoys appreciates favors prefers admires values endorses',
[VerbType.ReportsTo]: 'reports answers subordinate accountable responsible supervised managed',
[VerbType.Supervises]: 'supervises manages oversees directs leads controls guides administers',
[VerbType.Mentors]: 'mentors teaches guides coaches instructs trains advises counsels',
[VerbType.Communicates]: 'communicates talks speaks messages contacts interacts corresponds exchanges',
// Descriptive/Functional Types
[VerbType.Describes]: 'describes explains details documents specifies outlines depicts characterizes',
[VerbType.Defines]: 'defines specifies establishes determines sets declares identifies designates',
[VerbType.Categorizes]: 'categorizes classifies groups sorts organizes arranges labels tags',
[VerbType.Measures]: 'measures quantifies gauges assesses evaluates calculates determines counts',
[VerbType.Evaluates]: 'evaluates assesses analyzes reviews examines appraises judges rates',
[VerbType.Uses]: 'uses utilizes employs applies operates handles manipulates exploits',
[VerbType.Implements]: 'implements executes realizes performs accomplishes carries delivers completes',
[VerbType.Extends]: 'extends expands enhances augments amplifies broadens enlarges develops',
// Enhanced Relationships
[VerbType.Inherits]: 'inherits derives extends receives obtains acquires succeeds legacy',
[VerbType.Conflicts]: 'conflicts contradicts opposes clashes disputes disagrees incompatible inconsistent',
[VerbType.Synchronizes]: 'synchronizes coordinates aligns harmonizes matches corresponds parallels coincides',
[VerbType.Competes]: 'competes rivals contends contests challenges opposes vies struggles'
}
/**
* Result of type matching with confidence scores
*/
export interface TypeMatchResult {
type: string
confidence: number
reasoning: string
alternatives: Array<{
type: string
confidence: number
}>
}
/**
* Intelligent Type Matcher using semantic embeddings
*/
export class IntelligentTypeMatcher {
private embedder: TransformerEmbedding
private nounEmbeddings: Map<string, Vector> = new Map()
private verbEmbeddings: Map<string, Vector> = new Map()
private initialized = false
private cache: Map<string, TypeMatchResult> = new Map()
constructor() {
this.embedder = new TransformerEmbedding({ verbose: false })
}
/**
* Initialize the type matcher by generating embeddings for all types
*/
async init(): Promise<void> {
if (this.initialized) return
await this.embedder.init()
// Generate embeddings for noun types
for (const [type, description] of Object.entries(NOUN_TYPE_DESCRIPTIONS)) {
const embedding = await this.embedder.embed(description)
this.nounEmbeddings.set(type, embedding)
}
// Generate embeddings for verb types
for (const [type, description] of Object.entries(VERB_TYPE_DESCRIPTIONS)) {
const embedding = await this.embedder.embed(description)
this.verbEmbeddings.set(type, embedding)
}
this.initialized = true
}
/**
* Match an object to the most appropriate noun type
*/
async matchNounType(obj: any): Promise<TypeMatchResult> {
await this.init()
// Create a text representation of the object for embedding
const textRepresentation = this.createTextRepresentation(obj)
// Check cache
const cacheKey = `noun:${textRepresentation}`
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey)!
}
// Generate embedding for the input
const inputEmbedding = await this.embedder.embed(textRepresentation)
// Calculate similarities to all noun types
const similarities: Array<{ type: string; similarity: number }> = []
for (const [type, typeEmbedding] of this.nounEmbeddings.entries()) {
// Convert cosine distance to similarity (1 - distance)
const similarity = 1 - cosineDistance(inputEmbedding, typeEmbedding)
similarities.push({ type, similarity })
}
// Sort by similarity (highest first)
similarities.sort((a, b) => b.similarity - a.similarity)
// Apply heuristic rules for common patterns
const heuristicType = this.applyNounHeuristics(obj)
if (heuristicType) {
// Boost the heuristic type's confidence
const heuristicIndex = similarities.findIndex(s => s.type === heuristicType)
if (heuristicIndex > 0) {
similarities[heuristicIndex].similarity *= 1.2 // 20% boost
similarities.sort((a, b) => b.similarity - a.similarity)
}
}
// Create result
const result: TypeMatchResult = {
type: similarities[0].type,
confidence: similarities[0].similarity,
reasoning: this.generateReasoning(obj, similarities[0].type, 'noun'),
alternatives: similarities.slice(1, 4).map(s => ({
type: s.type,
confidence: s.similarity
}))
}
// Cache result
this.cache.set(cacheKey, result)
return result
}
/**
* Match a relationship to the most appropriate verb type
*/
async matchVerbType(
sourceObj: any,
targetObj: any,
relationshipHint?: string
): Promise<TypeMatchResult> {
await this.init()
// Create text representation of the relationship
const textRepresentation = this.createRelationshipText(sourceObj, targetObj, relationshipHint)
// Check cache
const cacheKey = `verb:${textRepresentation}`
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey)!
}
// Generate embedding
const inputEmbedding = await this.embedder.embed(textRepresentation)
// Calculate similarities to all verb types
const similarities: Array<{ type: string; similarity: number }> = []
for (const [type, typeEmbedding] of this.verbEmbeddings.entries()) {
const similarity = 1 - cosineDistance(inputEmbedding, typeEmbedding)
similarities.push({ type, similarity })
}
// Sort by similarity
similarities.sort((a, b) => b.similarity - a.similarity)
// Apply heuristic rules
const heuristicType = this.applyVerbHeuristics(sourceObj, targetObj, relationshipHint)
if (heuristicType) {
const heuristicIndex = similarities.findIndex(s => s.type === heuristicType)
if (heuristicIndex > 0) {
similarities[heuristicIndex].similarity *= 1.2
similarities.sort((a, b) => b.similarity - a.similarity)
}
}
// Create result
const result: TypeMatchResult = {
type: similarities[0].type,
confidence: similarities[0].similarity,
reasoning: this.generateReasoning(
{ source: sourceObj, target: targetObj, hint: relationshipHint },
similarities[0].type,
'verb'
),
alternatives: similarities.slice(1, 4).map(s => ({
type: s.type,
confidence: s.similarity
}))
}
// Cache result
this.cache.set(cacheKey, result)
return result
}
/**
* Create text representation of an object for embedding
*/
private createTextRepresentation(obj: any): string {
const parts: string[] = []
// Add type if available
if (typeof obj === 'object' && obj !== null) {
// Add field names and values
for (const [key, value] of Object.entries(obj)) {
parts.push(key)
if (typeof value === 'string') {
parts.push(value.slice(0, 100)) // Limit string length
} else if (typeof value === 'number' || typeof value === 'boolean') {
parts.push(String(value))
}
}
// Add special fields with higher weight
const importantFields = ['type', 'kind', 'category', 'class', 'name', 'title', 'description']
for (const field of importantFields) {
if (obj[field]) {
parts.push(String(obj[field]))
parts.push(String(obj[field])) // Double weight for important fields
}
}
} else if (typeof obj === 'string') {
parts.push(obj)
} else {
parts.push(String(obj))
}
return parts.join(' ')
}
/**
* Create text representation of a relationship
*/
private createRelationshipText(
sourceObj: any,
targetObj: any,
relationshipHint?: string
): string {
const parts: string[] = []
if (relationshipHint) {
parts.push(relationshipHint)
parts.push(relationshipHint) // Double weight for explicit hint
}
// Add source context
if (sourceObj) {
parts.push('source:')
parts.push(this.getObjectSummary(sourceObj))
}
// Add target context
if (targetObj) {
parts.push('target:')
parts.push(this.getObjectSummary(targetObj))
}
return parts.join(' ')
}
/**
* Get a brief summary of an object
*/
private getObjectSummary(obj: any): string {
if (typeof obj === 'string') return obj.slice(0, 50)
if (typeof obj !== 'object' || obj === null) return String(obj)
const summary: string[] = []
const fields = ['type', 'name', 'title', 'id', 'category', 'kind']
for (const field of fields) {
if (obj[field]) {
summary.push(String(obj[field]))
}
}
return summary.join(' ').slice(0, 100)
}
/**
* Apply heuristic rules for noun type detection
*/
private applyNounHeuristics(obj: any): string | null {
if (typeof obj !== 'object' || obj === null) return null
// Person heuristics
if (obj.email || obj.firstName || obj.lastName || obj.username || obj.age || obj.gender) {
return NounType.Person
}
// Organization heuristics
if (obj.companyName || obj.organizationId || obj.employees || obj.industry) {
return NounType.Organization
}
// Location heuristics
if (obj.latitude || obj.longitude || obj.address || obj.city || obj.country || obj.coordinates) {
return NounType.Location
}
// Document heuristics
if (obj.content && (obj.title || obj.author) || obj.documentType || obj.pages) {
return NounType.Document
}
// Event heuristics
if (obj.startTime || obj.endTime || obj.date || obj.eventType || obj.attendees) {
return NounType.Event
}
// Product heuristics
if (obj.price || obj.sku || obj.inventory || obj.productId) {
return NounType.Product
}
// Task heuristics
if (obj.status && (obj.assignee || obj.dueDate) || obj.priority || obj.completed !== undefined) {
return NounType.Task
}
// Media heuristics
if (obj.url && (obj.url.match(/\.(jpg|jpeg|png|gif|mp4|mp3|wav)/i))) {
return NounType.Media
}
// Dataset heuristics
if (Array.isArray(obj.data) || obj.rows || obj.columns || obj.schema) {
return NounType.Dataset
}
return null
}
/**
* Apply heuristic rules for verb type detection
*/
private applyVerbHeuristics(
sourceObj: any,
targetObj: any,
relationshipHint?: string
): string | null {
if (!relationshipHint) return null
const hint = relationshipHint.toLowerCase()
// Ownership patterns
if (hint.includes('own') || hint.includes('possess') || hint.includes('has')) {
return VerbType.Owns
}
// Creation patterns
if (hint.includes('create') || hint.includes('made') || hint.includes('authored')) {
return VerbType.Creates
}
// Containment patterns
if (hint.includes('contain') || hint.includes('include') || hint.includes('has')) {
return VerbType.Contains
}
// Membership patterns
if (hint.includes('member') || hint.includes('belong') || hint.includes('part')) {
return VerbType.MemberOf
}
// Reference patterns
if (hint.includes('refer') || hint.includes('cite') || hint.includes('link')) {
return VerbType.References
}
// Dependency patterns
if (hint.includes('depend') || hint.includes('require') || hint.includes('need')) {
return VerbType.DependsOn
}
return null
}
/**
* Generate human-readable reasoning for the type selection
*/
private generateReasoning(
obj: any,
selectedType: string,
typeKind: 'noun' | 'verb'
): string {
const descriptions = typeKind === 'noun' ? NOUN_TYPE_DESCRIPTIONS : VERB_TYPE_DESCRIPTIONS
const typeDesc = descriptions[selectedType]
if (typeKind === 'noun') {
const fields = Object.keys(obj).slice(0, 3).join(', ')
return `Matched to ${selectedType} based on semantic similarity to "${typeDesc.split(' ').slice(0, 5).join(' ')}..." and object fields: ${fields}`
} else {
return `Matched to ${selectedType} based on semantic similarity to "${typeDesc.split(' ').slice(0, 5).join(' ')}..." and relationship context`
}
}
/**
* Clear the cache
*/
clearCache(): void {
this.cache.clear()
}
/**
* Dispose of resources
*/
async dispose(): Promise<void> {
await this.embedder.dispose()
this.cache.clear()
this.nounEmbeddings.clear()
this.verbEmbeddings.clear()
}
}
/**
* Singleton instance for efficient reuse
*/
let globalMatcher: IntelligentTypeMatcher | null = null
/**
* Get or create the global type matcher instance
*/
export async function getTypeMatcher(): Promise<IntelligentTypeMatcher> {
if (!globalMatcher) {
globalMatcher = new IntelligentTypeMatcher()
await globalMatcher.init()
}
return globalMatcher
}