Skip to main content

Vercel AI SDK

gnarl-ai-sdk gives an AI SDK (ai 7) app a memory that lives on the user's own node: tools a model can call to remember and recall, and a middleware that recalls relevant memories into every prompt. A thin layer over the TypeScript client; the node embeds, so there is no embedding model to configure.

npm install gnarl-ai-sdk gnarl-client ai zod

Requires Node 22+ and ESM, as ai 7 does. With the Gnarly app running there is no address to configure: the client finds the node through $GNARL_URL, then the endpoint the node on this machine recorded, then http://127.0.0.1:43300.

import { generateText, stepCountIs, wrapLanguageModel } from "ai";
import { GnarlClient } from "gnarl-client";
import { gnarlMemoryMiddleware, gnarlMemoryTools } from "gnarl-ai-sdk";

const client = new GnarlClient(); // finds the local node
const model = wrapLanguageModel({ model: yourModel, middleware: gnarlMemoryMiddleware({ client, user: "alice" }) });
const { text } = await generateText({
model,
tools: gnarlMemoryTools({ client, user: "alice" }), // rememberFact, recallMemories
stopWhen: stepCountIs(5),
prompt: "I'm vegetarian. What should I cook tonight?",
});

gnarlMemoryTools(options)​

Returns { rememberFact, recallMemories }, made with the AI SDK's tool() and zod input schemas.

ToolInputOutput
rememberFact{ fact, factType?, pinned? }{ id, stored: true }
recallMemories{ query, k? } (k ≤ maxK, default 10){ memories: [{ id, content, score, factType, created, session }], partial }

The scope — namespace, space, user, session — is set by you, not the model, so a model cannot read or write another user's memories. Recall is scoped to user by the node itself.

gnarlMemoryMiddleware(options)​

A LanguageModelMiddleware for wrapLanguageModel. Before each model call it recalls the k (default 5) memories most relevant to the latest user message and appends them to the system prompt, or adds one.

OptionMeaning
client, namespace, space, user, sessionthe scope, as above
k, minScorehow many memories, and a score floor
remember"none" (default), "user", or "user-and-assistant": store the turn after a successful call. Only the first call of a turn stores, so a multi-step tool loop does not store one message once per step. A streamed reply is stored when the stream completes.
format(memories, partial)replace the injected text
onError"throw" (default) fails the call if Gnarl is unreachable; "continue" or a callback runs the model without memories

If part of the memory did not answer, the injected text says memories may be missing, rather than presenting a subset as everything.

Source​

gnarl-ai-sdk on GitHub · Apache-2.0