Skip to main content

Go SDK

Idiomatic Go client for the public VectorAmp API. REST transport today, with a small transport interface so gRPC can be added later.

Install

go get github.com/vectoramp/vectoramp-go
import vectoramp "github.com/vectoramp/vectoramp-go"

Configure

NewClient needs only an API key; everything else has a sane default. Auth uses X-API-Key.

client := vectoramp.NewClient(os.Getenv("VECTORAMP_API_KEY"))

client = vectoramp.NewClient(os.Getenv("VECTORAMP_API_KEY"))

Quickstart

A minimal init, a one-call Create, then object → method calls. Only a name is required: the dimension is inferred (2560), the embedding defaults to VectorAmp-Embedding-4B, the metric defaults to cosine, and the index is always SABLE.

package main

import (
"context"
"fmt"
"log"
"os"

vectoramp "github.com/vectoramp/vectoramp-go"
)

func main() {
ctx := context.Background()
client := vectoramp.NewClient(os.Getenv("VECTORAMP_API_KEY"))

// One call creates a SABLE dataset with the managed VectorAmp embedding model.
ds, err := client.Datasets.Create(ctx, vectoramp.CreateDatasetRequest{Name: "product-docs"})
if err != nil { log.Fatal(err) }

// Object -> method: operate on the returned dataset directly.
if _, err := ds.AddTexts(ctx, []string{"VectorAmp uses SABLE for vector search."}); err != nil {
log.Fatal(err)
}

results, err := ds.Search(ctx, "How does VectorAmp search documents?", vectoramp.WithSearchTopK(5))
if err != nil { log.Fatal(err) }

answer, err := ds.Ask(ctx, "What index does VectorAmp use?", vectoramp.WithTopK(5))
if err != nil { log.Fatal(err) }

fmt.Println(results.Results)
fmt.Println(answer.Answer)
}
info

Dataset creation always requests SABLE. CreateDatasetRequest intentionally does not expose an IndexType field.

Creating datasets

Dim is inferred from the embedding model; the metric defaults to cosine, the embedding defaults to vectoramp/VectorAmp-Embedding-4B, and the index is always SABLE.

// Minimal: name only.
ds, err := client.Datasets.Create(ctx, vectoramp.CreateDatasetRequest{Name: "docs"})

// Hybrid (dense + sparse) index.
ds, err = client.Datasets.Create(ctx, vectoramp.CreateDatasetRequest{Name: "docs", Hybrid: true})

// OpenAI embedding (dim inferred: small -> 1536, large -> 3072).
ds, err = client.Datasets.Create(ctx, vectoramp.CreateDatasetRequest{
Name: "docs",
Embedding: vectoramp.OpenAIEmbedding("large"),
})

// Custom / unknown embedding models require an explicit Dim.
ds, err = client.Datasets.Create(ctx, vectoramp.CreateDatasetRequest{
Name: "docs",
Dim: 768,
Embedding: &vectoramp.EmbeddingConfig{Provider: "acme", Model: "acme-embed-v1"},
})

Object and service styles

Create, Get, and List return bound Dataset values. Call instance methods directly (ds.Search(...)) or use the service-style form — both work everywhere.

// Object -> method style (preferred)
page, err := client.Datasets.List(ctx, 50, 0)
dataset, err := client.Datasets.Get(ctx, page.Datasets[0].ID)
resp, err := dataset.Search(ctx, "release notes", vectoramp.WithSearchTopK(10))
err = dataset.Delete(ctx)

// Service style
resp, err = client.Datasets.Search(ctx, "dataset-id", vectoramp.SearchRequest{QueryText: "release notes", TopK: 10})
err = client.Datasets.Delete(ctx, "dataset-id")

Vector ids accept strings or integers. Integer ids serialize as JSON numbers so the API keeps them as-is; leave the id unset to let the API assign one.

_, err = dataset.Insert(ctx, []vectoramp.Vector{
{ID: vectoramp.StringID("doc-1"), Values: []float64{0.1, 0.2, 0.3}, Metadata: vectoramp.Metadata{"title": "Intro"}},
{ID: vectoramp.IntID(42), Values: []float64{0.4, 0.5, 0.6}}, // serialized as "id": 42
{Values: []float64{0.7, 0.8, 0.9}}, // no id -> API assigns one
})

