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)
}
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")
Insert, add text, and search
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,
})
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
| Method | Required | Optional | Returns |
|---|---|---|---|
List(limit, offset) | — | limit, offset (0 omits) | *DatasetList |
Get(id) | id | — | *Dataset |
Create(req) | req.Name | Dim (inferred), Metric (cosine), Hybrid, Embedding, Tuning, Metadata | *Dataset |
Delete(id) / ds.Delete() | id | — | error |
ListDocuments(id, opts) / ds.ListDocuments(opts) | id | opts.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.Texts | EmbeddingProvider, 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, paths | opts.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
| Method | Required | Returns |
|---|---|---|
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, force | error |
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
| Method | Required | Returns |
|---|---|---|
List(provider) | — (provider optional filter) | *ConnectionList |
Create(req) | req.Provider | *Connection (Status: "pending", AuthorizationURL) |
Get(id) | id | *Connection (poll until Status == "connected") |
Delete(id) | id | error |
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(...).