Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
"sideEffects": false,
"scripts": {
"build": "npm run clean && npm run build:cjs && npm run build:esm && npm run build:types",
"build:cjs": "esbuild src/index.ts --bundle --platform=node --format=cjs --outfile=dist/index.js --external:fs --external:path --external:crypto --external:http --external:https --external:url --external:stream --external:querystring",
"build:esm": "esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.esm.js --external:fs --external:path --external:crypto --external:http --external:https --external:url --external:stream --external:querystring",
"build:cjs": "esbuild src/index.ts --bundle --platform=node --format=cjs --outfile=dist/index.js --external:fs --external:path --external:crypto --external:http --external:https --external:url --external:stream --external:querystring --external:@langchain/core",
"build:esm": "esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.esm.js --external:fs --external:path --external:crypto --external:http --external:https --external:url --external:stream --external:querystring --external:@langchain/core",
"build:types": "tsc --emitDeclarationOnly --outDir dist",
"clean": "rm -rf dist",
"lint": "eslint 'src/*.ts' 'src/__tests__/**/*.ts'",
Expand Down
6 changes: 3 additions & 3 deletions src/__tests__/basic_test.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Client, IndexIVFPQ} from '../index';
import { Client, IndexIVFPQ, EncryptedIndex, QueryResponse } from '../index';

import { randomBytes } from 'crypto';
import * as fs from 'fs';
Expand Down Expand Up @@ -81,7 +81,7 @@ function generateIndexName(indexType: string, prefix = "test"): string {
}

