Skip to main content

Ruby SDK

Official Ruby client for VectorAmp. Defaults to https://api.vectoramp.com; REST transport today, designed so gRPC can be added later.

Install​

gem "vector_amp"

From a checkout for local development:

git clone https://github.com/vectoramp/vectoramp-ruby.git
cd vectoramp-ruby
bundle install

Configure​

require "vector_amp"

# api_key defaults to ENV["VECTORAMP_API_KEY"]; base_url defaults to production.
client = VectorAmp::Client.new

# Or configure explicitly.
client = VectorAmp::Client.new(
api_key: ENV.fetch("VECTORAMP_API_KEY"),
timeout: 30
)

Auth uses the X-Api-Key header.

Quickstart​

A minimal init, a one-call create, then object → method calls. Only a name is required: the SDK defaults to the managed vectoramp/VectorAmp-Embedding-4B model, infers dim (2560), metric "cosine", and always uses the SABLE index.

require "vector_amp"

client = VectorAmp::Client.new

# One-call create.
dataset = client.datasets.create(name: "product-docs")

# Object -> method style: call helpers directly on the returned dataset.
dataset.add_texts(["VectorAmp is powered by SABLE."], metadata: { source: "readme" })

results = dataset.search("What powers VectorAmp?", top_k: 5, include_documents: true)
answer = dataset.ask("What powers VectorAmp?")

puts results.inspect
puts answer["answer"]
info

Dataset creation intentionally does not accept index_type; all datasets are created with SABLE.

Creating datasets​

# Minimal: name only.
client.datasets.create(name: "docs")

# Enable hybrid (dense + sparse) search.
client.datasets.create(name: "docs", hybrid: true)

# Use an OpenAI embedding model (dim inferred from the model).
client.datasets.create(name: "docs", embedding: VectorAmp::Embedding.openai("large"))

# Custom/unknown model: pass dim explicitly.
client.datasets.create(name: "docs", embedding: { provider: "acme", model: "acme-embed" }, dim: 1024)

Object and service styles​

# Object -> method style (preferred)
page = client.datasets.list(limit: 50, offset: 0)
dataset = page["datasets"].first
dataset.search("release notes", top_k: 10)
dataset.delete

# Service style
client.datasets.search("dataset-uuid", "release notes", top_k: 10)
client.datasets.insert("dataset-uuid", vectors: [])
client.datasets.delete("dataset-uuid")

List calls return pagination envelopes such as { "datasets" => [...], "total" => n, "limit" => 50, "offset" => 0 }.

Vector ids may be strings or integers; integer ids stay numbers.

# Raw vectors
dataset.insert(vectors: [
{ id: 1, values: [0.1, 0.2, 0.3], metadata: { title: "Intro" } },
{ id: "doc-002", values: [0.4, 0.5, 0.6], metadata: { title: "Details" } }
])

# Embed text with the dataset model, then insert.
dataset.add_texts(["Wireless headphones"], metadata: { category: "electronics" })

search accepts a bare string (text query) or a float vector; top_k defaults to 10.

dataset.search(
"wireless headphones",
top_k: 10,
filters: { category: "electronics" },
advanced_filters: [{ field: "price", op: "lt", value: 100.0 }],
include_metadata: true,
rerank: true # expands to vectoramp / VectorAmp-Rerank-v1
)

# Hybrid search on a hybrid dataset.
dataset.search("wireless headphones", hybrid: true, sparse_query: "headphones", alpha: 0.5)
tip

rerank: true enables the optional model reranker after initial retrieval. The separate rerank_depth_override 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".
dataset = client.datasets.create(
name: "openai-docs",
openai_api_key: ENV.fetch("OPENAI_API_KEY")
)

# Or manage the secret explicitly.
client.secrets.put_openai_api_key(ENV.fetch("OPENAI_API_KEY"))

# Delete individual vector ids from search.
dataset.delete_vectors(ids: ["doc-001", 42], write_concern: "quorum")
client.datasets.delete_vectors(dataset.id, ids: ["doc-002"])

Ingestion​

Typed builders cover web, s3, gcs, gdrive, jira, confluence, and file_upload.

