Vercel AI SDK
The @doubleword/vercel-ai package provides a Doubleword provider for the Vercel AI SDK, with automatic API key resolution and a pre-configured base URL.
Install
npm install @doubleword/vercel-ai aiChat / Text Generation
import { createDoubleword } from "@doubleword/vercel-ai";
import { generateText } from "ai";
const doubleword = createDoubleword({
apiKey: "{{apiKey}}",
});
const result = await generateText({
model: doubleword("{{selectedModel.id}}"),
prompt: "Say hello.",
});
console.log(result.text);Tool calling
The provider supports multi-step tool use via generateText. The model decides when to call tools, receives the results, and formulates a final answer:
import { createDoubleword } from "@doubleword/vercel-ai";
import { generateText, tool, jsonSchema, stepCountIs } from "ai";
const doubleword = createDoubleword({
apiKey: "{{apiKey}}",
});
const result = await generateText({
model: doubleword("{{selectedModel.id}}"),
tools: {
calculator: tool({
description: "Evaluate a basic arithmetic expression",
inputSchema: jsonSchema({
type: "object",
properties: {
expression: { type: "string", description: "The expression to evaluate" },
},
required: ["expression"],
additionalProperties: false,
}),
execute: async ({ expression }: { expression: string }) => {
return String(new Function(`return (${expression})`)());
},
}),
},
stopWhen: stepCountIs(5),
prompt: "What is 137 * 49?",
});
console.log(result.text);stopWhen: stepCountIs(5) allows up to 5 model→tool→model round-trips before returning. Each step where the model calls a tool automatically feeds the result back for the next step.
Streaming
import { createDoubleword } from "@doubleword/vercel-ai";
import { streamText } from "ai";
const doubleword = createDoubleword({
apiKey: "{{apiKey}}",
});
const stream = streamText({
model: doubleword("{{selectedModel.id}}"),
prompt: "Say hello.",
});
for await (const chunk of stream.textStream) {
process.stdout.write(chunk);
}Embeddings
import { createDoubleword } from "@doubleword/vercel-ai";
import { embed } from "ai";
const doubleword = createDoubleword({
apiKey: "{{apiKey}}",
});
const result = await embed({
model: doubleword.embeddingModel("Qwen/Qwen3-Embedding-8B"),
value: "Hello world",
});
console.log(result.embedding.length); // 4096Prompt caching
Doubleword reuses a repeated prompt prefix: the first request stores the processed prefix and later requests read it back cheaply instead of recomputing it. Enable it on the provider to cache the system prefix.
import { createDoubleword } from "@doubleword/vercel-ai";
import { generateText } from "ai";
const doubleword = createDoubleword({
apiKey: "{{apiKey}}",
cache: { ttl: "1h" },
});
const { text, usage } = await generateText({
model: doubleword("{{selectedModel.id}}"),
system: "…large, stable instructions…", // the ~1024-token floor applies
prompt: "What is 2 + 2?",
});
console.log(usage.inputTokenDetails); // { cacheReadTokens, noCacheTokens, ... }ttl is "5m" or "1h", and the cache is left-anchored: a stable prefix followed by a changing tail caches well, a changing prefix caches nothing. Cache activity is reported on usage.inputTokenDetails (cacheReadTokens, noCacheTokens).
To tune a single call, or to cache a different message, pass cacheControl through providerOptions; it overrides the provider default:
const result = await generateText({
model: doubleword("{{selectedModel.id}}"),
system: "…large, stable instructions…",
prompt: "What is 2 + 2?",
providerOptions: {
doubleword: { cacheControl: { ttl: "1h", scope: "system" } },
},
});scope selects which messages carry the breakpoint: "system" (default), "lastUser", or an array of message indices. Pass cacheControl: false to skip caching for one call.
The same cache option and cacheControl work on createDoublewordAsync and createDoublewordBatch; within one batch the shared prefix is processed once and read by the rest. See the prompt caching guide for the full mechanics.
Default singleton
For convenience, a pre-configured singleton is also exported that reads DOUBLEWORD_API_KEY from the environment:
import { doubleword } from "@doubleword/vercel-ai";
import { generateText } from "ai";
const result = await generateText({
model: doubleword("{{selectedModel.id}}"),
prompt: "Say hello.",
});Batch pricing
For background workloads where latency is not critical, use createDoublewordBatch to transparently route requests through Doubleword's Batch API — cutting inference costs by up to 90% with the Doubleword Inference API. Powered by autobatcher under the hood.
import { createDoublewordBatch } from "@doubleword/vercel-ai";
import { generateText } from "ai";
const doubleword = createDoublewordBatch({
apiKey: "{{apiKey}}",
batchWindowSeconds: 2.5,
});
const result = await generateText({
model: doubleword("{{selectedModel.id}}"),
prompt: "Summarize this document.",
});
console.log(result.text);
await doubleword.close();Concurrent generateText calls are automatically collected into batch submissions. The interface is identical to the real-time provider — only streaming is not supported.
Try it end-to-end
A full tool-calling example lives in the repo at examples/tool-calling/. It runs a calculator agent against concurrent arithmetic queries, demonstrating the multi-step agentic loop.