// AddTexts embeds through the dataset model, copies the text into metadata.text, and inserts.
_, err = dataset.AddTexts(ctx, []string{"Hello world", "Machine learning notes"})

A search query may be a bare string (text search), a []float64 (vector search), or a full SearchRequest. top_k defaults to 10. WithSearchRerank(true) expands to the full model-rerank object.

resp, err := dataset.Search(ctx, "machine learning best practices",
vectoramp.WithSearchTopK(10), vectoramp.WithSearchRerank(true))

includeMetadata := true
resp, err = dataset.Search(ctx, vectoramp.SearchRequest{
QueryText: "machine learning best practices",
TopK: 10,
Filters: map[string]string{"category": "engineering"},
IncludeDocuments: true,
IncludeMetadata: &includeMetadata,
Rerank: vectoramp.RerankConfig{Enabled: true}, // vectoramp / VectorAmp-Rerank-v1
})

// Hybrid search: pass a sparse query and an alpha blend weight.
alpha := 0.5
resp, err = dataset.Search(ctx, vectoramp.SearchRequest{
QueryText: "vector database", Hybrid: true, SparseQuery: "vector database", Alpha: &alpha,
})
tip

Rerank: vectoramp.RerankConfig{Enabled: true} (or WithSearchRerank(true)) enables the optional model reranker after initial retrieval. 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".
ds, err := client.Datasets.Create(ctx, vectoramp.CreateDatasetRequest{Name: "openai-docs"},
vectoramp.WithOpenAIAPIKeySecret(os.Getenv("OPENAI_API_KEY")),
)

// Or manage the secret explicitly.
_, err = client.OrgSecrets.PutOpenAIAPIKey(ctx, os.Getenv("OPENAI_API_KEY"))

// Delete individual vector ids from search.
_, err = client.Datasets.DeleteVectors(ctx, ds.ID, []vectoramp.VectorID{"doc-001", 42},
vectoramp.DeleteVectorsOptions{WriteConcern: "quorum"},
)

Ingestion

Typed builders cover web, s3, gcs, gdrive, jira, confluence, and file_upload. Use GenericSource for custom or future types.

web, err := client.Sources.CreateWeb(ctx, vectoramp.WebSource{
StartURLs: []string{"https://docs.example.com"},
MaxDepth: 2,
})

s3, err := client.Sources.CreateS3(ctx, vectoramp.S3Source{
Bucket: "my-bucket",
Prefix: "docs/",
AccessKeyID: os.Getenv("AWS_ACCESS_KEY_ID"),
SecretAccessKey: os.Getenv("AWS_SECRET_ACCESS_KEY"),
})

confluence, err := client.Sources.CreateConfluence(ctx, vectoramp.ConfluenceSource{
CloudID: "your-cloud-id",
Username: "user@example.com",
APIToken: os.Getenv("ATLASSIAN_API_TOKEN"),
Spaces: []string{"ENG", "DOCS"},
})

jira, err := client.Sources.CreateJira(ctx, vectoramp.JiraSource{CloudID: "your-cloud-id"})
upload, err := client.Sources.CreateFileUpload(ctx, vectoramp.FileUploadSource{})

_, _, _, _, _ = web, s3, confluence, jira, upload

IngestSource accepts an existing source ID/Source or a typed builder. Passing a builder creates the source first, then starts the ingestion job.

// One-liner: build + create + start job.
job, err := dataset.IngestSource(ctx, vectoramp.WebSource{StartURLs: []string{"https://docs.example.com"}})

// Existing source IDs still work.
job, err = dataset.IngestSource(ctx, "source-id")

jobs, err := client.Ingestion.ListJobs(ctx, dataset.ID, 50, 0)
job, err = client.Ingestion.GetJob(ctx, job.JobID)

Manage and clean up sources

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

// Validate without persisting.
_, err = client.Sources.Validate(ctx, "web", map[string]any{"start_urls": []string{"https://docs.example.com"}})

// See what's using a source (active schedules + in-flight jobs).
refs, err := client.Sources.GetReferences(ctx, "source-id") // refs.Schedules, refs.ScheduleCount, refs.ActiveJobCount, refs.InUse

