mnemos-coder
Version:
CLI-based coding agent with graph-based execution loop and terminal UI
175 lines • 5.8 kB
JavaScript
/**
* API-based embedding service for code semantic search
* Uses external embedding models via REST API
*/
export class ApiEmbedder {
config;
baseURL;
headers;
constructor(config) {
this.config = config;
this.baseURL = config.base_url.endsWith('/') ? config.base_url : config.base_url + '/';
this.headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${config.api_key}`
};
}
/**
* Generate embedding for a single text
*/
async embed(text) {
const startTime = Date.now();
try {
const response = await this.makeRequest({
input: text,
model: this.config.model,
encoding_format: 'float'
});
if (!response.data || response.data.length === 0) {
throw new Error('No embedding data received');
}
const embedding = response.data[0].embedding;
return {
embedding,
dimension: embedding.length,
processingTime: Date.now() - startTime,
model: response.model
};
}
catch (error) {
throw new Error(`Embedding API error: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Generate embeddings for multiple texts (batch processing)
*/
async embedBatch(texts) {
const startTime = Date.now();
if (texts.length === 0) {
return {
embeddings: [],
dimension: this.config.dimension,
processingTime: 0,
model: this.config.model
};
}
try {
// Process in batches to avoid API limits
const batchSize = this.config.batch_size || 32;
const batches = [];
for (let i = 0; i < texts.length; i += batchSize) {
batches.push(texts.slice(i, i + batchSize));
}
const allEmbeddings = [];
let totalTokens = 0;
let actualDimension = this.config.dimension;
for (const batch of batches) {
const response = await this.makeRequest({
input: batch,
model: this.config.model,
encoding_format: 'float'
});
// Extract embeddings and maintain order
const batchEmbeddings = response.data
.sort((a, b) => a.index - b.index)
.map(item => item.embedding);
allEmbeddings.push(...batchEmbeddings);
if (response.usage) {
totalTokens += response.usage.total_tokens;
}
if (batchEmbeddings.length > 0) {
actualDimension = batchEmbeddings[0].length;
}
// Add small delay between batches to avoid rate limiting
if (batches.length > 1) {
await new Promise(resolve => setTimeout(resolve, 100));
}
}
return {
embeddings: allEmbeddings,
dimension: actualDimension,
processingTime: Date.now() - startTime,
model: this.config.model,
total_tokens: totalTokens
};
}
catch (error) {
throw new Error(`Batch embedding API error: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Calculate cosine similarity between two embeddings
*/
calculateSimilarity(embedding1, embedding2) {
if (embedding1.length !== embedding2.length) {
throw new Error('Embedding dimensions must match');
}
let dotProduct = 0;
let norm1 = 0;
let norm2 = 0;
for (let i = 0; i < embedding1.length; i++) {
dotProduct += embedding1[i] * embedding2[i];
norm1 += embedding1[i] * embedding1[i];
norm2 += embedding2[i] * embedding2[i];
}
if (norm1 === 0 || norm2 === 0) {
return 0;
}
return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
}
/**
* Get embedding configuration
*/
getConfig() {
return { ...this.config };
}
/**
* Test API connection
*/
async testConnection() {
const startTime = Date.now();
try {
await this.embed('test connection');
const latency = Date.now() - startTime;
return {
success: true,
message: 'Connection successful',
latency
};
}
catch (error) {
return {
success: false,
message: error instanceof Error ? error.message : String(error)
};
}
}
/**
* Get model information
*/
getModelInfo() {
return {
name: this.config.name,
model: this.config.model,
dimension: this.config.dimension,
baseURL: this.config.base_url
};
}
/**
* Make HTTP request to embedding API
*/
async makeRequest(payload) {
const url = new URL('embeddings', this.baseURL).toString();
const response = await fetch(url, {
method: 'POST',
headers: this.headers,
body: JSON.stringify(payload)
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API request failed (${response.status}): ${errorText}`);
}
return response.json();
}
}
//# sourceMappingURL=ApiEmbedder.js.map