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.

Intelligence​

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()id—error
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(...).