web = client.sources.create_web(start_urls: ["https://docs.example.com"], max_depth: 1)
s3 = client.sources.create_s3(bucket: "vectoramp-docs", prefix: "public/")
gcs = client.sources.create_gcs(bucket: "vectoramp-docs-gcs", prefix: "public/")
jira = client.sources.create_jira(cloud_id: "atlassian-cloud-id", project_keys: ["ENG"])
confluence = client.sources.create_confluence(
cloud_id: "atlassian-cloud-id",
spaces: ["ENG"],
username: "bot@vectoramp.com",
api_token: ENV["CONFLUENCE_API_TOKEN"]
)
gdrive = client.sources.create_google_drive(folder_ids: ["google-drive-folder-id"])
file_source = client.sources.create_file_upload

# One-liner: ingest by source id or a typed source object that has an id.
job = dataset.ingest_source(web["id"])

Use VectorAmp::GenericSource or client.ingestion.create_source(...) when the API supports a source type before the SDK has a dedicated helper.

generic_source = VectorAmp::GenericSource.new(
source_type: "custom",
name: "custom-source",
config: { endpoint: "https://example.com/feed" }
)
client.sources.create(generic_source)

Manage and clean up sources​

Validate a config, delete a source, or find and remove sources nothing is using.

# Validate without persisting.
client.sources.validate("web", { start_urls: ["https://docs.example.com"] })

# See what's using a source (active schedules + in-flight jobs).
refs = client.sources.get_references("source-uuid") # { "schedules" => [...], "schedule_count" => …, "active_job_count" => …, "in_use" => … }

# Delete. Raises 409 if in use unless force: true (force also disables its schedules).
client.sources.delete("source-uuid")
client.sources.delete("source-uuid", force: true)

# Find unused sources, or remove all of them in one call.
unused = client.sources.list_unused
result = client.sources.cleanup_unused # { "deleted" => [...], "count" => n }

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).
client.sources.create_google_drive(
folder_ids: ["drive-folder-id"],
service_account_json: { type: "service_account", client_email: "ingest@proj.iam.gserviceaccount.com", private_key: "…" }
)

# Option 2 — connection (interactive once, reused server-side).
# connect creates the grant, prints/opens authorization_url, and polls until connected.
conn = client.connections.connect(provider: "google", source_type: "gdrive")
client.sources.create_google_drive(folder_ids: ["drive-folder-id"], connection_id: conn["id"])

Drive the grant flow yourself when you need to:

conn = client.connections.create(provider: "google", source_type: "gdrive") # { "id" => …, "status" => "pending", "authorization_url" => … }
puts "Open in a browser: #{conn["authorization_url"]}"
client.connections.get(conn["id"]) # poll until status == "connected"
client.connections.list(provider: "google")
client.connections.delete(conn["id"])

Minimal file upload ingestion​

ingest_files uses the REST upload flow. No source name is required; the SDK creates a file_upload source with a generated name before uploading.

dataset.ingest_files(paths: ["docs/whitepaper.pdf"])

# Service style
client.ingestion.ingest_files(
dataset_id: "dataset-uuid",
paths: ["docs/whitepaper.pdf", "docs/notes.md"],
metadata: { team: "product" }
)

Dataset documents​

List retained source documents and download originals when download_available is true. Pagination is cursor-based; keep passing next_cursor until it is empty.

page = client.datasets.list_documents("dataset-uuid", limit: 50, status: "ready")

page.fetch("documents").each do |document|
next unless document["download_available"]

bytes = client.datasets.download_document("dataset-uuid", document.fetch("id"))
File.binwrite(document["file_name"] || document.fetch("id"), bytes)
end

if page["next_cursor"]
next_page = client.datasets.list_documents("dataset-uuid", cursor: page["next_cursor"])
end

# Object style
docs = dataset.list_documents(limit: 25)
raw = dataset.download_document(docs.fetch("documents").first.fetch("id"))

The SDK follows download redirects and returns raw bytes. Storage buckets, object keys, and payload references are never exposed.

Intelligence​

ask defaults top_k=5 (server-side), includes sources, and uses "all" datasets when unscoped.

answer = client.ask("What are the key features?", dataset_id: "all", top_k: 5)
puts answer["answer"]

client.ask_stream("Summarize the docs", dataset_id: "dataset-uuid") do |event|
print event["content"] if event["chunk_type"] == "text"
end

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.