// Delete. Returns a 409 *APIError if in use unless force is true (force also disables its schedules).
err = client.Sources.Delete(ctx, "source-id", false)
err = client.Sources.Delete(ctx, "source-id", true)

// Find unused sources, or remove all of them in one call.
unused, err := client.Sources.ListUnused(ctx)
result, err := client.Sources.CleanupUnused(ctx) // result.Deleted, result.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).
drive, err := client.Sources.CreateGoogleDrive(ctx, vectoramp.GoogleDriveSource{
FolderIDs: []string{"google-drive-folder-id"},
ServiceAccountJSON: os.Getenv("GOOGLE_SERVICE_ACCOUNT_JSON"),
})

// Option 2 — connection (interactive once, reused server-side).
// Connect creates the grant, prints/opens AuthorizationURL, and polls until connected.
conn, err := client.Connections.Connect(ctx, vectoramp.CreateConnectionRequest{Provider: "google", SourceType: "gdrive"})
drive, err = client.Sources.CreateGoogleDrive(ctx, vectoramp.GoogleDriveSource{
FolderIDs: []string{"google-drive-folder-id"},
ConnectionID: conn.ID,
})
_ = drive

Drive the grant flow yourself when you need to:

conn, err := client.Connections.Create(ctx, vectoramp.CreateConnectionRequest{Provider: "google", SourceType: "gdrive"})
fmt.Println("Open in a browser:", conn.AuthorizationURL) // conn.Status == "pending"
conn, err = client.Connections.Get(ctx, conn.ID) // poll until conn.Status == "connected"
list, err := client.Connections.List(ctx, "google")
err = client.Connections.Delete(ctx, conn.ID)
_ = list

Minimal file upload ingestion

For local files, the SDK creates a file_upload source, initializes presigned uploads, uploads file bytes, and completes the upload — all behind one call.

job, err := dataset.IngestFiles(ctx, []string{"./docs/guide.pdf"}, nil)

// Override optional details only when you need them.
job, err = dataset.IngestFiles(ctx, []string{"./docs/guide.pdf"}, &vectoramp.IngestFilesOptions{
SourceName: "product-docs-upload",
})

Dataset documents

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

page, err := dataset.ListDocuments(ctx, vectoramp.DocumentListOptions{Limit: 50, Status: "ready"})
if err != nil { log.Fatal(err) }

for _, doc := range page.Documents {
if !doc.DownloadAvailable { continue }
bytes, err := dataset.DownloadDocument(ctx, doc.ID)
if err != nil { log.Fatal(err) }
_ = os.WriteFile(doc.FileName, bytes, 0o644)
}

if page.NextCursor != "" {
next, err := dataset.ListDocuments(ctx, vectoramp.DocumentListOptions{Cursor: page.NextCursor})
_, _ = next, err
}

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

Ask / RAG

ask defaults top_k=5, includes source citations, and uses all datasets when unscoped.

answer, err := dataset.Ask(ctx, "What are the key product features?", vectoramp.WithTopK(5))
answer, err = client.Ask(ctx, "What are the key product features?", vectoramp.WithAllDatasets())

// Streaming SSE.
stream, err := client.Intelligence.Stream(ctx, vectoramp.AskRequest{Query: "Summarize the docs", DatasetID: dataset.ID})
if err != nil { log.Fatal(err) }
defer stream.Close()
for {
event, ok := stream.Next()
if !ok { break }
if event.ChunkType == "text" { fmt.Print(event.Content) }
}

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 := []vectoramp.ConversationMessage{
{Role: "user", Content: "What are the key features?"},
{Role: "assistant", Content: "Hybrid search, reranking, and managed ingestion."},
}
answer, err := client.Ask(ctx, "Which of those help with relevance?", vectoramp.WithHistory(history))

Persistent Intelligence sessions

session, err := client.Intelligence.CreateSession(ctx, vectoramp.SessionCreateRequest{
Title: "Q4 planning", DatasetID: dataset.ID, Metadata: vectoramp.Metadata{"team": "product"},
})
if err != nil { log.Fatal(err) }

_, err = client.Intelligence.AppendMessage(ctx, session.ID, vectoramp.SessionMessageCreateRequest{
Role: "user", Content: "Summarize the latest planning docs",
})
messages, err := client.Intelligence.ListMessages(ctx, session.ID, 100)
sessions, err := client.Intelligence.ListSessions(ctx, 50)

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

