2025-06-24 11:41:30 -07:00
|
|
|
/**
|
2025-08-05 19:29:59 -07:00
|
|
|
* Embedding functions for converting data to vectors using Transformers.js
|
|
|
|
|
* Complete rewrite to eliminate TensorFlow.js and use ONNX-based models
|
2025-06-24 11:41:30 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { EmbeddingFunction, EmbeddingModel, Vector } from '../coreTypes.js'
|
|
|
|
|
import { executeInThread } from './workerUtils.js'
|
2025-07-28 10:04:45 -07:00
|
|
|
import { isBrowser } from './environment.js'
|
2025-08-05 19:29:59 -07:00
|
|
|
import { pipeline, env } from '@huggingface/transformers'
|
2025-06-24 11:41:30 -07:00
|
|
|
|
|
|
|
|
/**
|
2025-08-05 19:29:59 -07:00
|
|
|
* Transformers.js Sentence Encoder embedding model
|
|
|
|
|
* Uses ONNX Runtime for fast, offline embeddings with smaller models
|
|
|
|
|
* Default model: all-MiniLM-L6-v2 (384 dimensions, ~90MB)
|
2025-06-24 11:41:30 -07:00
|
|
|
*/
|
2025-08-05 19:29:59 -07:00
|
|
|
export interface TransformerEmbeddingOptions {
|
|
|
|
|
/** Model name/path to use - defaults to all-MiniLM-L6-v2 */
|
|
|
|
|
model?: string
|
**feat(docs): add comprehensive documentation for model bundling and robust loading**
- Introduced new documentation files under `docs/`:
- `model-bundling-analysis.md`: Provides detailed analysis of current, bundled, hybrid, and dynamic model loading approaches, including pros, cons, and recommendations.
- `model-management.md`: Explains how Brainy manages Universal Sentence Encoder models, including setup, usage, and troubleshooting.
- `optional-model-bundling.md`: Details the `@soulcraft/brainy-models` package for offline reliability with pre-bundled models.
- Added `src/utils/robustModelLoader.ts`:
- Implements enhanced model loading with retry mechanisms, timeout handling, fallback URLs, and optional local model bundling.
- Supports Node.js and browser environments with exponential backoff logic.
- Key Updates:
- **Hybrid Loading Strategy**: Recommended for balancing reliability and flexibility via hybrid online/offline mechanisms.
- **Enhanced Fallback Scenarios**: Robust loader improves network-dependent reliability for embedding workflows.
- **Offline Reliability Support**: Optional model bundling eliminates dependency on external services, supporting air-gapped and edge environments.
**Purpose**: Introduce a hybrid model loading approach with robust options for
2025-08-01 15:35:29 -07:00
|
|
|
/** Whether to enable verbose logging */
|
|
|
|
|
verbose?: boolean
|
2025-08-05 19:29:59 -07:00
|
|
|
/** Custom cache directory for models */
|
|
|
|
|
cacheDir?: string
|
|
|
|
|
/** Force local files only (no downloads) */
|
|
|
|
|
localFilesOnly?: boolean
|
|
|
|
|
/** Quantization setting (fp32, fp16, q8, q4) */
|
|
|
|
|
dtype?: 'fp32' | 'fp16' | 'q8' | 'q4'
|
**feat(docs): add comprehensive documentation for model bundling and robust loading**
- Introduced new documentation files under `docs/`:
- `model-bundling-analysis.md`: Provides detailed analysis of current, bundled, hybrid, and dynamic model loading approaches, including pros, cons, and recommendations.
- `model-management.md`: Explains how Brainy manages Universal Sentence Encoder models, including setup, usage, and troubleshooting.
- `optional-model-bundling.md`: Details the `@soulcraft/brainy-models` package for offline reliability with pre-bundled models.
- Added `src/utils/robustModelLoader.ts`:
- Implements enhanced model loading with retry mechanisms, timeout handling, fallback URLs, and optional local model bundling.
- Supports Node.js and browser environments with exponential backoff logic.
- Key Updates:
- **Hybrid Loading Strategy**: Recommended for balancing reliability and flexibility via hybrid online/offline mechanisms.
- **Enhanced Fallback Scenarios**: Robust loader improves network-dependent reliability for embedding workflows.
- **Offline Reliability Support**: Optional model bundling eliminates dependency on external services, supporting air-gapped and edge environments.
**Purpose**: Introduce a hybrid model loading approach with robust options for
2025-08-01 15:35:29 -07:00
|
|
|
}
|
|
|
|
|
|
2025-08-05 19:29:59 -07:00
|
|
|
export class TransformerEmbedding implements EmbeddingModel {
|
|
|
|
|
private extractor: any = null
|
2025-06-24 11:41:30 -07:00
|
|
|
private initialized = false
|
2025-08-05 19:29:59 -07:00
|
|
|
private verbose: boolean = true
|
|
|
|
|
private options: Required<TransformerEmbeddingOptions>
|
**feat(docs): add comprehensive documentation for model bundling and robust loading**
- Introduced new documentation files under `docs/`:
- `model-bundling-analysis.md`: Provides detailed analysis of current, bundled, hybrid, and dynamic model loading approaches, including pros, cons, and recommendations.
- `model-management.md`: Explains how Brainy manages Universal Sentence Encoder models, including setup, usage, and troubleshooting.
- `optional-model-bundling.md`: Details the `@soulcraft/brainy-models` package for offline reliability with pre-bundled models.
- Added `src/utils/robustModelLoader.ts`:
- Implements enhanced model loading with retry mechanisms, timeout handling, fallback URLs, and optional local model bundling.
- Supports Node.js and browser environments with exponential backoff logic.
- Key Updates:
- **Hybrid Loading Strategy**: Recommended for balancing reliability and flexibility via hybrid online/offline mechanisms.
- **Enhanced Fallback Scenarios**: Robust loader improves network-dependent reliability for embedding workflows.
- **Offline Reliability Support**: Optional model bundling eliminates dependency on external services, supporting air-gapped and edge environments.
**Purpose**: Introduce a hybrid model loading approach with robust options for
2025-08-01 15:35:29 -07:00
|
|
|
|
2025-07-18 10:40:37 -07:00
|
|
|
/**
|
2025-08-05 19:29:59 -07:00
|
|
|
* Create a new TransformerEmbedding instance
|
2025-07-18 10:40:37 -07:00
|
|
|
*/
|
2025-08-05 19:29:59 -07:00
|
|
|
constructor(options: TransformerEmbeddingOptions = {}) {
|
2025-07-18 10:40:37 -07:00
|
|
|
this.verbose = options.verbose !== undefined ? options.verbose : true
|
2025-08-05 19:29:59 -07:00
|
|
|
this.options = {
|
|
|
|
|
model: options.model || 'Xenova/all-MiniLM-L6-v2',
|
**feat(docs): add comprehensive documentation for model bundling and robust loading**
- Introduced new documentation files under `docs/`:
- `model-bundling-analysis.md`: Provides detailed analysis of current, bundled, hybrid, and dynamic model loading approaches, including pros, cons, and recommendations.
- `model-management.md`: Explains how Brainy manages Universal Sentence Encoder models, including setup, usage, and troubleshooting.
- `optional-model-bundling.md`: Details the `@soulcraft/brainy-models` package for offline reliability with pre-bundled models.
- Added `src/utils/robustModelLoader.ts`:
- Implements enhanced model loading with retry mechanisms, timeout handling, fallback URLs, and optional local model bundling.
- Supports Node.js and browser environments with exponential backoff logic.
- Key Updates:
- **Hybrid Loading Strategy**: Recommended for balancing reliability and flexibility via hybrid online/offline mechanisms.
- **Enhanced Fallback Scenarios**: Robust loader improves network-dependent reliability for embedding workflows.
- **Offline Reliability Support**: Optional model bundling eliminates dependency on external services, supporting air-gapped and edge environments.
**Purpose**: Introduce a hybrid model loading approach with robust options for
2025-08-01 15:35:29 -07:00
|
|
|
verbose: this.verbose,
|
2025-08-05 19:29:59 -07:00
|
|
|
cacheDir: options.cacheDir || this.getDefaultCacheDir(),
|
2025-08-05 19:38:26 -07:00
|
|
|
localFilesOnly: options.localFilesOnly !== undefined ? options.localFilesOnly : !isBrowser(),
|
2025-08-05 19:29:59 -07:00
|
|
|
dtype: options.dtype || 'fp32'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Configure transformers.js environment
|
|
|
|
|
if (!isBrowser()) {
|
|
|
|
|
// Set cache directory for Node.js
|
|
|
|
|
env.cacheDir = this.options.cacheDir
|
|
|
|
|
// Prioritize local models for offline operation
|
|
|
|
|
env.allowRemoteModels = !this.options.localFilesOnly
|
|
|
|
|
env.allowLocalModels = true
|
2025-08-05 19:38:26 -07:00
|
|
|
} else {
|
|
|
|
|
// Browser configuration
|
|
|
|
|
// Allow both local and remote models, but prefer local if available
|
|
|
|
|
env.allowLocalModels = true
|
|
|
|
|
env.allowRemoteModels = true
|
|
|
|
|
// Force the configuration to ensure it's applied
|
|
|
|
|
if (this.verbose) {
|
|
|
|
|
this.logger('log', `Browser env config - allowLocalModels: ${env.allowLocalModels}, allowRemoteModels: ${env.allowRemoteModels}, localFilesOnly: ${this.options.localFilesOnly}`)
|
|
|
|
|
}
|
2025-08-05 19:29:59 -07:00
|
|
|
}
|
2025-07-18 10:40:37 -07:00
|
|
|
}
|
2025-06-24 11:41:30 -07:00
|
|
|
|
2025-07-02 16:16:19 -07:00
|
|
|
/**
|
2025-08-05 19:29:59 -07:00
|
|
|
* Get the default cache directory for models
|
2025-07-02 16:16:19 -07:00
|
|
|
*/
|
2025-08-05 19:29:59 -07:00
|
|
|
private getDefaultCacheDir(): string {
|
2025-07-28 10:04:45 -07:00
|
|
|
if (isBrowser()) {
|
2025-08-05 19:29:59 -07:00
|
|
|
return './models' // Browser default
|
2025-07-02 16:16:19 -07:00
|
|
|
}
|
|
|
|
|
|
2025-08-05 19:29:59 -07:00
|
|
|
// Check for bundled models in the package
|
|
|
|
|
const possiblePaths = [
|
|
|
|
|
// In the installed package
|
|
|
|
|
'./node_modules/@soulcraft/brainy/models',
|
|
|
|
|
// In development/source
|
|
|
|
|
'./models',
|
|
|
|
|
'./dist/../models',
|
|
|
|
|
// Alternative locations
|
|
|
|
|
'../models',
|
|
|
|
|
'../../models'
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
// Check if we're in Node.js and try to find the bundled models
|
|
|
|
|
if (typeof process !== 'undefined' && process.versions?.node) {
|
|
|
|
|
try {
|
|
|
|
|
const path = require('path')
|
|
|
|
|
const fs = require('fs')
|
2025-07-16 13:51:00 -07:00
|
|
|
|
2025-08-05 19:29:59 -07:00
|
|
|
// Try to resolve the package location
|
|
|
|
|
try {
|
|
|
|
|
const brainyPackagePath = require.resolve('@soulcraft/brainy/package.json')
|
|
|
|
|
const brainyPackageDir = path.dirname(brainyPackagePath)
|
|
|
|
|
const bundledModelsPath = path.join(brainyPackageDir, 'models')
|
|
|
|
|
|
|
|
|
|
if (fs.existsSync(bundledModelsPath)) {
|
|
|
|
|
this.logger('log', `Using bundled models from package: ${bundledModelsPath}`)
|
|
|
|
|
return bundledModelsPath
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
// Not installed as package, continue
|
2025-07-14 11:12:51 -07:00
|
|
|
}
|
2025-07-11 11:11:56 -07:00
|
|
|
|
2025-08-05 19:29:59 -07:00
|
|
|
// Try relative paths from current location
|
|
|
|
|
for (const relativePath of possiblePaths) {
|
|
|
|
|
const fullPath = path.resolve(relativePath)
|
|
|
|
|
if (fs.existsSync(fullPath)) {
|
|
|
|
|
this.logger('log', `Using bundled models from: ${fullPath}`)
|
|
|
|
|
return fullPath
|
|
|
|
|
}
|
2025-07-14 11:12:51 -07:00
|
|
|
}
|
2025-08-05 19:29:59 -07:00
|
|
|
} catch (error) {
|
|
|
|
|
this.logger('warn', 'Could not auto-detect bundled models directory:', error)
|
2025-07-16 13:51:00 -07:00
|
|
|
}
|
|
|
|
|
}
|
2025-08-05 19:29:59 -07:00
|
|
|
|
|
|
|
|
// Fallback to default cache directory
|
|
|
|
|
return './models'
|
2025-07-16 13:51:00 -07:00
|
|
|
}
|
2025-07-11 11:11:56 -07:00
|
|
|
|
2025-07-16 13:51:00 -07:00
|
|
|
/**
|
|
|
|
|
* Check if we're running in a test environment
|
|
|
|
|
*/
|
|
|
|
|
private isTestEnvironment(): boolean {
|
2025-08-05 19:29:59 -07:00
|
|
|
// Always use real implementation - no more mocking
|
|
|
|
|
return false
|
2025-07-16 13:51:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-08-05 19:29:59 -07:00
|
|
|
* Log message only if verbose mode is enabled
|
2025-07-16 13:51:00 -07:00
|
|
|
*/
|
2025-08-05 19:29:59 -07:00
|
|
|
private logger(level: 'log' | 'warn' | 'error', message: string, ...args: any[]): void {
|
2025-07-18 10:40:37 -07:00
|
|
|
if (level === 'error' || this.verbose) {
|
2025-08-05 19:29:59 -07:00
|
|
|
console[level](`[TransformerEmbedding] ${message}`, ...args)
|
2025-07-11 11:11:56 -07:00
|
|
|
}
|
2025-07-02 16:16:19 -07:00
|
|
|
}
|
|
|
|
|
|
2025-06-24 11:41:30 -07:00
|
|
|
/**
|
|
|
|
|
* Initialize the embedding model
|
|
|
|
|
*/
|
|
|
|
|
public async init(): Promise<void> {
|
2025-08-05 19:29:59 -07:00
|
|
|
if (this.initialized) {
|
2025-08-01 18:31:37 -07:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-05 19:29:59 -07:00
|
|
|
// Always use real implementation - no mocking
|
2025-07-14 11:12:51 -07:00
|
|
|
|
2025-08-05 19:29:59 -07:00
|
|
|
try {
|
|
|
|
|
this.logger('log', `Loading Transformer model: ${this.options.model}`)
|
|
|
|
|
|
|
|
|
|
const startTime = Date.now()
|
|
|
|
|
|
|
|
|
|
// Load the feature extraction pipeline
|
2025-08-05 19:38:26 -07:00
|
|
|
// In browsers, never use local_files_only to avoid conflicts
|
|
|
|
|
const pipelineOptions = {
|
2025-08-05 19:29:59 -07:00
|
|
|
cache_dir: this.options.cacheDir,
|
2025-08-05 19:38:26 -07:00
|
|
|
local_files_only: isBrowser() ? false : this.options.localFilesOnly,
|
2025-08-05 19:29:59 -07:00
|
|
|
dtype: this.options.dtype
|
2025-08-05 19:38:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.verbose) {
|
|
|
|
|
this.logger('log', `Pipeline options: ${JSON.stringify(pipelineOptions)}`)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.extractor = await pipeline('feature-extraction', this.options.model, pipelineOptions)
|
2025-06-24 11:41:30 -07:00
|
|
|
|
2025-08-05 19:29:59 -07:00
|
|
|
const loadTime = Date.now() - startTime
|
|
|
|
|
this.logger('log', `✅ Model loaded successfully in ${loadTime}ms`)
|
|
|
|
|
|
|
|
|
|
this.initialized = true
|
2025-06-24 11:41:30 -07:00
|
|
|
} catch (error) {
|
2025-08-05 19:29:59 -07:00
|
|
|
this.logger('error', 'Failed to initialize Transformer embedding model:', error)
|
|
|
|
|
throw new Error(`Transformer embedding initialization failed: ${error}`)
|
2025-06-24 11:41:30 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-08-05 19:29:59 -07:00
|
|
|
* Generate embeddings for text data
|
2025-06-24 11:41:30 -07:00
|
|
|
*/
|
|
|
|
|
public async embed(data: string | string[]): Promise<Vector> {
|
|
|
|
|
if (!this.initialized) {
|
|
|
|
|
await this.init()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Handle different input types
|
|
|
|
|
let textToEmbed: string[]
|
2025-08-05 19:29:59 -07:00
|
|
|
|
2025-06-24 11:41:30 -07:00
|
|
|
if (typeof data === 'string') {
|
|
|
|
|
// Handle empty string case
|
|
|
|
|
if (data.trim() === '') {
|
2025-08-05 19:29:59 -07:00
|
|
|
// Return a zero vector of 384 dimensions (all-MiniLM-L6-v2 standard)
|
|
|
|
|
return new Array(384).fill(0)
|
2025-06-24 11:41:30 -07:00
|
|
|
}
|
|
|
|
|
textToEmbed = [data]
|
2025-08-05 19:29:59 -07:00
|
|
|
} else if (Array.isArray(data) && data.every((item) => typeof item === 'string')) {
|
2025-06-24 11:41:30 -07:00
|
|
|
// Handle empty array or array with empty strings
|
|
|
|
|
if (data.length === 0 || data.every((item) => item.trim() === '')) {
|
2025-08-05 19:29:59 -07:00
|
|
|
return new Array(384).fill(0)
|
2025-06-24 11:41:30 -07:00
|
|
|
}
|
|
|
|
|
// Filter out empty strings
|
|
|
|
|
textToEmbed = data.filter((item) => item.trim() !== '')
|
|
|
|
|
if (textToEmbed.length === 0) {
|
2025-08-05 19:29:59 -07:00
|
|
|
return new Array(384).fill(0)
|
2025-06-24 11:41:30 -07:00
|
|
|
}
|
|
|
|
|
} else {
|
2025-08-05 19:29:59 -07:00
|
|
|
throw new Error('TransformerEmbedding only supports string or string[] data')
|
2025-07-28 16:00:05 -07:00
|
|
|
}
|
|
|
|
|
|
2025-08-05 19:29:59 -07:00
|
|
|
// Ensure the extractor is available
|
|
|
|
|
if (!this.extractor) {
|
|
|
|
|
throw new Error('Transformer embedding model is not available')
|
2025-06-27 14:06:59 -07:00
|
|
|
}
|
|
|
|
|
|
2025-08-05 19:29:59 -07:00
|
|
|
// Generate embeddings with mean pooling and normalization
|
|
|
|
|
const result = await this.extractor(textToEmbed, {
|
|
|
|
|
pooling: 'mean',
|
|
|
|
|
normalize: true
|
2025-07-28 16:00:05 -07:00
|
|
|
})
|
|
|
|
|
|
2025-08-05 19:29:59 -07:00
|
|
|
// Extract the embedding data
|
|
|
|
|
let embedding: number[]
|
|
|
|
|
|
|
|
|
|
if (textToEmbed.length === 1) {
|
|
|
|
|
// Single text input - return first embedding
|
|
|
|
|
embedding = Array.from(result.data.slice(0, 384))
|
|
|
|
|
} else {
|
|
|
|
|
// Multiple texts - return first embedding (maintain compatibility)
|
|
|
|
|
embedding = Array.from(result.data.slice(0, 384))
|
|
|
|
|
}
|
2025-06-27 14:06:59 -07:00
|
|
|
|
2025-08-05 19:29:59 -07:00
|
|
|
// Validate embedding dimensions
|
|
|
|
|
if (embedding.length !== 384) {
|
|
|
|
|
this.logger('warn', `Unexpected embedding dimension: ${embedding.length}, expected 384`)
|
|
|
|
|
// Pad or truncate to 384 dimensions
|
|
|
|
|
if (embedding.length < 384) {
|
|
|
|
|
embedding = [...embedding, ...new Array(384 - embedding.length).fill(0)]
|
2025-06-27 14:06:59 -07:00
|
|
|
} else {
|
2025-08-05 19:29:59 -07:00
|
|
|
embedding = embedding.slice(0, 384)
|
2025-06-27 14:06:59 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-05 19:29:59 -07:00
|
|
|
return embedding
|
2025-06-27 14:06:59 -07:00
|
|
|
} catch (error) {
|
2025-08-05 19:29:59 -07:00
|
|
|
this.logger('error', 'Error generating embeddings:', error)
|
|
|
|
|
throw new Error(`Failed to generate embeddings: ${error}`)
|
2025-06-27 14:06:59 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-24 11:41:30 -07:00
|
|
|
/**
|
2025-08-05 19:29:59 -07:00
|
|
|
* Dispose of the model and free resources
|
2025-06-24 11:41:30 -07:00
|
|
|
*/
|
|
|
|
|
public async dispose(): Promise<void> {
|
2025-08-05 19:29:59 -07:00
|
|
|
if (this.extractor && typeof this.extractor.dispose === 'function') {
|
|
|
|
|
await this.extractor.dispose()
|
2025-06-24 11:41:30 -07:00
|
|
|
}
|
2025-08-05 19:29:59 -07:00
|
|
|
this.extractor = null
|
|
|
|
|
this.initialized = false
|
2025-06-24 11:41:30 -07:00
|
|
|
}
|
|
|
|
|
|
2025-08-05 19:29:59 -07:00
|
|
|
/**
|
|
|
|
|
* Get the dimension of embeddings produced by this model
|
|
|
|
|
*/
|
|
|
|
|
public getDimension(): number {
|
|
|
|
|
return 384
|
2025-06-27 14:06:59 -07:00
|
|
|
}
|
|
|
|
|
|
2025-08-05 19:29:59 -07:00
|
|
|
/**
|
|
|
|
|
* Check if the model is initialized
|
|
|
|
|
*/
|
|
|
|
|
public isInitialized(): boolean {
|
|
|
|
|
return this.initialized
|
2025-07-28 16:00:05 -07:00
|
|
|
}
|
2025-06-27 14:06:59 -07:00
|
|
|
}
|
|
|
|
|
|
2025-08-05 19:29:59 -07:00
|
|
|
// Legacy alias for backward compatibility
|
|
|
|
|
export const UniversalSentenceEncoder = TransformerEmbedding
|
2025-07-16 13:51:00 -07:00
|
|
|
|
|
|
|
|
/**
|
2025-08-05 19:29:59 -07:00
|
|
|
* Create a new embedding model instance
|
2025-07-16 13:51:00 -07:00
|
|
|
*/
|
2025-08-05 19:29:59 -07:00
|
|
|
export function createEmbeddingModel(options?: TransformerEmbeddingOptions): EmbeddingModel {
|
|
|
|
|
return new TransformerEmbedding(options)
|
2025-07-16 13:51:00 -07:00
|
|
|
}
|
|
|
|
|
|
2025-06-24 11:41:30 -07:00
|
|
|
/**
|
2025-08-05 19:29:59 -07:00
|
|
|
* Default embedding function using the lightweight transformer model
|
2025-06-24 11:41:30 -07:00
|
|
|
*/
|
2025-08-05 19:29:59 -07:00
|
|
|
export const defaultEmbeddingFunction: EmbeddingFunction = async (data: string | string[]): Promise<Vector> => {
|
|
|
|
|
const embedder = new TransformerEmbedding({ verbose: false })
|
|
|
|
|
return await embedder.embed(data)
|
2025-06-24 11:41:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-08-05 19:29:59 -07:00
|
|
|
* Create an embedding function with custom options
|
2025-06-24 11:41:30 -07:00
|
|
|
*/
|
2025-08-05 19:29:59 -07:00
|
|
|
export function createEmbeddingFunction(options: TransformerEmbeddingOptions = {}): EmbeddingFunction {
|
|
|
|
|
const embedder = new TransformerEmbedding(options)
|
|
|
|
|
|
|
|
|
|
return async (data: string | string[]): Promise<Vector> => {
|
|
|
|
|
return await embedder.embed(data)
|
2025-07-18 10:40:37 -07:00
|
|
|
}
|
|
|
|
|
}
|
2025-06-24 11:41:30 -07:00
|
|
|
|
2025-06-27 14:06:59 -07:00
|
|
|
/**
|
2025-08-05 19:29:59 -07:00
|
|
|
* Batch embedding function for processing multiple texts efficiently
|
2025-07-18 10:40:37 -07:00
|
|
|
*/
|
2025-08-05 19:29:59 -07:00
|
|
|
export async function batchEmbed(
|
|
|
|
|
texts: string[],
|
|
|
|
|
options: TransformerEmbeddingOptions = {}
|
|
|
|
|
): Promise<Vector[]> {
|
|
|
|
|
const embedder = new TransformerEmbedding(options)
|
|
|
|
|
await embedder.init()
|
|
|
|
|
|
|
|
|
|
const embeddings: Vector[] = []
|
|
|
|
|
|
|
|
|
|
// Process in batches for memory efficiency
|
|
|
|
|
const batchSize = 32
|
|
|
|
|
for (let i = 0; i < texts.length; i += batchSize) {
|
|
|
|
|
const batch = texts.slice(i, i + batchSize)
|
|
|
|
|
|
|
|
|
|
for (const text of batch) {
|
|
|
|
|
const embedding = await embedder.embed(text)
|
|
|
|
|
embeddings.push(embedding)
|
2025-07-18 10:40:37 -07:00
|
|
|
}
|
2025-06-24 11:41:30 -07:00
|
|
|
}
|
2025-08-05 19:29:59 -07:00
|
|
|
|
|
|
|
|
await embedder.dispose()
|
|
|
|
|
return embeddings
|
2025-06-24 11:41:30 -07:00
|
|
|
}
|
|
|
|
|
|
2025-07-18 10:40:37 -07:00
|
|
|
/**
|
2025-08-05 19:29:59 -07:00
|
|
|
* Embedding functions for specific model types
|
2025-06-24 11:41:30 -07:00
|
|
|
*/
|
2025-08-05 19:29:59 -07:00
|
|
|
export const embeddingFunctions = {
|
|
|
|
|
/** Default lightweight model (all-MiniLM-L6-v2, 384 dimensions) */
|
|
|
|
|
default: defaultEmbeddingFunction,
|
|
|
|
|
|
|
|
|
|
/** Create custom embedding function */
|
|
|
|
|
create: createEmbeddingFunction,
|
|
|
|
|
|
|
|
|
|
/** Batch processing */
|
|
|
|
|
batch: batchEmbed
|
|
|
|
|
}
|