Vercel AI SDK
Boxline's web search and page fetch as AI SDK tools. The model (your own OpenAI key) decides when to search and which pages to read.
How it works
tool({ description, inputSchema, execute }) from ai (v7), generateText({ model: openai(...), tools, stopWhen: isStepCount(8) }); the tool calls are in result.steps.
| Needs | a plan with web search for boxline_search; your own OpenAI key. |
|---|---|
| Site | books.toscrape.com or quotes.toscrape.com, demo sites made for scraping practice. |
| Environment | BOXLINE_API_KEY, OPENAI_API_KEY (your OpenAI key: the AI SDK calls OpenAI itself), AI_MODEL (the OpenAI model; default gpt-6-luna), QUESTION (your own question). |
| Packages | @ai-sdk/openai@4.0.81, @boxline/sdk, ai@7.0.122, tsx@4.23.15, zod@4.4.3 |
The code
/** * Vercel AI SDK with Boxline: Boxline's web search and page fetch as AI SDK tools. The model (your own OpenAI key) * decides when to search and which pages to read; each page renders in a real browser inside a Boxline sandbox. * * npm install && npx tsx index.ts (BOXLINE_API_KEY and OPENAI_API_KEY in the environment; AI_MODEL, QUESTION) * * Writes output/result.json: the answer and every tool call. */import { mkdirSync, writeFileSync } from "node:fs";import { join } from "node:path";import { openai } from "@ai-sdk/openai";import { Boxline } from "@boxline/sdk";import { generateText, isStepCount, tool } from "ai";import { z } from "zod";const out = process.env.OUTPUT_DIR ?? "output";const question = process.env.QUESTION ?? 'On books.toscrape.com, what does "Sapiens: A Brief History of Humankind" cost, and how many are in stock?';const bx = new Boxline();const tools = { boxline_search: tool({ description: "Search the web. Returns titles, addresses and snippets.", inputSchema: z.object({ query: z.string(), limit: z.number().int().min(1).max(10).optional() }), execute: async ({ query, limit }) => (await bx.search({ query, limit: limit ?? 5 })).results.map(({ title, url, snippet }) => ({ title, url, snippet })), }), boxline_fetch: tool({ description: "Open a web page in a real browser and return it as Markdown, with the absolute addresses of its links.", inputSchema: z.object({ url: z.string().url() }), execute: async ({ url }) => { const page = await bx.fetch(url, { format: "markdown", links: true }); return { url: page.finalUrl, status: page.status, title: page.title, content: page.content.slice(0, 12_000), links: (page.links ?? []).slice(0, 80) }; }, }),};const result = await generateText({ model: openai(process.env.AI_MODEL ?? "gpt-6-luna"), tools, stopWhen: isStepCount(8), system: "Answer from the pages you read with the tools, and say which page the answer came from.", prompt: question,});const calls = result.steps.flatMap((s) => s.toolCalls.map((c) => ({ tool: c.toolName, input: c.input })));for (const c of calls) console.log(`→ ${c.tool} ${JSON.stringify(c.input)}`);console.log(`\n${result.text}`);mkdirSync(out, { recursive: true });writeFileSync( join(out, "result.json"), JSON.stringify({ question, answer: result.text, calls, steps: result.steps.length, usage: { ownModelTokens: result.totalUsage.totalTokens ?? 0 } }, null, 2),);Install and run
The packages are pinned at the versions the example’s check ran with. It writes to output/ and stops its session when it finishes.
# Node 22 or newerexport BOXLINE_API_KEY=… # your Boxline API keyexport OPENAI_API_KEY=… # your own OpenAI keynpm install @ai-sdk/openai@4.0.81 @boxline/sdk ai@7.0.122 tsx@4.23.15 zod@4.4.3npx tsx index.tsWhat its check proves
It checks that the model read the book's page with the Boxline fetch tool and answered with the price and stock the site shows. To run it from a clone of the examples (git clone https://github.com/Boxline-dev/examples.git), go to examples/integrations/vercel-ai-sdk, run the commands above, then check the result from the same folder: npx tsx check.ts output.
Source: github.com/Boxline-dev/examples/integrations/vercel-ai-sdk on GitHub. See all integrations.
These docs describe a beta API. Endpoints, fields and SDK methods may change before general availability; breaking changes will be listed in the changelog.