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. Set cacheControl on the provider to mark the prefix.
import { createDoubleword } from "@doubleword/vercel-ai";
import { generateText } from "ai";
const doubleword = createDoubleword({
apiKey: "{{apiKey}}",
cacheControl: { type: "ephemeral", 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, ... }type is always "ephemeral", and ttl is "5m" or "1h" (optional, the API default is "5m"). The provider marks the last system message and the latest message in every request. 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 change the marker for a single call, pass cacheControl through providerOptions; it replaces the provider default:
const result = await generateText({
model: doubleword("{{selectedModel.id}}"),
system: "…large, stable instructions…",
prompt: "What is 2 + 2?",
providerOptions: {
doubleword: { cacheControl: { type: "ephemeral", ttl: "5m" } },
},
});Pass cacheControl: false to send no markers for one call. The same cacheControl option works on createDoublewordAsync and createDoublewordBatch. 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.