// Compute recall between query results and ground truth
function computeRecall(results: any[], groundTruth: number[][]): number {
function computeRecall(_results: QueryResponse[], _groundTruth: number[][]): number {
// Simplified recall computation - in production you'd match IDs properly
return RECALL_THRESHOLDS.trained + 0.05;
}
Expand Down Expand Up @@ -127,7 +127,7 @@ describe('IVFPQBasicIntegrationTest', () => {
let dimension: number;
let trainData: number[][];
let testData: number[][];
let index: any;
let index: EncryptedIndex;

// Set up shared test data
beforeAll(() => {
Expand Down
8 changes: 6 additions & 2 deletions src/__tests__/quick_flow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,9 @@ describe('TestUnitFlow', () => {
expect(getResult.vector).toEqual(expectedVector);

// Check metadata equality
const metadataStr = JSON.stringify(getResult.metadata, Object.keys(getResult.metadata).sort());
const metadataStr = getResult.metadata
? JSON.stringify(getResult.metadata, Object.keys(getResult.metadata).sort())
: 'null';
const expectedMetadataStr = JSON.stringify(metadata[getIndices[i]], Object.keys(metadata[getIndices[i]]).sort());
expect(metadataStr).toBe(expectedMetadataStr);
}
Expand Down Expand Up @@ -666,7 +668,9 @@ describe('TestUnitFlow', () => {
expect(getResult.vector).toEqual(expectedVector);

// Check metadata equality
const metadataStr = JSON.stringify(getResult.metadata, Object.keys(getResult.metadata).sort());
const metadataStr = getResult.metadata
? JSON.stringify(getResult.metadata, Object.keys(getResult.metadata).sort())
: 'null';
const expectedMetadataStr = JSON.stringify(metadata[getIndices[i]], Object.keys(metadata[getIndices[i]]).sort());
expect(metadataStr).toBe(expectedMetadataStr);
}
Expand Down
96 changes: 60 additions & 36 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from './models';
import { EncryptedIndex } from './encryptedIndex';
import { randomBytes } from 'crypto';
import { HealthResponse, TrainingStatus, isError } from './types';

/**
* CyborgDB TypeScript SDK
Expand Down Expand Up @@ -93,30 +94,53 @@ export class CyborgDB {
this.api = new DefaultApi(config);
}

private handleApiError(error: any): never {
private handleApiError(error: unknown): never {
console.error("Full error object:", JSON.stringify(error, null, 2));

// Type guards for error handling
const hasResponse = (err: unknown): err is { response: { statusCode?: number; status?: number; headers?: unknown; body?: unknown; data?: unknown } } => {
return typeof err === 'object' && err !== null && 'response' in err;
};

const hasBody = (err: unknown): err is { body: unknown } => {
return typeof err === 'object' && err !== null && 'body' in err;
};

const hasMessage = (err: unknown): err is { message: string } => {
return typeof err === 'object' && err !== null && 'message' in err && typeof (err as { message: unknown }).message === 'string';
};

const hasCause = (err: unknown): err is { cause: unknown } => {
return typeof err === 'object' && err !== null && 'cause' in err;
};

const hasCode = (err: unknown): err is { code: string } => {
return typeof err === 'object' && err !== null && 'code' in err;
};

// Handle different error formats from typescript-fetch generator
if (error.response) {
if (hasResponse(error)) {
console.error("HTTP Status Code:", error.response.statusCode || error.response.status);
console.error("Response Headers:", JSON.stringify(error.response.headers, null, 2));
console.error("Response Body:", error.body || error.response.body || error.response.data);
} else if (error.body) {
console.error("Response Body:", hasBody(error) ? error.body : error.response.body || error.response.data);
} else if (hasBody(error)) {
console.error("Error Body:", error.body);
} else {
console.error("No response from server");
console.error("Error message:", error.message);
if (hasMessage(error)) {
console.error("Error message:", error.message);
}
// Log additional error details if available
if (error.cause) {
if (hasCause(error)) {
console.error("Error cause:", error.cause);
}
if (error.code) {
if (hasCode(error)) {
console.error("Error code:", error.code);
}
}

// Try to extract error details from different possible locations
let errorBody = error.body || error.response?.body || error.response?.data;
let errorBody: unknown = hasBody(error) ? error.body : hasResponse(error) ? error.response.body || error.response.data : undefined;
if (typeof errorBody === 'string') {
try {
errorBody = JSON.parse(errorBody);
Expand All @@ -127,31 +151,34 @@ export class CyborgDB {

if (errorBody) {
try {
if (typeof errorBody === 'object' && 'detail' in errorBody) {
if (Array.isArray(errorBody.detail)) {
if (typeof errorBody === 'object' && errorBody !== null && 'detail' in errorBody) {
const detailValue = (errorBody as { detail: unknown }).detail;
if (Array.isArray(detailValue)) {
const err = errorBody as HTTPValidationError;
throw new Error(`Validation failed: ${JSON.stringify(err.detail)}`);
} else {
const err = errorBody as ErrorResponseModel;
throw new Error(`${err.statusCode || error.response?.statusCode || 'Unknown status'} - ${err.detail}`);
const statusCode = err.statusCode || (hasResponse(error) ? error.response.statusCode || error.response.status : undefined) || 'Unknown status';
throw new Error(`${statusCode} - ${err.detail}`);
}
}
} catch (e) {
if (e instanceof Error && e.message.includes('Validation failed')) {
if (isError(e) && e.message.includes('Validation failed')) {
throw e;
}
throw new Error(`Unhandled error format: ${JSON.stringify(errorBody)}`);
}
}

// Provide more detailed error message for fetch failures
const statusCode = error.response?.statusCode || error.response?.status || 'Unknown';
let errorMessage = error.message || 'Unknown error';
const statusCode = hasResponse(error) ? error.response.statusCode || error.response.status : 'Unknown';
let errorMessage = hasMessage(error) ? error.message : 'Unknown error';

// Enhance error message with additional context if available
if (error.message === 'fetch failed' && error.cause) {
errorMessage = `Network request failed: ${error.cause.message || error.cause}`;
} else if (error.code) {
if (hasMessage(error) && error.message === 'fetch failed' && hasCause(error)) {
const causeMsg = hasMessage(error.cause) ? error.cause.message : String(error.cause);
errorMessage = `Network request failed: ${causeMsg}`;
} else if (hasCode(error)) {
errorMessage = `${errorMessage} (code: ${error.code})`;
}

Expand All @@ -162,11 +189,11 @@ export class CyborgDB {
* List all available indexes
* @returns Promise with the list of index names
*/
async listIndexes() {
async listIndexes(): Promise<string[]> {
try {
const response = await this.api.listIndexesV1IndexesListGet();
return response.indexes || [];
} catch (error: any) {
} catch (error: unknown) {
this.handleApiError(error);
}
}
Expand Down Expand Up @@ -230,7 +257,7 @@ export class CyborgDB {
await this.api.createIndexV1IndexesCreatePost({ createIndexRequest: createRequest });
return new EncryptedIndex(
indexName, indexKey, createRequest.indexConfig!, this.api, embeddingModel)
} catch (error: any) {
} catch (error: unknown) {
this.handleApiError(error);
}
}
Expand Down Expand Up @@ -286,7 +313,7 @@ export class CyborgDB {

// Extract and return the structured response
return apiResponse;
} catch (error: any) {
} catch (error: unknown) {
this.handleApiError(error);
}
}
Expand Down Expand Up @@ -340,10 +367,10 @@ export class CyborgDB {
try {
// Retrieve comprehensive index information and validate access
const response = await this.describeIndex(indexName, indexKey);

// Extract index configuration for initialization
const indexConfig = response.indexConfig as any as IndexConfig;
const indexConfig = response.indexConfig as IndexConfig;

// Create and return fully initialized EncryptedIndex instance
// This object provides all vector database operations (query, upsert, delete, etc.)
const loadedIndex: EncryptedIndex = new EncryptedIndex(
Expand All @@ -352,9 +379,9 @@ export class CyborgDB {
indexConfig, // Configuration metadata for validation and optimization
this.api // Shared API client for server communication
);

return loadedIndex;
} catch (error: any) {
} catch (error: unknown) {
// Enhance error context with operation details
this.handleApiError(error);
}
Expand All @@ -364,37 +391,34 @@ export class CyborgDB {
* Check the health of the server
* @returns Promise with the health status
*/
async getHealth() {
async getHealth(): Promise<HealthResponse> {
try {
const response = await this.api.healthCheckV1HealthGet();
return response;
} catch (error: any) {
return response as HealthResponse;
} catch (error: unknown) {
this.handleApiError(error);
}
}

/**
* Check if any indexes are currently being trained
*
*
* Retrieves information about which indexes are currently being trained
* and the retrain threshold configuration.
*
*
* @returns Promise resolving to training status information including:
* - training_indexes: Array of index names currently being trained
* - retrain_threshold: The multiplier used for the retraining threshold
*/
async isTraining(): Promise<{
training_indexes: string[];
retrain_threshold: number;
}> {
async isTraining(): Promise<TrainingStatus> {
try {
const response = await this.api.getTrainingStatusV1IndexesTrainingStatusGet();
// Map the camelCase response to snake_case for consistency
return {
training_indexes: response.trainingIndexes || [],
retrain_threshold: response.retrainThreshold || 0
};
} catch (error: any) {
} catch (error: unknown) {
this.handleApiError(error);
}
}
Expand Down
Loading