Vector Storage

Store documents and embeddings for semantic search and retrieval

Use vector storage when exact keys or SQL filters are not enough, for example knowledge search, RAG retrieval, recommendations, or long-lived memory. Start with VectorClient; Hono apps can use c.var.vector after installing the Agentuity middleware.

npm install @agentuity/vector
import { VectorClient } from '@agentuity/vector';
 
const vector = new VectorClient();
 
await vector.upsert(
  'docs',
  {
    key: 'deploy-guide',
    document: 'Deploy framework apps to Agentuity with agentuity deploy.',
    metadata: { category: 'guide', source: 'docs' },
  },
  {
    key: 'service-clients',
    document: 'Agentuity services can be used through dedicated client packages.',
    metadata: { category: 'api', source: 'docs' },
  }
);
 
const results = await vector.search('docs', {
  query: 'How do I use Agentuity services from my app?',
  limit: 3,
  similarity: 0.7,
});

VectorClient reads AGENTUITY_SDK_KEY, then AGENTUITY_CLI_KEY, from the environment. Agentuity project code can keep that key in .env for local development and deployed environments can receive it through project environment configuration.

When to use vector storage

NeedUse
semantic search or RAG retrievalVector
exact key lookupKey-Value
relational joins or transactionsDatabase
files or binary dataObject Storage
append-only ordered dataDurable Streams

Upsert Data

Pass document when you want Agentuity to generate embeddings. Pass embeddings when you already have vectors from another model.

await vector.upsert('products', {
  key: 'chair-001',
  document: 'Ergonomic office chair with lumbar support',
  metadata: { category: 'furniture', source: 'catalog' },
  ttl: 60 * 60 * 24 * 90,
});
 
await vector.upsert('custom-embeddings', {
  key: 'embedding-001',
  embeddings: [0.12, 0.34, 0.56, 0.78],
  metadata: { source: 'external-model' },
  ttl: null,
});

The operation is idempotent by key: upserting the same key updates the stored vector.

TTL

Vectors expire after 30 days unless you pass ttl.

ValueBehavior
omittedexpire after 30 days
null or 0never expire
>= 60expire after that many seconds

Search accepts a natural-language query, optional result limit, optional similarity threshold, and optional metadata filter.

const furniture = await vector.search('products', {
  query: 'comfortable seating for a desk',
  limit: 5,
  similarity: 0.65,
  metadata: { category: 'furniture', source: 'catalog' },
});
 
const rankedKeys = furniture.map((result) => result.key);

Search results include id, key, similarity, optional metadata, and optional expiresAt.

Type-Safe Metadata

get(), getMany(), and search() accept a metadata generic. The constraint is Record<string, unknown>, so define metadata types as a type intersection or as an interface that extends the record.

type ProductMetadata = {
  readonly category: 'furniture' | 'electronics';
  readonly source: string;
} & Record<string, unknown>;
 
const typed = await vector.search<ProductMetadata>('products', {
  query: 'comfortable seating for a desk',
  metadata: { category: 'furniture', source: 'catalog' },
});
 
const document = await vector.get<ProductMetadata>('products', 'chair-001');
 
if (document.exists) {
  const category = document.data.metadata?.category;
  void category;
}

Retrieve and Delete

Use get() when you already know the key, getMany() for batches, and delete() when you want to remove one or more vectors.

const document = await vector.get('products', 'chair-001');
 
if (document.exists) {
  const metadata = document.data.metadata;
}
 
const batch = await vector.getMany('products', 'chair-001', 'desk-001');
const deleted = await vector.delete('products', 'chair-001', 'desk-001');

Namespaces and Stats

Vector namespaces are created on first upsert. Use stats and namespace methods to inspect or clean up stored data.

const hasProducts = await vector.exists('products');
const stats = await vector.getStats('products');
const allStats = await vector.getAllStats({ limit: 25, sort: 'size' });
const namespaces = await vector.getNamespaces();
 
await vector.deleteNamespace('old-products');

getStats() returns sum (bytes), count, optional createdAt/lastUsed, and sampledResults with up to 20 vectors and their embeddings. Use the samples for quick inspection without running a search.

Hono

In Hono apps, @agentuity/hono initializes VectorClient once and exposes it on c.var.vector.

npm install @agentuity/hono hono
import { Hono } from 'hono';
import { agentuity } from '@agentuity/hono';
import type { Services } from '@agentuity/hono';
 
type Variables = Pick<Services, 'vector'>;
 
const app = new Hono<{ Variables: Variables }>();
 
app.use('*', agentuity());
 
app.get('/search', async (c) => {
  const query = c.req.query('q');
 
  if (!query) {
    return c.json({ error: 'Missing q query parameter' }, 400);
  }
 
  const results = await c.var.vector.search('docs', {
    query,
    limit: 5,
  });
 
  return c.json({ results });
});
 
export default app;

Next Steps