Dev local vector store
The Dev Local Vector Store provides a local, file-based vector store for development and testing purposes. It is not intended for production use.
Installation
Section titled “Installation”The local vector store functionality is built into Genkit Go. You need to import the localvec package:
import "github.com/firebase/genkit/go/plugins/localvec"Configuration
Section titled “Configuration”To use the local vector store, initialize it and define a retriever with an embedder:
package main
import ( "context" "log"
"github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/plugins/googlegenai" "github.com/firebase/genkit/go/plugins/localvec")
func main() { ctx := context.Background()
g := genkit.Init(ctx, genkit.WithPlugins(&googlegenai.VertexAI{}))
if err := localvec.Init(); err != nil { log.Fatal(err) }
embedder := genkit.LookupEmbedder(g, "vertexai/text-embedding-004") if embedder == nil { log.Fatal("embedder vertexai/text-embedding-004 is not registered") }
myDocStore, myRetriever, err := localvec.DefineRetriever( g, "my_vectorstore", localvec.Config{ Dir: ".genkit/localvec", Embedder: embedder, }, nil, ) if err != nil { log.Fatal(err) }
// The Usage examples below continue from here: myDocStore for indexing, // myRetriever for queries. They also need "fmt" and // "github.com/firebase/genkit/go/ai". _, _ = myDocStore, myRetriever}genkit.LookupEmbedder(g, name) returns nil if no embedder with that
identifier is registered, and the name must carry the provider prefix
(vertexai/, googleai/). Check for nil before you hand the result to
localvec.Config; a nil embedder panics on the first index or query.
Function signature
Section titled “Function signature”func DefineRetriever( g *genkit.Genkit, name string, cfg Config, opts *ai.RetrieverOptions,) (*DocStore, ai.Retriever, error)Hold the first result as a *localvec.DocStore if you need it later; it is the
value you pass to localvec.Index. The retriever is registered under
devLocalVectorStore/<name>, which is the identifier to use with
ai.WithRetrieverName.
Configuration options
Section titled “Configuration options”- name (string): A unique name for this vector store instance. This is used as the retriever reference.
- Dir (string): Directory for the database file. Defaults to
os.TempDir(). - Embedder (
ai.Embedder): The embedding model to use. Must be a configured embedder in your Genkit project. - EmbedderOptions (
any): Options passed through to the embedder on every call, for example&genai.EmbedContentConfig{TaskType: "RETRIEVAL_DOCUMENT"}. - opts (
*ai.RetrieverOptions): Action metadata for the retriever this call defines:Label,ConfigSchema,Supports,Metadata. Passnilto accept the defaults.
ai.RetrieverOptions describes the action. It is not the per-query options
struct: that is localvec.RetrieverOptions, covered under
Retrieving documents.
Where the data lives
Section titled “Where the data lives”The store writes one JSON file per retriever at <Dir>/__db_<name>.json,
rewritten through a .tmp file on every Index call. The index survives a
process restart as long as that directory does, so the default os.TempDir()
outlives a restart but not necessarily a reboot or a temp sweep. Set Dir to a
project path such as .genkit/localvec if you want it stable, and add that path
to .gitignore. To reset the store, delete the __db_*.json file.
Indexing documents
Section titled “Indexing documents”The Dev Local Vector Store automatically creates indexes. To populate one, build
ai.Document
values and pass them to localvec.Index with the doc store:
data := []string{ "This is the first document.", "This is the second document.", "This is the third document.", "This is the fourth document.",}
var docs []*ai.Documentfor i, text := range data { docs = append(docs, ai.DocumentFromText(text, map[string]any{ "id": fmt.Sprintf("doc-%d", i), "source": "handbook.md", }))}
// Index the documents using the DocStore returned by DefineRetrieverif err := localvec.Index(ctx, docs, myDocStore); err != nil { log.Fatal(err)}The store persists the whole ai.Document as JSON, so metadata you attach at
index time comes back on every retrieved document. Use it for IDs, titles and
source paths. Because it round-trips through JSON, numeric metadata values come
back as float64 even if you stored an int.
Concurrency and re-indexing
Section titled “Concurrency and re-indexing”localvec.Index is not safe for concurrent use on the same DocStore. It
mutates an unsynchronized map and rewrites the whole database file, so
concurrent calls race and can lose writes. Call it from one goroutine at a time.
Reads through the retriever run against that same unsynchronized map, so do not
index while you are serving queries.
Indexing is keyed by a hash of the document content, so re-running ingestion
over unchanged text is a no-op and does not duplicate chunks. Editing a document
adds a new entry and leaves the old one in place. There is no delete or clear
API; reset the store by deleting <Dir>/__db_<name>.json.
Retrieving documents
Section titled “Retrieving documents”Use genkit.Retrieve
with the retriever you defined. Pass &localvec.RetrieverOptions{K: n} to
control how many documents come back; the default is 3.
resp, err := genkit.Retrieve(ctx, g, ai.WithRetriever(myRetriever), ai.WithConfig(&localvec.RetrieverOptions{K: 5}), ai.WithTextDocs("search query"))if err != nil { log.Fatal(err)}
// Process the retrieved documentsfor _, doc := range resp.Documents { fmt.Println(doc.Metadata["source"], doc.Content[0].Text)}If you do not have the ai.Retriever value at hand, name it instead:
resp, err := genkit.Retrieve(ctx, g, ai.WithRetrieverName("devLocalVectorStore/my_vectorstore"), ai.WithTextDocs("search query"))ai.RetrieverResponse carries only Documents. The store ranks by cosine
similarity internally but does not return the scores, so K is its only
relevance control.