Errors

Non-2xx responses return *vectoramp.APIError.

if apiErr, ok := err.(*vectoramp.APIError); ok {
fmt.Println(apiErr.StatusCode, apiErr.Message)
}

Method reference

All methods take context.Context as the first argument (omitted below for brevity). Both the service-style (client.Datasets.Search(id, ...)) and object-style (ds.Search(...)) forms work where listed.

client.Datasets / *Dataset

MethodRequiredOptionalReturns
List(limit, offset)limit, offset (0 omits)*DatasetList
Get(id)id*Dataset
Create(req)req.NameDim (inferred), Metric (cosine), Hybrid, Embedding, Tuning, Metadata*Dataset
Delete(id) / ds.Delete()iderror
ListDocuments(id, opts) / ds.ListDocuments(opts)idopts.Limit, opts.Cursor, opts.Status*DatasetDocumentList
DownloadDocument(id, docID) / ds.DownloadDocument(docID)id, docID[]byte
Search(id, input, opts...) / ds.Search(input, opts...)id, input (string, []float64, or SearchRequest)WithSearchTopK (10), WithSearchMetadata, WithSearchDocuments, WithSearchRerank, WithSearchRerankConfig*SearchResponse
Insert(id, vectors) / ds.Insert(vectors)id, vectors— (string or numeric id)*InsertVectorsResponse
Embed(id, req) / ds.Embed(req)id, req.Text or req.TextsEmbeddingProvider, EmbeddingModel*EmbedResponse
AddTexts(id, input, opts...) / ds.AddTexts(input, opts...)id, input (string, []string, []TextDocument, or AddTextsRequest)WithEmbedding(provider, model)*AddTextsResponse
IngestSource(id, source, pipelineID...) / ds.IngestSource(source, pipelineID...)id, source (ID, Source, or builder)pipelineID*Job
IngestFiles(id, paths, opts) / ds.IngestFiles(paths, opts)id, pathsopts.SourceName, opts.Description, opts.PipelineID, opts.Metadata*Job
Ask(id, input, opts...) / ds.Ask(input, opts...)id, input (string or AskRequest)WithTopK (5), WithSources, WithHistory*AskResponse

Vector id helpers: StringID(s), IntID(n), NewVectorID(any). Embedding helpers: VectorAmpEmbedding(), OpenAIEmbedding("small"|"large").

client.Sources / client.Ingestion

MethodRequiredReturns
CreateSource(source) / Create(source)source (builder or CreateSourceRequest)*Source
CreateWeb/CreateS3/CreateGCS/CreateGoogleDrive/CreateJira/CreateConfluence/CreateFileUpload(source)typed builder*Source
ListSources(limit, offset)*SourceList
GetSource(id)id*Source
Validate(sourceType, config)sourceType, config*ValidateResult
GetReferences(id)id*SourceReferences
Delete(id, force)id, forceerror
ListUnused()*SourceList
CleanupUnused()*CleanupResult (Deleted, Count)
StartJob(req)req.SourceID, req.DatasetID*Job
ListJobs(datasetID, limit, offset)— (datasetID optional filter)*JobList
GetJob(id) / RetryJob(id)id*Job
IngestFiles(datasetID, paths, opts)datasetID, paths*Job

Source builders: WebSource, S3Source, GCSSource, GoogleDriveSource (ServiceAccountJSON or ConnectionID for auth), JiraSource, ConfluenceSource, FileUploadSource, GenericSource.

client.Connections

MethodRequiredReturns
List(provider)— (provider optional filter)*ConnectionList
Create(req)req.Provider*Connection (Status: "pending", AuthorizationURL)
Get(id)id*Connection (poll until Status == "connected")
Delete(id)iderror
Connect(req)req.Provider*Connection — create + open AuthorizationURL + poll until connected

client.Schedules

List(limit, offset), Get(id), Create(req), Update(id, req), Delete(id), Trigger(id).

client.Intelligence

Ask(input, opts...), Stream(req), CreateSession(req), ListSessions(limit), GetSession(id), DeleteSession(id), AppendMessage(sessionID, req), ListMessages(sessionID, limit). client.Ask(...) is a shortcut for client.Intelligence.Ask(...).