Node.js SDK
Official TypeScript/JavaScript SDK for VectorAmp. Dual ESM/CJS, TypeScript-first, with a REST transport today and a transport interface ready for gRPC later.
Install
npm install @vectoramp/vectoramp
From a checkout for local development:
git clone https://github.com/VectorAmp/vectoramp-node.git
cd vectoramp-node
npm ci
npm run build
Configure
import { VectorAmp } from '@vectoramp/vectoramp';
// Reads VECTORAMP_API_KEY from the environment when apiKey is omitted.
const client = new VectorAmp();
// Or configure explicitly.
const configured = new VectorAmp({
apiKey: 'vsk_...',
baseUrl: 'https://api.vectoramp.com' // default; override for dedicated gateways
});
Auth uses the X-API-Key header. Paths are unprefixed against the public gateway (the apiPrefix option defaults to '').
Quickstart
A minimal init, a one-call create, then object → method calls. Only a name is required: the embedding defaults to VectorAmp-Embedding-4B, the dimension is inferred (2560), the metric defaults to cosine, and the index is always SABLE.
import { VectorAmp } from '@vectoramp/vectoramp';
const client = new VectorAmp();
// One call creates a SABLE dataset using the default VectorAmp embedding model.
const dataset = await client.datasets.create({ name: 'product-docs' });
// Object -> method: operate on the returned dataset directly.
await dataset.addTexts([
'VectorAmp stores and searches vectors at scale.',
{ text: "SABLE is VectorAmp's index architecture.", metadata: { source: 'whitepaper' } }
]);
const results = await dataset.search('How does VectorAmp search documents?');
const answer = await dataset.ask('What index does VectorAmp use?');
console.log(results.results);
console.log(answer.answer);
Dataset creation always requests SABLE. The SDK intentionally does not expose an index_type option.
Creating datasets
import { openai } from '@vectoramp/vectoramp';
// Minimal: only a name is required.
const docs = await client.datasets.create({ name: 'docs' });
// Custom embeddings infer the dimension automatically for built-in models.
const openaiDataset = await client.datasets.create({ name: 'docs', embedding: openai('large') });
// Custom/unknown models require an explicit dim.
const customDataset = await client.datasets.create({
name: 'docs',
embedding: { provider: 'acme', model: 'acme-embed-1' },
dim: 1024
});
// Hybrid (dense + sparse) datasets:
const hybrid = await client.datasets.create({ name: 'docs', hybrid: true });
Built-in dimension inference: vectoramp/VectorAmp-Embedding-4B → 2560, openai/text-embedding-3-small → 1536, openai/text-embedding-3-large → 3072.
Object and service styles
Both access styles work everywhere. Prefer the object → method style (dataset.search(...)) over the service style (client.datasets.search(id, ...)).
// Object -> method style (preferred)
const page = await client.datasets.list({ limit: 20, offset: 0 });
const dataset = await client.datasets.get(page.data[0].id);
await dataset.search('release notes');
await dataset.delete();
// Service style
await client.datasets.search('dataset_id', { queryText: 'release notes', topK: 10 });
await client.datasets.delete('dataset_id');
List calls return pagination envelopes with data, total, and nextOffset.
Insert, add text, and search
Vector ids accept a string or an integer; integers are serialized as JSON numbers and preserved by the API.
await dataset.insert([
{ id: 1, values: [0.12, 0.98, 0.42], metadata: { title: 'Intro' } }, // numeric id stays numeric
{ id: 'vec_2', values: [0.22, 0.18, 0.62] }
]);
addTexts embeds each text with the dataset's model, copies the source text into metadata.text, generates ids when omitted, then inserts.
await dataset.addTexts('Plain text chunk');
await dataset.addTexts([
'Another plain text chunk',
{ id: 'doc_2', text: 'Text with metadata', metadata: { url: 'https://example.com' } }
]);
search accepts a bare string (text query) or a float vector; topK defaults to 10.
await dataset.search('semantic query'); // text query
await dataset.search([0.1, 0.2, 0.3]); // vector query
await dataset.search({
queryText: 'noise cancelling headphones',
topK: 10,
includeMetadata: true,
hybrid: true,
alpha: 0.5,
rerank: true // expands to vectoramp / VectorAmp-Rerank-v1
});
rerank: true (or rerank: { enabled: true }) enables the optional model reranker after initial retrieval. topN, maxDocuments, and failOpen are optional and use server defaults. The separate rerankDepthOverride only tunes SABLE's vector-index candidate depth and is not the model reranker.
Organization secrets and vector deletion
Store external provider credentials as organization secrets, then reference the secret from datasets. The default OpenAI BYOM reference is emb:openai:api_key.
// One-step convenience: saves/updates the OpenAI org secret, then creates
// the dataset with embedding.secret_ref = "emb:openai:api_key".
const dataset = await client.datasets.create({
name: 'openai-docs',
openaiApiKey: process.env.OPENAI_API_KEY
});
// Or manage the secret explicitly.
await client.orgSecrets.putOpenAIApiKey(process.env.OPENAI_API_KEY!);
// Delete individual vector ids from search.
await dataset.deleteVectors(['doc-001', 42], { writeConcern: 'quorum' });
await client.datasets.deleteVectors(dataset.id, ['doc-002']);
Ingestion
Source builders keep payloads typed while matching the REST source_type contract (web, s3, gcs, gdrive, file_upload, jira, confluence).
import { webSource, s3Source, confluenceSource } from '@vectoramp/vectoramp';
// One-liner: the SDK creates the source, then starts the ingestion job.
await dataset.ingestSource(webSource('https://docs.example.com'));
await dataset.ingestSource(s3Source('s3://my-bucket/docs/'));
await dataset.ingestSource(confluenceSource({ baseUrl: 'https://acme.atlassian.net', username: 'me', config: { apiToken: '…' } }));
// Existing source ids start a job directly.
await dataset.ingestSource('source_id');
// Optional pipeline override.
await dataset.ingestSource(webSource('https://docs.example.com'), { pipelineId: 'pl_1' });
Create reusable sources through client.sources (an alias of client.ingestion):
const web = await client.sources.createWeb('https://docs.example.com');
const s3 = await client.sources.createS3('s3://my-bucket/docs/');
const drive = await client.sources.createGoogleDrive({ folderId: 'google-drive-folder-id' });
const confluence = await client.sources.createConfluence({ cloudId: 'cloud-id', accessToken: 'token', spaceKeys: ['DOCS'] });
await dataset.ingestSource(web.id!);
Use genericSource(sourceType, payload) for source types not yet modeled by the SDK.
Manage and clean up sources
Validate a config, delete a source, or find and remove sources nothing is using.
// Validate without persisting.
await client.sources.validate('web', { start_urls: ['https://docs.example.com'] });
// See what's using a source (active schedules + in-flight jobs).
const refs = await client.sources.getReferences('source_id'); // { schedules, scheduleCount, activeJobCount, inUse }
// Delete. Throws 409 if in use unless force: true (force also disables its schedules).
await client.sources.delete('source_id');
await client.sources.delete('source_id', { force: true });
// Find unused sources, or remove all of them in one call.
const unused = await client.sources.listUnused();
const result = await client.sources.cleanupUnused(); // { deleted: [...], count }
Authenticated sources & connections
Authenticate a gdrive/gcs/confluence/jira source with a headless service account, or a reusable connection granted once in the browser.
// Option 1 — service account (headless, no browser).
await client.sources.createGoogleDrive({
folderId: 'google-drive-folder-id',
serviceAccountJson: { type: 'service_account', client_email: 'ingest@proj.iam.gserviceaccount.com', private_key: '…' }
});
// Option 2 — connection (interactive once, reused server-side).
// connect() creates the grant, opens/prints authorization_url, and polls until connected.
const conn = await client.connections.connect({ provider: 'google', sourceType: 'gdrive' });
await client.sources.createGoogleDrive({ folderId: 'google-drive-folder-id', connectionId: conn.id });
Drive the grant flow yourself when you need to:
const conn = await client.connections.create({ provider: 'google', sourceType: 'gdrive' }); // { id, status: 'pending', authorizationUrl }
console.log('Open in a browser:', conn.authorizationUrl);
await client.connections.get(conn.id); // poll until status === 'connected'
await client.connections.list({ provider: 'google' });
await client.connections.delete(conn.id);
Upload local files
ingestFiles hides the full presigned upload flow: it auto-creates a file_upload source, initializes presigned uploads, PUTs each file, and completes the upload.
await dataset.ingestFiles(['./docs/intro.md', './docs/guide.pdf']);
// Walk a directory and upload matching text files.
await dataset.ingestFilesystem('./docs', {
extensions: ['.md', '.txt'],
maxBytesPerFile: 512_000
});
Ingestion jobs
const job = await client.ingestion.startJob({ sourceId: 'src_1', datasetId: dataset.id });
const jobs = await client.ingestion.listJobs({ datasetId: dataset.id });
const detail = await client.ingestion.getJob(job.id!);
await client.ingestion.retryJob(job.id!);
Dataset documents
List retained source documents and download originals when download_available is true. Pagination is cursor-based; keep passing nextCursor until it is empty.
import { writeFile } from 'node:fs/promises';
let cursor: string | undefined;
do {
const page = await client.datasets.listDocuments('dataset_id', { limit: 50, cursor, status: 'ready' });
for (const document of page.data) {
if (!document.download_available) continue;
const bytes = await client.datasets.downloadDocument('dataset_id', document.id);
await writeFile(document.file_name ?? document.id, Buffer.from(bytes));
}
cursor = page.nextCursor;
} while (cursor);
// Object style
await dataset.listDocuments({ limit: 50 });
await dataset.downloadDocument('document_id');
The SDK follows download redirects and returns an ArrayBuffer. Storage buckets, object keys, and payload references are never exposed.
Ask / RAG
ask defaults topK: 5, includes sources, and queries all accessible datasets when unscoped.
const answer = await dataset.ask('What changed in the Q4 planning docs?');
console.log(answer.answer, answer.sources);
// Unscoped questions query across all accessible datasets.
const acrossAll = await client.ask('Summarize everything about onboarding.');
// Streaming SSE.
for await (const event of client.askStream({ query: 'Summarize this dataset', datasetId: dataset.id })) {
if (event.event === 'done') break;
console.log(event.data);
}
Multi-turn conversations
The Intelligence API is stateless. To ask a follow-up, send the prior turns as conversation history; you decide how many previous messages to include.
const history = [
{ role: 'user', content: 'What are the key features?' },
{ role: 'assistant', content: 'Hybrid search, reranking, and managed ingestion.' }
];
const answer = await client.ask({
query: 'Which of those help with relevance?',
conversationHistory: history
});
Persistent Intelligence sessions
const session = await client.intelligence.createSession({ title: 'Q4 planning', datasetId: dataset.id });
await client.intelligence.appendMessage(session.id, { role: 'user', content: 'Summarize the latest planning docs' });
const messages = await client.intelligence.listMessages(session.id, { limit: 100 });
const sessions = await client.intelligence.listSessions({ limit: 50 });
Intelligence answers return sources[] and chunks[]. Inline [1] citations refer to sources[0]; previewRef is an opaque preview token, never a raw storage key.
Schedules
const schedule = await client.schedules.create({
sourceId: 'src_1',
datasetId: dataset.id,
cron: '0 2 * * *',
timezone: 'UTC'
});
await client.schedules.trigger(schedule.id!);
Method reference
Both client.datasets.X(id, …) and datasetObj.X(…) work for dataset-scoped methods.
client.datasets
| Method | Required | Optional | HTTP |
|---|---|---|---|
list(params?) | — | limit, offset | GET /datasets |
get(id) | id | — | GET /datasets/{id} |
create(request) | name | dim, embedding, metric, hybrid, metadata | POST /datasets |
delete(id) / ds.delete() | id | — | DELETE /datasets/{id} |
stats(id) / ds.stats() | id | — | GET /datasets/{id}/stats |
listDocuments(id, params?) / ds.listDocuments(params?) | id | limit, cursor, status | GET /datasets/{id}/documents |
downloadDocument(id, docId) / ds.downloadDocument(docId) | id, docId | — | GET …/documents/{docId}/download |
search(id, query) / ds.search(query) | id, query (string, vector, or options) | topK (10), filters, hybrid, sparseQuery, alpha, rerank, includeDocuments, includeMetadata | POST …/search |
insert(id, vectors) / ds.insert(vectors) | id, vectors | — (string or numeric id) | POST …/insert |
addTexts(id, texts) / ds.addTexts(texts) | id, texts | per-record id/metadata | embed + POST …/insert |
embed(id, …) / ds.embed(…) | id | text/texts | POST …/embed |
ingestSource(id, source, options?) / ds.ingestSource(source, options?) | id, source | pipelineId | create-source? + POST /ingestion/jobs |
ingestFiles(id, paths, options?) / ds.ingestFiles(paths, options?) | id, paths | sourceName, description, metadata | presigned upload flow |
ingestFilesystem(id, root, options?) / ds.ingestFilesystem(root, options?) | id, root | extensions, maxBytesPerFile, sourceName, metadata | presigned upload flow |
ask(id, request) / ds.ask(query, options?) | id, query | topK (5), conversationHistory, includeSources | POST /intelligence/query |
client.intelligence (and client.ask / client.askStream)
| Method | Required | Optional | HTTP |
|---|---|---|---|
ask(request) | query | datasetId ("all"), topK (5), conversationHistory, includeSources | POST /intelligence/query |
askStream(request) | query | same as ask | POST /intelligence/query (SSE) |
createSession(input?) | — | title, workspaceId, datasetId, metadata | POST /intelligence/sessions |
listSessions(params?) | — | limit | GET /intelligence/sessions |
getSession(id) | id | — | GET /intelligence/sessions/{id} |
deleteSession(id) | id | — | DELETE /intelligence/sessions/{id} |
appendMessage(sessionId, input) | sessionId, role, content | metadata | POST …/messages |
listMessages(sessionId, params?) | sessionId | limit | GET …/messages |
client.sources / client.ingestion
| Method | Required | Optional | HTTP |
|---|---|---|---|
create(request) | source_type | source config | POST /ingestion/sources |
createWeb/S3/Gcs/GoogleDrive/Jira/Confluence/FileUpload(input) | per helper | per helper | POST /ingestion/sources |
list(params?) | — | limit, offset | GET /ingestion/sources |
get(sourceId) | sourceId | — | GET /ingestion/sources/{id} |
validate(sourceType, config) | sourceType, config | — | POST /ingestion/sources/validate |
getReferences(sourceId) | sourceId | — | GET /ingestion/sources/{id}/references |
delete(sourceId, options?) | sourceId | force | DELETE /ingestion/sources/{id} |
listUnused() | — | — | GET /ingestion/sources/unused |
cleanupUnused() | — | — | POST /ingestion/sources/cleanup |
startJob(request) | sourceId, datasetId | pipelineId | POST /ingestion/jobs |
listJobs(params?) | — | datasetId, limit, offset | GET /ingestion/jobs |
getJob(jobId) | jobId | — | GET /ingestion/jobs/{id} |
retryJob(jobId) | jobId | — | POST /ingestion/jobs/{id}/retry |
client.connections
| Method | Required | Optional | HTTP |
|---|---|---|---|
list(params?) | — | provider (google|atlassian) | GET /connections |
create(request) | provider | sourceType | POST /connections |
get(id) | id | — | GET /connections/{id} |
delete(id) | id | — | DELETE /connections/{id} |
connect(request) | provider | sourceType | create + open authorizationUrl + poll get until connected |
client.schedules
| Method | Required | Optional | HTTP |
|---|---|---|---|
list(params?) | — | limit, offset | GET /ingestion/schedules |
get(id) | id | — | GET /ingestion/schedules/{id} |
create(request) | sourceId, datasetId, cron | timezone, pipelineId, enabled, name, metadata | POST /ingestion/schedules |
update(id, updates) | id | all create fields | PATCH /ingestion/schedules/{id} |
delete(id) | id | — | DELETE /ingestion/schedules/{id} |
trigger(id) | id | — | POST /ingestion/schedules/{id}/trigger |
Source helpers
webSource, s3Source, gcsSource, googleDriveSource, jiraSource, confluenceSource, fileUploadSource, and genericSource(sourceType, payload). Embedding helper: openai('small' | 'large').