history = [
{ role: "user", content: "What are the key features?" },
{ role: "assistant", content: "Hybrid search, reranking, and managed ingestion." }
]
answer = client.intelligence.query(
"Which of those help with relevance?",
dataset_id: "all",
conversation_history: history.last(10)
)
puts answer["answer"]

Persistent Intelligence sessions​

Persist multi-turn conversations server-side instead of resending history.

session = client.intelligence.create_session(title: "Onboarding", dataset_id: "dataset-uuid")

client.intelligence.append_message(session.fetch("id"), role: "user", content: "What is SABLE?")
client.intelligence.append_message(session.fetch("id"), role: "assistant", content: "A managed index type.")

messages = client.intelligence.list_messages(session.fetch("id"))
sessions = client.intelligence.list_sessions(limit: 20)
client.intelligence.get_session(session.fetch("id"))
client.intelligence.delete_session(session.fetch("id"))

answer["sources"] contains source-level citations. Inline [1] markers refer to sources[0]; preview_ref is an opaque preview token, never a raw storage key.

Error handling​

Non-2xx responses raise VectorAmp::APIError with status, body, and headers.

begin
client.datasets.get("missing")
rescue VectorAmp::APIError => e
warn "VectorAmp API error #{e.status}: #{e.message}"
end

Method reference​

Both access styles work everywhere: client.datasets.<m>(id, ...) and the equivalent datasetObj.<m>(...) on a returned VectorAmp::Dataset.

Datasets — client.datasets​

MethodRequiredOptional (default)
create(name:)namedim (inferred), embedding (vectoramp/VectorAmp-Embedding-4B), metric ("cosine"), hybrid, filters, metadata_schema, tuning, metadata
list—limit (50), offset (0)
get(id)id—
delete(id) / ds.deleteid—
stats(id) / ds.statsid—
search(id, query = nil) / ds.search(query)idquery/query_text/search_text, top_k (10), filters, advanced_filters, hybrid, sparse_query, alpha, rerank, include_documents, include_metadata, include_embeddings, embedding_model, embedding_provider, nprobe_override, rerank_depth_override
insert(id, vectors:) / ds.insert(vectors:)id, vectors— (integer ids preserved as numbers)
embed(id) / ds.embedidtext, texts (one required)
add_texts(id, texts = nil) / ds.add_texts(texts)id, textsids (auto-generated), metadata
list_documents(id) / ds.list_documentsidlimit (50), cursor, status
download_document(id, document_id) / ds.download_document(document_id)id, document_id—
ds.ask(query)querytop_k, conversation_history, include_sources
ds.ingest_source(source)source (id or builder)pipeline_id
ds.ingest_files(paths:)pathssource_name, metadata

Intelligence — client.intelligence (and client.ask / client.ask_stream)​

MethodRequiredOptional (default)
query(query) / ask / ask_streamquerydataset_id ("all" when unscoped), top_k (5 server-side), conversation_history, include_sources, stream
create_session—title, dataset_id, workspace_id, metadata
list_sessions—limit (50)
get_session(session_id) / delete_session(session_id)session_id—
append_message(session_id, role:, content:)session_id, role, contentmetadata
list_messages(session_id)session_idlimit (100)

Sources — client.sources (alias of client.ingestion)​

create(source), create_web(start_urls:), create_s3(bucket:), create_gcs(bucket:), create_google_drive(folder_ids:|file_ids:, service_account_json:|connection_id:), create_jira(cloud_id:), create_confluence(cloud_id:|base_url:), create_file_upload, list_sources, get_source(id). Management: validate(source_type, config), get_references(id), delete(id, force: false), list_unused, cleanup_unused. Jobs: start_job(source_id:, dataset_id:), list_jobs, get_job(id), retry_job(id), ingest_files(dataset_id:, paths:).

Schedules — client.schedules​

list, get(id), create(source_id:, dataset_id:, cron:), update(id), delete(id), trigger(id).

Connections — client.connections​

list(provider: nil), create(provider:, source_type: nil) → { "id", "status" => "pending", "authorization_url" }, get(id) (poll until status == "connected"), delete(id), and the connect(provider:, source_type: nil) helper (create → open authorization_url → poll until connected).