@olostep/ai-sdk 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,196 @@
1
+ // src/index.ts
2
+ import { tool } from "ai";
3
+ import { Format, Olostep } from "olostep";
4
+ import { z } from "zod";
5
+ var DEFAULT_MAX_CHARACTERS = 2e4;
6
+ var DEFAULT_TIMEOUT_SECONDS = 300;
7
+ var DEFAULT_PAGES_PER_CALL = 20;
8
+ var MAX_PAGES_PER_CALL = 100;
9
+ var MAX_JOB_SIZE = 1e5;
10
+ var API_URL = "https://api.olostep.com/v1";
11
+ function waitOptions(options) {
12
+ return { checkEveryNSecs: 5, timeoutSeconds: options.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS };
13
+ }
14
+ function client(options) {
15
+ return new Olostep({ apiKey: options.apiKey, apiBaseUrl: options.apiBaseUrl });
16
+ }
17
+ function truncate(text, max) {
18
+ if (!text) return "";
19
+ return text.length > max ? `${text.slice(0, max)}
20
+
21
+ [truncated]` : text;
22
+ }
23
+ function stillRunning(id) {
24
+ return {
25
+ id,
26
+ status: "in_progress",
27
+ message: "Still running. Use the results tool with this id in a few minutes."
28
+ };
29
+ }
30
+ async function readPages(options, kind, id, cursor) {
31
+ const olostep = client(options);
32
+ const info = await olostep[kind].info(id);
33
+ if (info.status === "in_progress") return stillRunning(id);
34
+ const limit = Math.max(1, Math.min(options.pagesPerCall ?? DEFAULT_PAGES_PER_CALL, MAX_PAGES_PER_CALL));
35
+ const params = new URLSearchParams({ limit: String(limit) });
36
+ if (kind === "batches") params.set("status", "completed");
37
+ if (cursor) params.set("cursor", cursor);
38
+ const path = kind === "batches" ? "items" : "pages";
39
+ const response = await fetch(`${options.apiBaseUrl ?? API_URL}/${kind}/${id}/${path}?${params}`, {
40
+ headers: { Authorization: `Bearer ${options.apiKey ?? process.env.OLOSTEP_API_KEY}` }
41
+ });
42
+ if (!response.ok) throw new Error(`Olostep API error (HTTP ${response.status}) while listing ${id}`);
43
+ const data = await response.json();
44
+ const max = options.maxCharacters ?? DEFAULT_MAX_CHARACTERS;
45
+ const found = data.items ?? data.pages ?? [];
46
+ const pages = await Promise.all(
47
+ found.map(async ({ url = "", retrieve_id: retrieveId }) => {
48
+ if (!retrieveId) return { url, markdown: "", error: "No content is available for this page." };
49
+ try {
50
+ const content = await olostep.retrieve.get({ retrieveId, formats: [Format.MARKDOWN] });
51
+ return { url, markdown: truncate(content.markdown_content, max) };
52
+ } catch (error) {
53
+ return { url, markdown: "", error: error instanceof Error ? error.message : String(error) };
54
+ }
55
+ })
56
+ );
57
+ const result = { id, status: info.status ?? "completed", pages };
58
+ if (found.length === limit && data.cursor !== void 0 && data.cursor !== null) {
59
+ result.nextCursor = String(data.cursor);
60
+ result.message = "More pages are available. Use the results tool with this id and nextCursor.";
61
+ }
62
+ return result;
63
+ }
64
+ async function waitThenRead(options, kind, job) {
65
+ try {
66
+ await job.waitTillDone(waitOptions(options));
67
+ } catch (error) {
68
+ if (error instanceof Error && error.message.includes("timed out")) return stillRunning(job.id);
69
+ throw error;
70
+ }
71
+ return readPages(options, kind, job.id);
72
+ }
73
+ function parseAnswer(answer) {
74
+ if (!answer) return null;
75
+ try {
76
+ const parsed = JSON.parse(answer);
77
+ return parsed && typeof parsed === "object" && "result" in parsed ? parsed.result : parsed;
78
+ } catch {
79
+ return answer;
80
+ }
81
+ }
82
+ var olostepScrape = (options = {}) => tool({
83
+ description: "Scrape a web page and return its content as clean markdown. Handles JavaScript-rendered pages. Use when you need the full content of a specific URL.",
84
+ inputSchema: z.object({
85
+ url: z.string().url().describe("The URL of the page to scrape"),
86
+ waitBeforeScraping: z.number().int().min(0).max(1e4).optional().describe("Milliseconds to wait for JavaScript-heavy pages to render")
87
+ }),
88
+ execute: async ({ url, waitBeforeScraping }) => {
89
+ const result = await client(options).scrapes.create({
90
+ url,
91
+ formats: ["markdown"],
92
+ waitBeforeScraping
93
+ });
94
+ return {
95
+ url,
96
+ markdown: truncate(result.markdown_content, options.maxCharacters ?? DEFAULT_MAX_CHARACTERS)
97
+ };
98
+ }
99
+ });
100
+ var olostepSearch = (options = {}) => tool({
101
+ description: "Search the live web. Returns result titles, URLs and descriptions. Use to find current information or sources to scrape.",
102
+ inputSchema: z.object({
103
+ query: z.string().describe("The search query"),
104
+ limit: z.number().int().min(1).max(25).optional().describe("Maximum number of results, up to 25 (default 10)")
105
+ }),
106
+ execute: async ({ query, limit }) => {
107
+ const result = await client(options).searches.create({ query, limit });
108
+ return {
109
+ query,
110
+ results: result.links.map((link) => ({
111
+ title: link.title,
112
+ url: link.url,
113
+ description: link.description
114
+ }))
115
+ };
116
+ }
117
+ });
118
+ var olostepAnswer = (options = {}) => tool({
119
+ description: "Answer a question by searching and reading the live web. Returns an answer with source URLs. Use for factual or current-events questions that need citations.",
120
+ inputSchema: z.object({
121
+ task: z.string().describe("The question or research task")
122
+ }),
123
+ execute: async ({ task }) => {
124
+ const result = await client(options).answers.create({ task });
125
+ return { answer: parseAnswer(result.answer), sources: result.sources };
126
+ }
127
+ });
128
+ var olostepMap = (options = {}) => tool({
129
+ description: "Discover the URLs on a website. Optionally rank them by relevance to a search query. Use to find the right pages on a site before scraping them.",
130
+ inputSchema: z.object({
131
+ url: z.string().url().describe("The website to map"),
132
+ searchQuery: z.string().optional().describe("Rank URLs by relevance to this query"),
133
+ topN: z.number().int().min(1).max(MAX_JOB_SIZE).optional().describe("Maximum number of URLs to return, up to 100k")
134
+ }),
135
+ execute: async ({ url, searchQuery, topN }) => {
136
+ const result = await client(options).maps.create({ url, searchQuery, topN });
137
+ const urls = [];
138
+ for await (const found of result.urls()) urls.push(found);
139
+ return { url, urls };
140
+ }
141
+ });
142
+ var olostepCrawl = (options = {}) => tool({
143
+ description: "Crawl a website from a starting URL and return the markdown content of each page found. Use to read several pages of a site, such as documentation. Large crawls return an id or nextCursor: use the results tool to get the remaining pages.",
144
+ inputSchema: z.object({
145
+ url: z.string().url().describe("The URL to start crawling from"),
146
+ maxPages: z.number().int().min(1).max(MAX_JOB_SIZE).optional().describe("Maximum pages to crawl, up to 100k (default 5)"),
147
+ includeUrls: z.array(z.string()).optional().describe('Only crawl URLs matching these glob patterns, e.g. ["/docs/**"]')
148
+ }),
149
+ execute: async ({ url, maxPages, includeUrls }) => {
150
+ const olostep = client(options);
151
+ const crawl = await olostep.crawls.create({ url, maxPages: maxPages ?? 5, includeUrls });
152
+ return waitThenRead(options, "crawls", crawl);
153
+ }
154
+ });
155
+ var olostepBatch = (options = {}) => tool({
156
+ description: "Scrape up to 100k URLs in one batch and return the markdown of each page. Use when you already have a list of URLs to read; for one or two URLs use the scrape tool instead. Can take a minute or more. Large batches return an id or nextCursor: use the results tool to get the remaining pages.",
157
+ inputSchema: z.object({
158
+ urls: z.array(z.string().url()).min(1).max(MAX_JOB_SIZE).describe("The URLs to scrape (up to 100k)")
159
+ }),
160
+ execute: async ({ urls }) => {
161
+ const olostep = client(options);
162
+ const batch = await olostep.batches.create(urls);
163
+ return waitThenRead(options, "batches", batch);
164
+ }
165
+ });
166
+ var olostepResults = (options = {}) => tool({
167
+ description: "Get the markdown pages of a crawl or batch by its id (crawl_... or batch_...). Use when the crawl or batch tool returned status in_progress or a nextCursor.",
168
+ inputSchema: z.object({
169
+ id: z.string().describe("The crawl or batch id, starting with crawl_ or batch_"),
170
+ cursor: z.string().optional().describe("The nextCursor from a previous result. Leave empty for the first pages.")
171
+ }),
172
+ execute: async ({ id, cursor }) => {
173
+ const kind = id.startsWith("batch_") ? "batches" : id.startsWith("crawl_") ? "crawls" : null;
174
+ if (!kind) throw new Error(`Enter a crawl or batch id, for example crawl_abc123 or batch_abc123 (got ${id}).`);
175
+ return readPages(options, kind, id, cursor);
176
+ }
177
+ });
178
+ var olostepTools = (options = {}) => ({
179
+ scrape: olostepScrape(options),
180
+ search: olostepSearch(options),
181
+ answer: olostepAnswer(options),
182
+ map: olostepMap(options),
183
+ crawl: olostepCrawl(options),
184
+ batch: olostepBatch(options),
185
+ results: olostepResults(options)
186
+ });
187
+ export {
188
+ olostepAnswer,
189
+ olostepBatch,
190
+ olostepCrawl,
191
+ olostepMap,
192
+ olostepResults,
193
+ olostepScrape,
194
+ olostepSearch,
195
+ olostepTools
196
+ };
package/package.json CHANGED
@@ -1,6 +1,65 @@
1
1
  {
2
2
  "name": "@olostep/ai-sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "Olostep tools for the Vercel AI SDK. Live web search, scraping and crawling API for AI. Get structured, LLM-ready data from any site. Scale to 1M requests with 10 parallel batches at low cost.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsup src/index.ts --format esm --dts --clean",
22
+ "typecheck": "tsc --noEmit",
23
+ "test": "node --import tsx --test test/*.test.ts",
24
+ "prepublishOnly": "npm run build"
25
+ },
26
+ "keywords": [
27
+ "ai",
28
+ "ai-sdk",
29
+ "vercel",
30
+ "tools",
31
+ "agents",
32
+ "web-search",
33
+ "web-scraping",
34
+ "crawling",
35
+ "olostep"
36
+ ],
37
+ "homepage": "https://www.olostep.com",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/olostep-api/olostep-ai-sdk.git"
41
+ },
42
+ "bugs": {
43
+ "url": "https://github.com/olostep-api/olostep-ai-sdk/issues"
44
+ },
45
+ "author": "Olostep <info@olostep.com>",
46
+ "license": "MIT",
47
+ "engines": {
48
+ "node": ">=18"
49
+ },
50
+ "dependencies": {
51
+ "olostep": "^1.2.2"
52
+ },
53
+ "peerDependencies": {
54
+ "ai": "^5.0.0 || ^6.0.0 || ^7.0.0",
55
+ "zod": "^3.25.0 || ^4.0.0"
56
+ },
57
+ "devDependencies": {
58
+ "@types/node": "^22.0.0",
59
+ "ai": "^7.0.0",
60
+ "tsup": "^8.5.0",
61
+ "tsx": "^4.23.15",
62
+ "typescript": "^5.6.0",
63
+ "zod": "^4.0.0"
64
+ }
65
+ }