LLM features that don't
re-invent your framework.

The default for "add AI to my app" is somewhere between "wire the Vercel SDK directly and pray" and "adopt a separate agent framework with its own database". Voltro takes a third path: agents, tools, RAG, streaming threads — all first-class primitives in the same runtime as your mutations and your reactive queries. The Vercel AI SDK does the heavy lifting; @voltro/ai gives you a stable surface that works with your tests.

support.agent.tsx
TypeScript
// agents/support.agent.tsx — DESCRIPTOR (browser-safe wire contract)
import { defineAgent } from '@voltro/ai/agent'
import { Schema } from 'effect'

export const support = defineAgent({
  name:  'support',
  input: Schema.Struct({ prompt: Schema.String }),
})

// agents/support.agent.server.tsx — EXECUTOR (server-only behaviour)
import { defineAgentExecutor } from '@voltro/ai'
import { searchDocs } from '../tools/searchDocs.tool'

export default defineAgentExecutor(support, {
  system:   'You are a friendly support agent. Be concise.',
  tools:    { searchDocs },
  maxSteps: 8,
})

// The framework synthesises TWO procedures for free:
//   support.send     — kicks off a streaming turn
//   support.messages — reactive query, streams deltas

// On the client:
const { data: messages } = useSubscription(
  'app', ['support.messages', { threadId }], { threadId },
)
const send = useMutation('app', 'support.send')
// Live typewriter bubble is just a row with streaming: true.

Six primitives for the AI feature you were going to build.

defineAgent — the chat primitive

One .agent.tsx file declares an LLM chat with typed input, a system prompt, optional tools, and a token budget. The framework auto-synthesises a streaming send action + a reactive query that streams persisted message deltas to the browser.

defineTool — typed function-calling

A *.tool.tsx file declares a Schema-typed input + output. The framework wires it as a model tool. Inputs are validated; the result is decoded against the output schema before the model sees it.

pgvector RAG out of the box

vector(1536) is a first-class column type. The vectorEmbedding() mixin adds the column, the HNSW index, AND a re-embed hook that calls @voltro/ai's embed on every insert/update. nearestNeighbours('text', k) embeds the query inline.

Streaming as a React row

runAssistant patches a persisted message row as deltas arrive. The reactive subscription streams every patch. The live typewriter effect is just a row with streaming: true — no manual WebSocket plumbing, no SSE handler.

Durable agent loops via workflows

For multi-step agent reasoning that must survive a crash (the model call, then a tool call, then waiting for human approval), wrap the loop in a *.workflow.tsx. Each step result is journaled; resumption replays from where it died.

Provider switching + mock for tests

@voltro/ai wraps the Vercel AI SDK behind a stable surface. Swap the provider (Anthropic, OpenAI, local Ollama) without touching call sites. mockAi(...) installs a deterministic provider for tests — no real model calls in CI.

The agent loop IS the runtime.

An agent that needs to (a) stream tokens to a browser, (b) call tools that touch your database, (c) survive a deploy mid-loop, and (d) handle 1000 concurrent users — that's four problems Voltro's existing primitives already solve. Reactive queries do the streaming. Mutations + actions do the tool calls. Workflows do the durability. Multi-tenancy does the isolation.

Choose the right shape:

  • One-shot prompt → text. generateText(prompt). Inside any handler.
  • Schema-constrained output. generateObject(schema, prompt). Inside a mutation or action.
  • Live chat thread. defineAgent + useSubscription on the synthesised messages query.
  • Long agent loop with approvals. Wrap the loop in a workflow; use awaitSignal for the approval.
  • RAG search. vectorEmbedding() mixin on the source table; nearestNeighbours on the query.

AI is just one more handler shape.

Open the framework. See it for yourself.

Every primitive on this page is in the framework today. Clone the starter, run `voltro dev`, and have it on screen in two minutes.