@search1api/ai-sdk 0.1.1 → 0.2.0
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/README.md +50 -14
- package/dist/index.cjs +117 -17
- package/dist/index.cjs.map +2 -2
- package/dist/index.d.ts +68 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +117 -17
- package/dist/index.js.map +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Search1API tools for the Vercel AI SDK
|
|
2
2
|
|
|
3
|
-
Add web search, news search,
|
|
4
|
-
use the official
|
|
5
|
-
for transport,
|
|
3
|
+
Add web search, news search, webpage reading, and agentic search to an AI SDK
|
|
4
|
+
agent. The tools use the official
|
|
5
|
+
[`@search1api/client`](https://s1.dev/docs/integrations/sdks) for transport,
|
|
6
|
+
retries, timeouts, and typed API errors.
|
|
6
7
|
|
|
7
8
|
## Install
|
|
8
9
|
|
|
@@ -32,23 +33,58 @@ const result = await generateText({
|
|
|
32
33
|
console.log(result.text);
|
|
33
34
|
```
|
|
34
35
|
|
|
35
|
-
The tool set has
|
|
36
|
+
The tool set has four entries:
|
|
36
37
|
|
|
37
38
|
| Tool | Model input | API | Output |
|
|
38
39
|
| --- | --- | --- | --- |
|
|
39
|
-
| `search` | `{ query }` | `/search` | Typed results with titles, links, and snippets |
|
|
40
|
-
| `news` | `
|
|
40
|
+
| `search` | `{ query, search_service?, page?, max_results?, include_sites?, exclude_sites?, time_range? }` | `/search` | Typed results with titles, links, and snippets |
|
|
41
|
+
| `news` | Same fields as `search` except `page`, with news engines | `/news` | Typed news results and source links |
|
|
41
42
|
| `crawl` | `{ url }` | `/crawl` | A page's title, URL, and readable content |
|
|
43
|
+
| `ask` | `{ query }` | `/ask` | Up to 10 results ranked by relevance, plus the engines and window used |
|
|
44
|
+
|
|
45
|
+
The model-facing parameters match the
|
|
46
|
+
[Search1API MCP server](https://s1.dev/docs/integrations/mcp). The model can
|
|
47
|
+
pick an engine (`google`, `bing`, `bingcn`, `duckduckgo`, `yahoo`, `yandex`,
|
|
48
|
+
`x`, `reddit`, `github`, `youtube`, `arxiv`, `wechat`, `bilibili`, `imdb`,
|
|
49
|
+
`wikipedia`, `grokipedia`; news supports `google`, `bing`, `duckduckgo`,
|
|
50
|
+
`yahoo`, and `hackernews`), return 1–50 results, request a later results page
|
|
51
|
+
(only `bing`, `bingcn`, `baidu`, and `grokipedia` paginate), scope or exclude
|
|
52
|
+
domains, and limit results to the past day, week, month, or year. Result page
|
|
53
|
+
crawling is not exposed to the model. Unknown arguments are ignored rather than
|
|
54
|
+
failing the call.
|
|
42
55
|
|
|
43
56
|
Outputs preserve the API response, including optional publication dates,
|
|
44
|
-
metadata, and content. Empty results remain
|
|
45
|
-
returned links; these tools do not add AI SDK
|
|
57
|
+
GitHub and Hacker News fields, metadata, and content. Empty results remain
|
|
58
|
+
empty. Ask the model to cite the returned links; these tools do not add AI SDK
|
|
59
|
+
source events automatically. When an answer uses `grokipedia` results, credit
|
|
60
|
+
them as "Powered by xAI"; see
|
|
61
|
+
[Improving search results](https://s1.dev/docs/guides/improving-search-results).
|
|
62
|
+
|
|
63
|
+
## Agentic search with ask
|
|
64
|
+
|
|
65
|
+
`ask` sends a natural-language request to
|
|
66
|
+
[`POST /ask`](https://s1.dev/docs/basic/ask). Search1API chooses up to five
|
|
67
|
+
engines and a time window, drops off-topic results, and returns at most 10
|
|
68
|
+
results ranked by relevance. Each call costs 5 credits, compared with 1 credit
|
|
69
|
+
for `search`. `ask` is in the default tool set, matching the MCP server; leave
|
|
70
|
+
it out with `only`, or create it on its own with `search1apiAsk()`:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { search1apiAsk, search1apiTools } from '@search1api/ai-sdk';
|
|
74
|
+
|
|
75
|
+
const withoutAsk = search1apiTools({ only: ['search', 'news', 'crawl'] });
|
|
76
|
+
const askOnly = { ask: search1apiAsk() };
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The SDK gives `ask` a 45-second timeout. `ask` is not available with
|
|
80
|
+
pay-per-request payments. Prefer `search` when the agent already knows the
|
|
81
|
+
engine and keywords.
|
|
46
82
|
|
|
47
83
|
## Configure one tool
|
|
48
84
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
85
|
+
Settings passed in code are defaults. When the model supplies the same
|
|
86
|
+
parameter, the model's value is used for that call; anything it omits falls
|
|
87
|
+
back to your settings.
|
|
52
88
|
|
|
53
89
|
```ts
|
|
54
90
|
import { search1apiSearch, search1apiCrawl } from '@search1api/ai-sdk';
|
|
@@ -72,8 +108,8 @@ const tools = search1apiTools({
|
|
|
72
108
|
});
|
|
73
109
|
```
|
|
74
110
|
|
|
75
|
-
Search and news default to
|
|
76
|
-
`search.crawlResults` or `news.crawlResults`
|
|
111
|
+
Search and news default to ten results with `crawlResults: 0`. Set
|
|
112
|
+
`search.crawlResults` or `news.crawlResults` in code when you want result page
|
|
77
113
|
content included. Each successful page retrieval incurs a separate crawl charge;
|
|
78
114
|
see [pricing](https://s1.dev/pricing).
|
|
79
115
|
|
|
@@ -132,7 +168,7 @@ To verify the current version from the public registry, run
|
|
|
132
168
|
`node scripts/verify-package.mjs --registry` after publication.
|
|
133
169
|
|
|
134
170
|
To validate a local build, create and install the tarball with
|
|
135
|
-
`npm pack` and `npm install /absolute/path/search1api-ai-sdk-0.
|
|
171
|
+
`npm pack` and `npm install /absolute/path/search1api-ai-sdk-0.2.0.tgz`.
|
|
136
172
|
Publication tracking is in [RELEASE.md](https://github.com/superagents-lab/search1api-ai-sdk/blob/main/RELEASE.md).
|
|
137
173
|
|
|
138
174
|
## License
|
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
search1apiAsk: () => search1apiAsk,
|
|
23
24
|
search1apiCrawl: () => search1apiCrawl,
|
|
24
25
|
search1apiNews: () => search1apiNews,
|
|
25
26
|
search1apiSearch: () => search1apiSearch,
|
|
@@ -29,15 +30,85 @@ module.exports = __toCommonJS(index_exports);
|
|
|
29
30
|
var import_client = require("@search1api/client");
|
|
30
31
|
var import_ai = require("ai");
|
|
31
32
|
var import_zod = require("zod");
|
|
32
|
-
var
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
var SEARCH_ENGINES = [
|
|
34
|
+
"google",
|
|
35
|
+
"bing",
|
|
36
|
+
"bingcn",
|
|
37
|
+
"duckduckgo",
|
|
38
|
+
"yahoo",
|
|
39
|
+
"yandex",
|
|
40
|
+
"x",
|
|
41
|
+
"reddit",
|
|
42
|
+
"github",
|
|
43
|
+
"youtube",
|
|
44
|
+
"arxiv",
|
|
45
|
+
"wechat",
|
|
46
|
+
"bilibili",
|
|
47
|
+
"imdb",
|
|
48
|
+
"wikipedia",
|
|
49
|
+
"grokipedia"
|
|
50
|
+
];
|
|
51
|
+
var NEWS_ENGINES = [
|
|
52
|
+
"google",
|
|
53
|
+
"bing",
|
|
54
|
+
"duckduckgo",
|
|
55
|
+
"yahoo",
|
|
56
|
+
"hackernews"
|
|
57
|
+
];
|
|
58
|
+
function queryFields(kind) {
|
|
59
|
+
const noun = kind === "search" ? "results" : "articles";
|
|
60
|
+
return {
|
|
61
|
+
query: import_zod.z.string().trim().min(1).describe(`Short, focused ${kind} query`),
|
|
62
|
+
max_results: import_zod.z.number().int().min(1).max(50).optional().describe(`Maximum number of ${noun} to return`),
|
|
63
|
+
include_sites: import_zod.z.array(import_zod.z.string()).optional().describe(
|
|
64
|
+
kind === "search" ? "Domains to include when the user explicitly scopes the search" : "News domains to include"
|
|
65
|
+
),
|
|
66
|
+
exclude_sites: import_zod.z.array(import_zod.z.string()).optional().describe(
|
|
67
|
+
kind === "search" ? "Domains to exclude from the search" : "News domains to exclude"
|
|
68
|
+
),
|
|
69
|
+
time_range: import_zod.z.enum(["day", "week", "month", "year"]).optional().describe(
|
|
70
|
+
kind === "search" ? "Optional recency window for time-sensitive searches" : "Optional recency window; use day for breaking news"
|
|
71
|
+
)
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
var searchSchema = import_zod.z.object({
|
|
75
|
+
...queryFields("search"),
|
|
76
|
+
search_service: import_zod.z.enum(SEARCH_ENGINES).optional().describe(
|
|
77
|
+
"Search engine to use; choose one only when it matches the user's source intent"
|
|
78
|
+
),
|
|
79
|
+
page: import_zod.z.number().int().min(1).max(100).optional().describe(
|
|
80
|
+
"Results page to fetch; only bing, bingcn, baidu, and grokipedia paginate, other engines ignore it"
|
|
81
|
+
)
|
|
82
|
+
});
|
|
83
|
+
var newsSchema = import_zod.z.object({
|
|
84
|
+
...queryFields("news"),
|
|
85
|
+
search_service: import_zod.z.enum(NEWS_ENGINES).optional().describe("News search engine to use")
|
|
86
|
+
});
|
|
35
87
|
var crawlSchema = import_zod.z.object({
|
|
36
88
|
url: import_zod.z.string().url().refine(
|
|
37
89
|
(url) => ["http:", "https:"].includes(new URL(url).protocol),
|
|
38
90
|
"Use an HTTP or HTTPS URL."
|
|
39
|
-
).describe("
|
|
40
|
-
})
|
|
91
|
+
).describe("Public HTTP or HTTPS URL to retrieve")
|
|
92
|
+
});
|
|
93
|
+
var askSchema = import_zod.z.object({
|
|
94
|
+
query: import_zod.z.string().trim().min(1).max(500).describe(
|
|
95
|
+
"Natural-language description of what to find, including any platform or time frame"
|
|
96
|
+
)
|
|
97
|
+
});
|
|
98
|
+
function requestOptions(defaults, input) {
|
|
99
|
+
const fromModel = {
|
|
100
|
+
searchService: input.search_service,
|
|
101
|
+
maxResults: input.max_results,
|
|
102
|
+
includeSites: input.include_sites,
|
|
103
|
+
excludeSites: input.exclude_sites,
|
|
104
|
+
timeRange: input.time_range,
|
|
105
|
+
page: input.page
|
|
106
|
+
};
|
|
107
|
+
const overrides = Object.fromEntries(
|
|
108
|
+
Object.entries(fromModel).filter(([, value]) => value !== void 0)
|
|
109
|
+
);
|
|
110
|
+
return { ...defaults, ...overrides };
|
|
111
|
+
}
|
|
41
112
|
function clientGetter(options) {
|
|
42
113
|
let client = options.client;
|
|
43
114
|
const { client: _client, ...settings } = options;
|
|
@@ -54,26 +125,40 @@ function clientGetter(options) {
|
|
|
54
125
|
};
|
|
55
126
|
}
|
|
56
127
|
function searchTool(getClient, settings = {}) {
|
|
57
|
-
const
|
|
128
|
+
const defaults = {
|
|
129
|
+
maxResults: 10,
|
|
130
|
+
crawlResults: 0,
|
|
131
|
+
...settings
|
|
132
|
+
};
|
|
58
133
|
return (0, import_ai.tool)({
|
|
59
|
-
description: "Search the public web
|
|
60
|
-
inputSchema:
|
|
134
|
+
description: "Search the live public web when the user needs current information, sources, or research. Returns titles, links, and snippets. Use crawl to read a result page in full.",
|
|
135
|
+
inputSchema: searchSchema,
|
|
61
136
|
execute: async (input, { abortSignal }) => {
|
|
62
137
|
abortSignal?.throwIfAborted();
|
|
63
|
-
const
|
|
64
|
-
return getClient().search(
|
|
138
|
+
const parsed = searchSchema.parse(input);
|
|
139
|
+
return getClient().search(
|
|
140
|
+
parsed.query,
|
|
141
|
+
requestOptions(defaults, parsed),
|
|
142
|
+
{ signal: abortSignal }
|
|
143
|
+
);
|
|
65
144
|
}
|
|
66
145
|
});
|
|
67
146
|
}
|
|
68
147
|
function newsTool(getClient, settings = {}) {
|
|
69
|
-
const
|
|
148
|
+
const defaults = {
|
|
149
|
+
maxResults: 10,
|
|
150
|
+
crawlResults: 0,
|
|
151
|
+
...settings
|
|
152
|
+
};
|
|
70
153
|
return (0, import_ai.tool)({
|
|
71
|
-
description: "Search news
|
|
72
|
-
inputSchema:
|
|
154
|
+
description: "Search current news when the user asks about recent events, announcements, or coverage. Returns article titles, links, and snippets. Use crawl to read an article in full.",
|
|
155
|
+
inputSchema: newsSchema,
|
|
73
156
|
execute: async (input, { abortSignal }) => {
|
|
74
157
|
abortSignal?.throwIfAborted();
|
|
75
|
-
const
|
|
76
|
-
return getClient().news(query,
|
|
158
|
+
const parsed = newsSchema.parse(input);
|
|
159
|
+
return getClient().news(parsed.query, requestOptions(defaults, parsed), {
|
|
160
|
+
signal: abortSignal
|
|
161
|
+
});
|
|
77
162
|
}
|
|
78
163
|
});
|
|
79
164
|
}
|
|
@@ -89,6 +174,17 @@ function crawlTool(getClient, settings = {}) {
|
|
|
89
174
|
}
|
|
90
175
|
});
|
|
91
176
|
}
|
|
177
|
+
function askTool(getClient) {
|
|
178
|
+
return (0, import_ai.tool)({
|
|
179
|
+
description: "Describe what you need in natural language; Search1API chooses up to five engines and a time window, then returns at most 10 results ranked by relevance, with the engines and window it used. Costs 5 credits per call. Use search instead when you already know the engine and keywords.",
|
|
180
|
+
inputSchema: askSchema,
|
|
181
|
+
execute: async (input, { abortSignal }) => {
|
|
182
|
+
abortSignal?.throwIfAborted();
|
|
183
|
+
const { query } = askSchema.parse(input);
|
|
184
|
+
return getClient().ask(query, { signal: abortSignal });
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
92
188
|
function search1apiSearch(options = {}) {
|
|
93
189
|
return searchTool(clientGetter(options), options.search);
|
|
94
190
|
}
|
|
@@ -98,14 +194,18 @@ function search1apiNews(options = {}) {
|
|
|
98
194
|
function search1apiCrawl(options = {}) {
|
|
99
195
|
return crawlTool(clientGetter(options), options.crawl);
|
|
100
196
|
}
|
|
197
|
+
function search1apiAsk(options = {}) {
|
|
198
|
+
return askTool(clientGetter(options));
|
|
199
|
+
}
|
|
101
200
|
function search1apiTools(options = {}) {
|
|
102
201
|
const getClient = clientGetter(options);
|
|
103
202
|
const tools = {
|
|
104
203
|
search: searchTool(getClient, options.search),
|
|
105
204
|
news: newsTool(getClient, options.news),
|
|
106
|
-
crawl: crawlTool(getClient, options.crawl)
|
|
205
|
+
crawl: crawlTool(getClient, options.crawl),
|
|
206
|
+
ask: askTool(getClient)
|
|
107
207
|
};
|
|
108
|
-
const names = options.only ?? ["search", "news", "crawl"];
|
|
208
|
+
const names = options.only ?? ["search", "news", "crawl", "ask"];
|
|
109
209
|
const selected = {};
|
|
110
210
|
for (const name of names) {
|
|
111
211
|
if (!Object.hasOwn(tools, name)) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["import { Search1API } from '@search1api/client';\nimport type {\n CrawlOptions,\n CrawlResponse,\n NewsOptions,\n NewsResponse,\n Search1APIOptions,\n SearchOptions,\n SearchResponse,\n} from '@search1api/client';\nimport { tool } from 'ai';\nimport type { Tool } from 'ai';\nimport { z } from 'zod';\n\nexport interface Search1APIToolOptions extends Search1APIOptions {\n /** Reuse an existing client. When supplied, other client options are ignored. */\n client?: Search1API;\n}\n\nexport interface Search1APISearchOptions extends Search1APIToolOptions {\n /** Application-owned search settings; the model only supplies the query. */\n search?: SearchOptions;\n}\n\nexport interface Search1APINewsOptions extends Search1APIToolOptions {\n /** Application-owned news settings; the model only supplies the query. */\n news?: NewsOptions;\n}\n\nexport interface Search1APICrawlOptions extends Search1APIToolOptions {\n crawl?: CrawlOptions;\n}\n\nexport type QueryInput = { query: string };\nexport type CrawlInput = { url: string };\n\nconst querySchema = z\n .object({\n query: z\n .string()\n .trim()\n .min(1)\n .describe('Search keywords or a specific question.'),\n })\n .strict();\n\nconst crawlSchema = z\n .object({\n url: z\n .string()\n .url()\n .refine(\n (url) => ['http:', 'https:'].includes(new URL(url).protocol),\n 'Use an HTTP or HTTPS URL.'\n )\n .describe('A public webpage URL, for example a link returned by search.'),\n })\n .strict();\n\ntype ClientGetter = () => Search1API;\n\nfunction clientGetter(options: Search1APIToolOptions): ClientGetter {\n let client = options.client;\n const { client: _client, ...settings } = options;\n return () => {\n if (!client) {\n const processLike = globalThis as typeof globalThis & {\n process?: { env?: Record<string, string | undefined> };\n };\n const env = processLike.process?.env;\n client = new Search1API({\n ...settings,\n apiKey:\n settings.apiKey || env?.SEARCH1API_API_KEY || env?.SEARCH1API_KEY,\n });\n }\n return client;\n };\n}\n\nfunction searchTool(\n getClient: ClientGetter,\n settings: SearchOptions = {}\n): Tool<QueryInput, SearchResponse> {\n const options = { maxResults: 5, crawlResults: 0, ...settings };\n return tool({\n description:\n 'Search the public web for current information and sources. Returns titles, links, and snippets. Use crawl to read a result page in full.',\n inputSchema: querySchema,\n execute: async (input, { abortSignal }) => {\n abortSignal?.throwIfAborted();\n const { query } = querySchema.parse(input);\n return getClient().search(query, options, { signal: abortSignal });\n },\n });\n}\n\nfunction newsTool(\n getClient: ClientGetter,\n settings: NewsOptions = {}\n): Tool<QueryInput, NewsResponse> {\n const options = { maxResults: 5, crawlResults: 0, ...settings };\n return tool({\n description:\n 'Search news coverage of recent events and announcements. Returns article titles, links, and snippets. Use crawl to read an article in full.',\n inputSchema: querySchema,\n execute: async (input, { abortSignal }) => {\n abortSignal?.throwIfAborted();\n const { query } = querySchema.parse(input);\n return getClient().news(query, options, { signal: abortSignal });\n },\n });\n}\n\nfunction crawlTool(\n getClient: ClientGetter,\n settings: CrawlOptions = {}\n): Tool<CrawlInput, CrawlResponse> {\n const options = { ...settings };\n return tool({\n description:\n 'Read one public webpage and return its title, source URL, and readable content. Use a URL supplied by the user or returned by search or news.',\n inputSchema: crawlSchema,\n execute: async (input, { abortSignal }) => {\n abortSignal?.throwIfAborted();\n const { url } = crawlSchema.parse(input);\n return getClient().crawl(url, options, { signal: abortSignal });\n },\n });\n}\n\nexport function search1apiSearch(\n options: Search1APISearchOptions = {}\n): Tool<QueryInput, SearchResponse> {\n return searchTool(clientGetter(options), options.search);\n}\n\nexport function search1apiNews(\n options: Search1APINewsOptions = {}\n): Tool<QueryInput, NewsResponse> {\n return newsTool(clientGetter(options), options.news);\n}\n\nexport function search1apiCrawl(\n options: Search1APICrawlOptions = {}\n): Tool<CrawlInput, CrawlResponse> {\n return crawlTool(clientGetter(options), options.crawl);\n}\n\nexport interface Search1APIToolSet {\n search: Tool<QueryInput, SearchResponse>;\n news: Tool<QueryInput, NewsResponse>;\n crawl: Tool<CrawlInput, CrawlResponse>;\n}\n\nexport type Search1APIToolName = keyof Search1APIToolSet;\n\nexport interface Search1APIToolsOptions<\n Name extends Search1APIToolName = Search1APIToolName,\n> extends Search1APIToolOptions {\n /** Include only these tools. By default all three are included. */\n only?: readonly Name[];\n search?: SearchOptions;\n news?: NewsOptions;\n crawl?: CrawlOptions;\n}\n\nexport function search1apiTools<\n Name extends Search1APIToolName = Search1APIToolName,\n>(options: Search1APIToolsOptions<Name> = {}): Pick<Search1APIToolSet, Name> {\n const getClient = clientGetter(options);\n const tools: Search1APIToolSet = {\n search: searchTool(getClient, options.search),\n news: newsTool(getClient, options.news),\n crawl: crawlTool(getClient, options.crawl),\n };\n const names = options.only ?? (['search', 'news', 'crawl'] as const);\n const selected = {} as Pick<Search1APIToolSet, Name>;\n for (const name of names) {\n if (!Object.hasOwn(tools, name)) {\n throw new Error(`Unknown Search1API tool: ${name}`);\n }\n Object.assign(selected, { [name]: tools[name] });\n }\n return selected;\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA2B;
|
|
4
|
+
"sourcesContent": ["import { Search1API } from '@search1api/client';\nimport type {\n AskResponse,\n CrawlOptions,\n CrawlResponse,\n NewsOptions,\n NewsResponse,\n Search1APIOptions,\n SearchOptions,\n SearchResponse,\n} from '@search1api/client';\nimport { tool } from 'ai';\nimport type { Tool } from 'ai';\nimport { z } from 'zod';\n\nexport interface Search1APIToolOptions extends Search1APIOptions {\n /** Reuse an existing client. When supplied, other client options are ignored. */\n client?: Search1API;\n}\n\nexport interface Search1APISearchOptions extends Search1APIToolOptions {\n /** Default search settings; values the model supplies take precedence. */\n search?: SearchOptions;\n}\n\nexport interface Search1APINewsOptions extends Search1APIToolOptions {\n /** Default news settings; values the model supplies take precedence. */\n news?: NewsOptions;\n}\n\nexport interface Search1APICrawlOptions extends Search1APIToolOptions {\n crawl?: CrawlOptions;\n}\n\nexport type CrawlInput = { url: string };\nexport type AskInput = { query: string };\n\n// Model-facing parameters mirror the Search1API MCP server's tool schemas,\n// except result crawling, which only application settings can enable.\nconst SEARCH_ENGINES = [\n 'google',\n 'bing',\n 'bingcn',\n 'duckduckgo',\n 'yahoo',\n 'yandex',\n 'x',\n 'reddit',\n 'github',\n 'youtube',\n 'arxiv',\n 'wechat',\n 'bilibili',\n 'imdb',\n 'wikipedia',\n 'grokipedia',\n] as const;\nconst NEWS_ENGINES = [\n 'google',\n 'bing',\n 'duckduckgo',\n 'yahoo',\n 'hackernews',\n] as const;\n\nfunction queryFields(kind: 'search' | 'news') {\n const noun = kind === 'search' ? 'results' : 'articles';\n return {\n query: z\n .string()\n .trim()\n .min(1)\n .describe(`Short, focused ${kind} query`),\n max_results: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(`Maximum number of ${noun} to return`),\n include_sites: z\n .array(z.string())\n .optional()\n .describe(\n kind === 'search'\n ? 'Domains to include when the user explicitly scopes the search'\n : 'News domains to include'\n ),\n exclude_sites: z\n .array(z.string())\n .optional()\n .describe(\n kind === 'search'\n ? 'Domains to exclude from the search'\n : 'News domains to exclude'\n ),\n time_range: z\n .enum(['day', 'week', 'month', 'year'])\n .optional()\n .describe(\n kind === 'search'\n ? 'Optional recency window for time-sensitive searches'\n : 'Optional recency window; use day for breaking news'\n ),\n };\n}\n\n// Unknown keys are stripped rather than rejected so a stray argument does not\n// cost the agent a retry step.\nconst searchSchema = z.object({\n ...queryFields('search'),\n search_service: z\n .enum(SEARCH_ENGINES)\n .optional()\n .describe(\n \"Search engine to use; choose one only when it matches the user's source intent\"\n ),\n page: z\n .number()\n .int()\n .min(1)\n .max(100)\n .optional()\n .describe(\n 'Results page to fetch; only bing, bingcn, baidu, and grokipedia paginate, other engines ignore it'\n ),\n});\n\nconst newsSchema = z.object({\n ...queryFields('news'),\n search_service: z\n .enum(NEWS_ENGINES)\n .optional()\n .describe('News search engine to use'),\n});\n\nconst crawlSchema = z.object({\n url: z\n .string()\n .url()\n .refine(\n (url) => ['http:', 'https:'].includes(new URL(url).protocol),\n 'Use an HTTP or HTTPS URL.'\n )\n .describe('Public HTTP or HTTPS URL to retrieve'),\n});\n\nconst askSchema = z.object({\n query: z\n .string()\n .trim()\n .min(1)\n .max(500)\n .describe(\n 'Natural-language description of what to find, including any platform or time frame'\n ),\n});\n\nexport type SearchInput = z.input<typeof searchSchema>;\nexport type NewsInput = z.input<typeof newsSchema>;\n\ntype ModelQueryInput =\n | z.output<typeof searchSchema>\n | (z.output<typeof newsSchema> & { page?: undefined });\n\n// Merge model-supplied values over application defaults, dropping omitted keys.\nfunction requestOptions<Options extends SearchOptions | NewsOptions>(\n defaults: Options,\n input: ModelQueryInput\n): Options {\n const fromModel = {\n searchService: input.search_service,\n maxResults: input.max_results,\n includeSites: input.include_sites,\n excludeSites: input.exclude_sites,\n timeRange: input.time_range,\n page: input.page,\n };\n const overrides = Object.fromEntries(\n Object.entries(fromModel).filter(([, value]) => value !== undefined)\n );\n return { ...defaults, ...overrides } as Options;\n}\n\ntype ClientGetter = () => Search1API;\n\nfunction clientGetter(options: Search1APIToolOptions): ClientGetter {\n let client = options.client;\n const { client: _client, ...settings } = options;\n return () => {\n if (!client) {\n const processLike = globalThis as typeof globalThis & {\n process?: { env?: Record<string, string | undefined> };\n };\n const env = processLike.process?.env;\n client = new Search1API({\n ...settings,\n apiKey:\n settings.apiKey || env?.SEARCH1API_API_KEY || env?.SEARCH1API_KEY,\n });\n }\n return client;\n };\n}\n\nfunction searchTool(\n getClient: ClientGetter,\n settings: SearchOptions = {}\n): Tool<SearchInput, SearchResponse> {\n const defaults: SearchOptions = {\n maxResults: 10,\n crawlResults: 0,\n ...settings,\n };\n return tool({\n description:\n 'Search the live public web when the user needs current information, sources, or research. Returns titles, links, and snippets. Use crawl to read a result page in full.',\n inputSchema: searchSchema,\n execute: async (input, { abortSignal }) => {\n abortSignal?.throwIfAborted();\n const parsed = searchSchema.parse(input);\n return getClient().search(\n parsed.query,\n requestOptions(defaults, parsed),\n { signal: abortSignal }\n );\n },\n });\n}\n\nfunction newsTool(\n getClient: ClientGetter,\n settings: NewsOptions = {}\n): Tool<NewsInput, NewsResponse> {\n const defaults: NewsOptions = {\n maxResults: 10,\n crawlResults: 0,\n ...settings,\n };\n return tool({\n description:\n 'Search current news when the user asks about recent events, announcements, or coverage. Returns article titles, links, and snippets. Use crawl to read an article in full.',\n inputSchema: newsSchema,\n execute: async (input, { abortSignal }) => {\n abortSignal?.throwIfAborted();\n const parsed = newsSchema.parse(input);\n return getClient().news(parsed.query, requestOptions(defaults, parsed), {\n signal: abortSignal,\n });\n },\n });\n}\n\nfunction crawlTool(\n getClient: ClientGetter,\n settings: CrawlOptions = {}\n): Tool<CrawlInput, CrawlResponse> {\n const options = { ...settings };\n return tool({\n description:\n 'Read one public webpage and return its title, source URL, and readable content. Use a URL supplied by the user or returned by search or news.',\n inputSchema: crawlSchema,\n execute: async (input, { abortSignal }) => {\n abortSignal?.throwIfAborted();\n const { url } = crawlSchema.parse(input);\n return getClient().crawl(url, options, { signal: abortSignal });\n },\n });\n}\n\nfunction askTool(getClient: ClientGetter): Tool<AskInput, AskResponse> {\n return tool({\n description:\n 'Describe what you need in natural language; Search1API chooses up to five engines and a time window, then returns at most 10 results ranked by relevance, with the engines and window it used. Costs 5 credits per call. Use search instead when you already know the engine and keywords.',\n inputSchema: askSchema,\n execute: async (input, { abortSignal }) => {\n abortSignal?.throwIfAborted();\n const { query } = askSchema.parse(input);\n return getClient().ask(query, { signal: abortSignal });\n },\n });\n}\n\nexport function search1apiSearch(\n options: Search1APISearchOptions = {}\n): Tool<SearchInput, SearchResponse> {\n return searchTool(clientGetter(options), options.search);\n}\n\nexport function search1apiNews(\n options: Search1APINewsOptions = {}\n): Tool<NewsInput, NewsResponse> {\n return newsTool(clientGetter(options), options.news);\n}\n\nexport function search1apiCrawl(\n options: Search1APICrawlOptions = {}\n): Tool<CrawlInput, CrawlResponse> {\n return crawlTool(clientGetter(options), options.crawl);\n}\n\n/** Agentic search through `POST /ask`. Costs 5 credits per call. */\nexport function search1apiAsk(\n options: Search1APIToolOptions = {}\n): Tool<AskInput, AskResponse> {\n return askTool(clientGetter(options));\n}\n\nexport interface Search1APIToolSet {\n search: Tool<SearchInput, SearchResponse>;\n news: Tool<NewsInput, NewsResponse>;\n crawl: Tool<CrawlInput, CrawlResponse>;\n ask: Tool<AskInput, AskResponse>;\n}\n\nexport type Search1APIToolName = keyof Search1APIToolSet;\n\nexport interface Search1APIToolsOptions<\n Name extends Search1APIToolName = Search1APIToolName,\n> extends Search1APIToolOptions {\n /** Include only these tools. By default all four are included. */\n only?: readonly Name[];\n search?: SearchOptions;\n news?: NewsOptions;\n crawl?: CrawlOptions;\n}\n\nexport function search1apiTools<\n Name extends Search1APIToolName = Search1APIToolName,\n>(options: Search1APIToolsOptions<Name> = {}): Pick<Search1APIToolSet, Name> {\n const getClient = clientGetter(options);\n const tools: Search1APIToolSet = {\n search: searchTool(getClient, options.search),\n news: newsTool(getClient, options.news),\n crawl: crawlTool(getClient, options.crawl),\n ask: askTool(getClient),\n };\n const names =\n options.only ?? (['search', 'news', 'crawl', 'ask'] as const);\n const selected = {} as Pick<Search1APIToolSet, Name>;\n for (const name of names) {\n if (!Object.hasOwn(tools, name)) {\n throw new Error(`Unknown Search1API tool: ${name}`);\n }\n Object.assign(selected, { [name]: tools[name] });\n }\n return selected;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,42 +1,99 @@
|
|
|
1
1
|
import { Search1API } from '@search1api/client';
|
|
2
|
-
import type { CrawlOptions, CrawlResponse, NewsOptions, NewsResponse, Search1APIOptions, SearchOptions, SearchResponse } from '@search1api/client';
|
|
2
|
+
import type { AskResponse, CrawlOptions, CrawlResponse, NewsOptions, NewsResponse, Search1APIOptions, SearchOptions, SearchResponse } from '@search1api/client';
|
|
3
3
|
import type { Tool } from 'ai';
|
|
4
|
+
import { z } from 'zod';
|
|
4
5
|
export interface Search1APIToolOptions extends Search1APIOptions {
|
|
5
6
|
/** Reuse an existing client. When supplied, other client options are ignored. */
|
|
6
7
|
client?: Search1API;
|
|
7
8
|
}
|
|
8
9
|
export interface Search1APISearchOptions extends Search1APIToolOptions {
|
|
9
|
-
/**
|
|
10
|
+
/** Default search settings; values the model supplies take precedence. */
|
|
10
11
|
search?: SearchOptions;
|
|
11
12
|
}
|
|
12
13
|
export interface Search1APINewsOptions extends Search1APIToolOptions {
|
|
13
|
-
/**
|
|
14
|
+
/** Default news settings; values the model supplies take precedence. */
|
|
14
15
|
news?: NewsOptions;
|
|
15
16
|
}
|
|
16
17
|
export interface Search1APICrawlOptions extends Search1APIToolOptions {
|
|
17
18
|
crawl?: CrawlOptions;
|
|
18
19
|
}
|
|
19
|
-
export type QueryInput = {
|
|
20
|
-
query: string;
|
|
21
|
-
};
|
|
22
20
|
export type CrawlInput = {
|
|
23
21
|
url: string;
|
|
24
22
|
};
|
|
25
|
-
export
|
|
26
|
-
|
|
23
|
+
export type AskInput = {
|
|
24
|
+
query: string;
|
|
25
|
+
};
|
|
26
|
+
declare const searchSchema: z.ZodObject<{
|
|
27
|
+
search_service: z.ZodOptional<z.ZodEnum<{
|
|
28
|
+
google: "google";
|
|
29
|
+
bing: "bing";
|
|
30
|
+
bingcn: "bingcn";
|
|
31
|
+
duckduckgo: "duckduckgo";
|
|
32
|
+
yahoo: "yahoo";
|
|
33
|
+
yandex: "yandex";
|
|
34
|
+
x: "x";
|
|
35
|
+
reddit: "reddit";
|
|
36
|
+
github: "github";
|
|
37
|
+
youtube: "youtube";
|
|
38
|
+
arxiv: "arxiv";
|
|
39
|
+
wechat: "wechat";
|
|
40
|
+
bilibili: "bilibili";
|
|
41
|
+
imdb: "imdb";
|
|
42
|
+
wikipedia: "wikipedia";
|
|
43
|
+
grokipedia: "grokipedia";
|
|
44
|
+
}>>;
|
|
45
|
+
page: z.ZodOptional<z.ZodNumber>;
|
|
46
|
+
query: z.ZodString;
|
|
47
|
+
max_results: z.ZodOptional<z.ZodNumber>;
|
|
48
|
+
include_sites: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
49
|
+
exclude_sites: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
50
|
+
time_range: z.ZodOptional<z.ZodEnum<{
|
|
51
|
+
day: "day";
|
|
52
|
+
week: "week";
|
|
53
|
+
month: "month";
|
|
54
|
+
year: "year";
|
|
55
|
+
}>>;
|
|
56
|
+
}, z.core.$strip>;
|
|
57
|
+
declare const newsSchema: z.ZodObject<{
|
|
58
|
+
search_service: z.ZodOptional<z.ZodEnum<{
|
|
59
|
+
google: "google";
|
|
60
|
+
bing: "bing";
|
|
61
|
+
duckduckgo: "duckduckgo";
|
|
62
|
+
yahoo: "yahoo";
|
|
63
|
+
hackernews: "hackernews";
|
|
64
|
+
}>>;
|
|
65
|
+
query: z.ZodString;
|
|
66
|
+
max_results: z.ZodOptional<z.ZodNumber>;
|
|
67
|
+
include_sites: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
68
|
+
exclude_sites: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
69
|
+
time_range: z.ZodOptional<z.ZodEnum<{
|
|
70
|
+
day: "day";
|
|
71
|
+
week: "week";
|
|
72
|
+
month: "month";
|
|
73
|
+
year: "year";
|
|
74
|
+
}>>;
|
|
75
|
+
}, z.core.$strip>;
|
|
76
|
+
export type SearchInput = z.input<typeof searchSchema>;
|
|
77
|
+
export type NewsInput = z.input<typeof newsSchema>;
|
|
78
|
+
export declare function search1apiSearch(options?: Search1APISearchOptions): Tool<SearchInput, SearchResponse>;
|
|
79
|
+
export declare function search1apiNews(options?: Search1APINewsOptions): Tool<NewsInput, NewsResponse>;
|
|
27
80
|
export declare function search1apiCrawl(options?: Search1APICrawlOptions): Tool<CrawlInput, CrawlResponse>;
|
|
81
|
+
/** Agentic search through `POST /ask`. Costs 5 credits per call. */
|
|
82
|
+
export declare function search1apiAsk(options?: Search1APIToolOptions): Tool<AskInput, AskResponse>;
|
|
28
83
|
export interface Search1APIToolSet {
|
|
29
|
-
search: Tool<
|
|
30
|
-
news: Tool<
|
|
84
|
+
search: Tool<SearchInput, SearchResponse>;
|
|
85
|
+
news: Tool<NewsInput, NewsResponse>;
|
|
31
86
|
crawl: Tool<CrawlInput, CrawlResponse>;
|
|
87
|
+
ask: Tool<AskInput, AskResponse>;
|
|
32
88
|
}
|
|
33
89
|
export type Search1APIToolName = keyof Search1APIToolSet;
|
|
34
90
|
export interface Search1APIToolsOptions<Name extends Search1APIToolName = Search1APIToolName> extends Search1APIToolOptions {
|
|
35
|
-
/** Include only these tools. By default all
|
|
91
|
+
/** Include only these tools. By default all four are included. */
|
|
36
92
|
only?: readonly Name[];
|
|
37
93
|
search?: SearchOptions;
|
|
38
94
|
news?: NewsOptions;
|
|
39
95
|
crawl?: CrawlOptions;
|
|
40
96
|
}
|
|
41
97
|
export declare function search1apiTools<Name extends Search1APIToolName = Search1APIToolName>(options?: Search1APIToolsOptions<Name>): Pick<Search1APIToolSet, Name>;
|
|
98
|
+
export {};
|
|
42
99
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,cAAc,EACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EACZ,aAAa,EACb,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,cAAc,EACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC9D,iFAAiF;IACjF,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,MAAM,WAAW,uBAAwB,SAAQ,qBAAqB;IACpE,0EAA0E;IAC1E,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,WAAW,qBAAsB,SAAQ,qBAAqB;IAClE,wEAAwE;IACxE,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AAED,MAAM,WAAW,sBAAuB,SAAQ,qBAAqB;IACnE,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,MAAM,UAAU,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AACzC,MAAM,MAAM,QAAQ,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AA0EzC,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiBhB,CAAC;AAEH,QAAA,MAAM,UAAU;;;;;;;;;;;;;;;;;;iBAMd,CAAC;AAwBH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AACvD,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AA4HnD,wBAAgB,gBAAgB,CAC9B,OAAO,GAAE,uBAA4B,GACpC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAEnC;AAED,wBAAgB,cAAc,CAC5B,OAAO,GAAE,qBAA0B,GAClC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAE/B;AAED,wBAAgB,eAAe,CAC7B,OAAO,GAAE,sBAA2B,GACnC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAEjC;AAED,oEAAoE;AACpE,wBAAgB,aAAa,CAC3B,OAAO,GAAE,qBAA0B,GAClC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAE7B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;IAC1C,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IACpC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IACvC,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;CAClC;AAED,MAAM,MAAM,kBAAkB,GAAG,MAAM,iBAAiB,CAAC;AAEzD,MAAM,WAAW,sBAAsB,CACrC,IAAI,SAAS,kBAAkB,GAAG,kBAAkB,CACpD,SAAQ,qBAAqB;IAC7B,kEAAkE;IAClE,IAAI,CAAC,EAAE,SAAS,IAAI,EAAE,CAAC;IACvB,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED,wBAAgB,eAAe,CAC7B,IAAI,SAAS,kBAAkB,GAAG,kBAAkB,EACpD,OAAO,GAAE,sBAAsB,CAAC,IAAI,CAAM,GAAG,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAkB3E"}
|
package/dist/index.js
CHANGED
|
@@ -2,15 +2,85 @@
|
|
|
2
2
|
import { Search1API } from "@search1api/client";
|
|
3
3
|
import { tool } from "ai";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
var SEARCH_ENGINES = [
|
|
6
|
+
"google",
|
|
7
|
+
"bing",
|
|
8
|
+
"bingcn",
|
|
9
|
+
"duckduckgo",
|
|
10
|
+
"yahoo",
|
|
11
|
+
"yandex",
|
|
12
|
+
"x",
|
|
13
|
+
"reddit",
|
|
14
|
+
"github",
|
|
15
|
+
"youtube",
|
|
16
|
+
"arxiv",
|
|
17
|
+
"wechat",
|
|
18
|
+
"bilibili",
|
|
19
|
+
"imdb",
|
|
20
|
+
"wikipedia",
|
|
21
|
+
"grokipedia"
|
|
22
|
+
];
|
|
23
|
+
var NEWS_ENGINES = [
|
|
24
|
+
"google",
|
|
25
|
+
"bing",
|
|
26
|
+
"duckduckgo",
|
|
27
|
+
"yahoo",
|
|
28
|
+
"hackernews"
|
|
29
|
+
];
|
|
30
|
+
function queryFields(kind) {
|
|
31
|
+
const noun = kind === "search" ? "results" : "articles";
|
|
32
|
+
return {
|
|
33
|
+
query: z.string().trim().min(1).describe(`Short, focused ${kind} query`),
|
|
34
|
+
max_results: z.number().int().min(1).max(50).optional().describe(`Maximum number of ${noun} to return`),
|
|
35
|
+
include_sites: z.array(z.string()).optional().describe(
|
|
36
|
+
kind === "search" ? "Domains to include when the user explicitly scopes the search" : "News domains to include"
|
|
37
|
+
),
|
|
38
|
+
exclude_sites: z.array(z.string()).optional().describe(
|
|
39
|
+
kind === "search" ? "Domains to exclude from the search" : "News domains to exclude"
|
|
40
|
+
),
|
|
41
|
+
time_range: z.enum(["day", "week", "month", "year"]).optional().describe(
|
|
42
|
+
kind === "search" ? "Optional recency window for time-sensitive searches" : "Optional recency window; use day for breaking news"
|
|
43
|
+
)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
var searchSchema = z.object({
|
|
47
|
+
...queryFields("search"),
|
|
48
|
+
search_service: z.enum(SEARCH_ENGINES).optional().describe(
|
|
49
|
+
"Search engine to use; choose one only when it matches the user's source intent"
|
|
50
|
+
),
|
|
51
|
+
page: z.number().int().min(1).max(100).optional().describe(
|
|
52
|
+
"Results page to fetch; only bing, bingcn, baidu, and grokipedia paginate, other engines ignore it"
|
|
53
|
+
)
|
|
54
|
+
});
|
|
55
|
+
var newsSchema = z.object({
|
|
56
|
+
...queryFields("news"),
|
|
57
|
+
search_service: z.enum(NEWS_ENGINES).optional().describe("News search engine to use")
|
|
58
|
+
});
|
|
8
59
|
var crawlSchema = z.object({
|
|
9
60
|
url: z.string().url().refine(
|
|
10
61
|
(url) => ["http:", "https:"].includes(new URL(url).protocol),
|
|
11
62
|
"Use an HTTP or HTTPS URL."
|
|
12
|
-
).describe("
|
|
13
|
-
})
|
|
63
|
+
).describe("Public HTTP or HTTPS URL to retrieve")
|
|
64
|
+
});
|
|
65
|
+
var askSchema = z.object({
|
|
66
|
+
query: z.string().trim().min(1).max(500).describe(
|
|
67
|
+
"Natural-language description of what to find, including any platform or time frame"
|
|
68
|
+
)
|
|
69
|
+
});
|
|
70
|
+
function requestOptions(defaults, input) {
|
|
71
|
+
const fromModel = {
|
|
72
|
+
searchService: input.search_service,
|
|
73
|
+
maxResults: input.max_results,
|
|
74
|
+
includeSites: input.include_sites,
|
|
75
|
+
excludeSites: input.exclude_sites,
|
|
76
|
+
timeRange: input.time_range,
|
|
77
|
+
page: input.page
|
|
78
|
+
};
|
|
79
|
+
const overrides = Object.fromEntries(
|
|
80
|
+
Object.entries(fromModel).filter(([, value]) => value !== void 0)
|
|
81
|
+
);
|
|
82
|
+
return { ...defaults, ...overrides };
|
|
83
|
+
}
|
|
14
84
|
function clientGetter(options) {
|
|
15
85
|
let client = options.client;
|
|
16
86
|
const { client: _client, ...settings } = options;
|
|
@@ -27,26 +97,40 @@ function clientGetter(options) {
|
|
|
27
97
|
};
|
|
28
98
|
}
|
|
29
99
|
function searchTool(getClient, settings = {}) {
|
|
30
|
-
const
|
|
100
|
+
const defaults = {
|
|
101
|
+
maxResults: 10,
|
|
102
|
+
crawlResults: 0,
|
|
103
|
+
...settings
|
|
104
|
+
};
|
|
31
105
|
return tool({
|
|
32
|
-
description: "Search the public web
|
|
33
|
-
inputSchema:
|
|
106
|
+
description: "Search the live public web when the user needs current information, sources, or research. Returns titles, links, and snippets. Use crawl to read a result page in full.",
|
|
107
|
+
inputSchema: searchSchema,
|
|
34
108
|
execute: async (input, { abortSignal }) => {
|
|
35
109
|
abortSignal?.throwIfAborted();
|
|
36
|
-
const
|
|
37
|
-
return getClient().search(
|
|
110
|
+
const parsed = searchSchema.parse(input);
|
|
111
|
+
return getClient().search(
|
|
112
|
+
parsed.query,
|
|
113
|
+
requestOptions(defaults, parsed),
|
|
114
|
+
{ signal: abortSignal }
|
|
115
|
+
);
|
|
38
116
|
}
|
|
39
117
|
});
|
|
40
118
|
}
|
|
41
119
|
function newsTool(getClient, settings = {}) {
|
|
42
|
-
const
|
|
120
|
+
const defaults = {
|
|
121
|
+
maxResults: 10,
|
|
122
|
+
crawlResults: 0,
|
|
123
|
+
...settings
|
|
124
|
+
};
|
|
43
125
|
return tool({
|
|
44
|
-
description: "Search news
|
|
45
|
-
inputSchema:
|
|
126
|
+
description: "Search current news when the user asks about recent events, announcements, or coverage. Returns article titles, links, and snippets. Use crawl to read an article in full.",
|
|
127
|
+
inputSchema: newsSchema,
|
|
46
128
|
execute: async (input, { abortSignal }) => {
|
|
47
129
|
abortSignal?.throwIfAborted();
|
|
48
|
-
const
|
|
49
|
-
return getClient().news(query,
|
|
130
|
+
const parsed = newsSchema.parse(input);
|
|
131
|
+
return getClient().news(parsed.query, requestOptions(defaults, parsed), {
|
|
132
|
+
signal: abortSignal
|
|
133
|
+
});
|
|
50
134
|
}
|
|
51
135
|
});
|
|
52
136
|
}
|
|
@@ -62,6 +146,17 @@ function crawlTool(getClient, settings = {}) {
|
|
|
62
146
|
}
|
|
63
147
|
});
|
|
64
148
|
}
|
|
149
|
+
function askTool(getClient) {
|
|
150
|
+
return tool({
|
|
151
|
+
description: "Describe what you need in natural language; Search1API chooses up to five engines and a time window, then returns at most 10 results ranked by relevance, with the engines and window it used. Costs 5 credits per call. Use search instead when you already know the engine and keywords.",
|
|
152
|
+
inputSchema: askSchema,
|
|
153
|
+
execute: async (input, { abortSignal }) => {
|
|
154
|
+
abortSignal?.throwIfAborted();
|
|
155
|
+
const { query } = askSchema.parse(input);
|
|
156
|
+
return getClient().ask(query, { signal: abortSignal });
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
65
160
|
function search1apiSearch(options = {}) {
|
|
66
161
|
return searchTool(clientGetter(options), options.search);
|
|
67
162
|
}
|
|
@@ -71,14 +166,18 @@ function search1apiNews(options = {}) {
|
|
|
71
166
|
function search1apiCrawl(options = {}) {
|
|
72
167
|
return crawlTool(clientGetter(options), options.crawl);
|
|
73
168
|
}
|
|
169
|
+
function search1apiAsk(options = {}) {
|
|
170
|
+
return askTool(clientGetter(options));
|
|
171
|
+
}
|
|
74
172
|
function search1apiTools(options = {}) {
|
|
75
173
|
const getClient = clientGetter(options);
|
|
76
174
|
const tools = {
|
|
77
175
|
search: searchTool(getClient, options.search),
|
|
78
176
|
news: newsTool(getClient, options.news),
|
|
79
|
-
crawl: crawlTool(getClient, options.crawl)
|
|
177
|
+
crawl: crawlTool(getClient, options.crawl),
|
|
178
|
+
ask: askTool(getClient)
|
|
80
179
|
};
|
|
81
|
-
const names = options.only ?? ["search", "news", "crawl"];
|
|
180
|
+
const names = options.only ?? ["search", "news", "crawl", "ask"];
|
|
82
181
|
const selected = {};
|
|
83
182
|
for (const name of names) {
|
|
84
183
|
if (!Object.hasOwn(tools, name)) {
|
|
@@ -89,6 +188,7 @@ function search1apiTools(options = {}) {
|
|
|
89
188
|
return selected;
|
|
90
189
|
}
|
|
91
190
|
export {
|
|
191
|
+
search1apiAsk,
|
|
92
192
|
search1apiCrawl,
|
|
93
193
|
search1apiNews,
|
|
94
194
|
search1apiSearch,
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["import { Search1API } from '@search1api/client';\nimport type {\n CrawlOptions,\n CrawlResponse,\n NewsOptions,\n NewsResponse,\n Search1APIOptions,\n SearchOptions,\n SearchResponse,\n} from '@search1api/client';\nimport { tool } from 'ai';\nimport type { Tool } from 'ai';\nimport { z } from 'zod';\n\nexport interface Search1APIToolOptions extends Search1APIOptions {\n /** Reuse an existing client. When supplied, other client options are ignored. */\n client?: Search1API;\n}\n\nexport interface Search1APISearchOptions extends Search1APIToolOptions {\n /** Application-owned search settings; the model only supplies the query. */\n search?: SearchOptions;\n}\n\nexport interface Search1APINewsOptions extends Search1APIToolOptions {\n /** Application-owned news settings; the model only supplies the query. */\n news?: NewsOptions;\n}\n\nexport interface Search1APICrawlOptions extends Search1APIToolOptions {\n crawl?: CrawlOptions;\n}\n\nexport type QueryInput = { query: string };\nexport type CrawlInput = { url: string };\n\nconst querySchema = z\n .object({\n query: z\n .string()\n .trim()\n .min(1)\n .describe('Search keywords or a specific question.'),\n })\n .strict();\n\nconst crawlSchema = z\n .object({\n url: z\n .string()\n .url()\n .refine(\n (url) => ['http:', 'https:'].includes(new URL(url).protocol),\n 'Use an HTTP or HTTPS URL.'\n )\n .describe('A public webpage URL, for example a link returned by search.'),\n })\n .strict();\n\ntype ClientGetter = () => Search1API;\n\nfunction clientGetter(options: Search1APIToolOptions): ClientGetter {\n let client = options.client;\n const { client: _client, ...settings } = options;\n return () => {\n if (!client) {\n const processLike = globalThis as typeof globalThis & {\n process?: { env?: Record<string, string | undefined> };\n };\n const env = processLike.process?.env;\n client = new Search1API({\n ...settings,\n apiKey:\n settings.apiKey || env?.SEARCH1API_API_KEY || env?.SEARCH1API_KEY,\n });\n }\n return client;\n };\n}\n\nfunction searchTool(\n getClient: ClientGetter,\n settings: SearchOptions = {}\n): Tool<QueryInput, SearchResponse> {\n const options = { maxResults: 5, crawlResults: 0, ...settings };\n return tool({\n description:\n 'Search the public web for current information and sources. Returns titles, links, and snippets. Use crawl to read a result page in full.',\n inputSchema: querySchema,\n execute: async (input, { abortSignal }) => {\n abortSignal?.throwIfAborted();\n const { query } = querySchema.parse(input);\n return getClient().search(query, options, { signal: abortSignal });\n },\n });\n}\n\nfunction newsTool(\n getClient: ClientGetter,\n settings: NewsOptions = {}\n): Tool<QueryInput, NewsResponse> {\n const options = { maxResults: 5, crawlResults: 0, ...settings };\n return tool({\n description:\n 'Search news coverage of recent events and announcements. Returns article titles, links, and snippets. Use crawl to read an article in full.',\n inputSchema: querySchema,\n execute: async (input, { abortSignal }) => {\n abortSignal?.throwIfAborted();\n const { query } = querySchema.parse(input);\n return getClient().news(query, options, { signal: abortSignal });\n },\n });\n}\n\nfunction crawlTool(\n getClient: ClientGetter,\n settings: CrawlOptions = {}\n): Tool<CrawlInput, CrawlResponse> {\n const options = { ...settings };\n return tool({\n description:\n 'Read one public webpage and return its title, source URL, and readable content. Use a URL supplied by the user or returned by search or news.',\n inputSchema: crawlSchema,\n execute: async (input, { abortSignal }) => {\n abortSignal?.throwIfAborted();\n const { url } = crawlSchema.parse(input);\n return getClient().crawl(url, options, { signal: abortSignal });\n },\n });\n}\n\nexport function search1apiSearch(\n options: Search1APISearchOptions = {}\n): Tool<QueryInput, SearchResponse> {\n return searchTool(clientGetter(options), options.search);\n}\n\nexport function search1apiNews(\n options: Search1APINewsOptions = {}\n): Tool<QueryInput, NewsResponse> {\n return newsTool(clientGetter(options), options.news);\n}\n\nexport function search1apiCrawl(\n options: Search1APICrawlOptions = {}\n): Tool<CrawlInput, CrawlResponse> {\n return crawlTool(clientGetter(options), options.crawl);\n}\n\nexport interface Search1APIToolSet {\n search: Tool<QueryInput, SearchResponse>;\n news: Tool<QueryInput, NewsResponse>;\n crawl: Tool<CrawlInput, CrawlResponse>;\n}\n\nexport type Search1APIToolName = keyof Search1APIToolSet;\n\nexport interface Search1APIToolsOptions<\n Name extends Search1APIToolName = Search1APIToolName,\n> extends Search1APIToolOptions {\n /** Include only these tools. By default all three are included. */\n only?: readonly Name[];\n search?: SearchOptions;\n news?: NewsOptions;\n crawl?: CrawlOptions;\n}\n\nexport function search1apiTools<\n Name extends Search1APIToolName = Search1APIToolName,\n>(options: Search1APIToolsOptions<Name> = {}): Pick<Search1APIToolSet, Name> {\n const getClient = clientGetter(options);\n const tools: Search1APIToolSet = {\n search: searchTool(getClient, options.search),\n news: newsTool(getClient, options.news),\n crawl: crawlTool(getClient, options.crawl),\n };\n const names = options.only ?? (['search', 'news', 'crawl'] as const);\n const selected = {} as Pick<Search1APIToolSet, Name>;\n for (const name of names) {\n if (!Object.hasOwn(tools, name)) {\n throw new Error(`Unknown Search1API tool: ${name}`);\n }\n Object.assign(selected, { [name]: tools[name] });\n }\n return selected;\n}\n"],
|
|
5
|
-
"mappings": ";AAAA,SAAS,kBAAkB;
|
|
4
|
+
"sourcesContent": ["import { Search1API } from '@search1api/client';\nimport type {\n AskResponse,\n CrawlOptions,\n CrawlResponse,\n NewsOptions,\n NewsResponse,\n Search1APIOptions,\n SearchOptions,\n SearchResponse,\n} from '@search1api/client';\nimport { tool } from 'ai';\nimport type { Tool } from 'ai';\nimport { z } from 'zod';\n\nexport interface Search1APIToolOptions extends Search1APIOptions {\n /** Reuse an existing client. When supplied, other client options are ignored. */\n client?: Search1API;\n}\n\nexport interface Search1APISearchOptions extends Search1APIToolOptions {\n /** Default search settings; values the model supplies take precedence. */\n search?: SearchOptions;\n}\n\nexport interface Search1APINewsOptions extends Search1APIToolOptions {\n /** Default news settings; values the model supplies take precedence. */\n news?: NewsOptions;\n}\n\nexport interface Search1APICrawlOptions extends Search1APIToolOptions {\n crawl?: CrawlOptions;\n}\n\nexport type CrawlInput = { url: string };\nexport type AskInput = { query: string };\n\n// Model-facing parameters mirror the Search1API MCP server's tool schemas,\n// except result crawling, which only application settings can enable.\nconst SEARCH_ENGINES = [\n 'google',\n 'bing',\n 'bingcn',\n 'duckduckgo',\n 'yahoo',\n 'yandex',\n 'x',\n 'reddit',\n 'github',\n 'youtube',\n 'arxiv',\n 'wechat',\n 'bilibili',\n 'imdb',\n 'wikipedia',\n 'grokipedia',\n] as const;\nconst NEWS_ENGINES = [\n 'google',\n 'bing',\n 'duckduckgo',\n 'yahoo',\n 'hackernews',\n] as const;\n\nfunction queryFields(kind: 'search' | 'news') {\n const noun = kind === 'search' ? 'results' : 'articles';\n return {\n query: z\n .string()\n .trim()\n .min(1)\n .describe(`Short, focused ${kind} query`),\n max_results: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(`Maximum number of ${noun} to return`),\n include_sites: z\n .array(z.string())\n .optional()\n .describe(\n kind === 'search'\n ? 'Domains to include when the user explicitly scopes the search'\n : 'News domains to include'\n ),\n exclude_sites: z\n .array(z.string())\n .optional()\n .describe(\n kind === 'search'\n ? 'Domains to exclude from the search'\n : 'News domains to exclude'\n ),\n time_range: z\n .enum(['day', 'week', 'month', 'year'])\n .optional()\n .describe(\n kind === 'search'\n ? 'Optional recency window for time-sensitive searches'\n : 'Optional recency window; use day for breaking news'\n ),\n };\n}\n\n// Unknown keys are stripped rather than rejected so a stray argument does not\n// cost the agent a retry step.\nconst searchSchema = z.object({\n ...queryFields('search'),\n search_service: z\n .enum(SEARCH_ENGINES)\n .optional()\n .describe(\n \"Search engine to use; choose one only when it matches the user's source intent\"\n ),\n page: z\n .number()\n .int()\n .min(1)\n .max(100)\n .optional()\n .describe(\n 'Results page to fetch; only bing, bingcn, baidu, and grokipedia paginate, other engines ignore it'\n ),\n});\n\nconst newsSchema = z.object({\n ...queryFields('news'),\n search_service: z\n .enum(NEWS_ENGINES)\n .optional()\n .describe('News search engine to use'),\n});\n\nconst crawlSchema = z.object({\n url: z\n .string()\n .url()\n .refine(\n (url) => ['http:', 'https:'].includes(new URL(url).protocol),\n 'Use an HTTP or HTTPS URL.'\n )\n .describe('Public HTTP or HTTPS URL to retrieve'),\n});\n\nconst askSchema = z.object({\n query: z\n .string()\n .trim()\n .min(1)\n .max(500)\n .describe(\n 'Natural-language description of what to find, including any platform or time frame'\n ),\n});\n\nexport type SearchInput = z.input<typeof searchSchema>;\nexport type NewsInput = z.input<typeof newsSchema>;\n\ntype ModelQueryInput =\n | z.output<typeof searchSchema>\n | (z.output<typeof newsSchema> & { page?: undefined });\n\n// Merge model-supplied values over application defaults, dropping omitted keys.\nfunction requestOptions<Options extends SearchOptions | NewsOptions>(\n defaults: Options,\n input: ModelQueryInput\n): Options {\n const fromModel = {\n searchService: input.search_service,\n maxResults: input.max_results,\n includeSites: input.include_sites,\n excludeSites: input.exclude_sites,\n timeRange: input.time_range,\n page: input.page,\n };\n const overrides = Object.fromEntries(\n Object.entries(fromModel).filter(([, value]) => value !== undefined)\n );\n return { ...defaults, ...overrides } as Options;\n}\n\ntype ClientGetter = () => Search1API;\n\nfunction clientGetter(options: Search1APIToolOptions): ClientGetter {\n let client = options.client;\n const { client: _client, ...settings } = options;\n return () => {\n if (!client) {\n const processLike = globalThis as typeof globalThis & {\n process?: { env?: Record<string, string | undefined> };\n };\n const env = processLike.process?.env;\n client = new Search1API({\n ...settings,\n apiKey:\n settings.apiKey || env?.SEARCH1API_API_KEY || env?.SEARCH1API_KEY,\n });\n }\n return client;\n };\n}\n\nfunction searchTool(\n getClient: ClientGetter,\n settings: SearchOptions = {}\n): Tool<SearchInput, SearchResponse> {\n const defaults: SearchOptions = {\n maxResults: 10,\n crawlResults: 0,\n ...settings,\n };\n return tool({\n description:\n 'Search the live public web when the user needs current information, sources, or research. Returns titles, links, and snippets. Use crawl to read a result page in full.',\n inputSchema: searchSchema,\n execute: async (input, { abortSignal }) => {\n abortSignal?.throwIfAborted();\n const parsed = searchSchema.parse(input);\n return getClient().search(\n parsed.query,\n requestOptions(defaults, parsed),\n { signal: abortSignal }\n );\n },\n });\n}\n\nfunction newsTool(\n getClient: ClientGetter,\n settings: NewsOptions = {}\n): Tool<NewsInput, NewsResponse> {\n const defaults: NewsOptions = {\n maxResults: 10,\n crawlResults: 0,\n ...settings,\n };\n return tool({\n description:\n 'Search current news when the user asks about recent events, announcements, or coverage. Returns article titles, links, and snippets. Use crawl to read an article in full.',\n inputSchema: newsSchema,\n execute: async (input, { abortSignal }) => {\n abortSignal?.throwIfAborted();\n const parsed = newsSchema.parse(input);\n return getClient().news(parsed.query, requestOptions(defaults, parsed), {\n signal: abortSignal,\n });\n },\n });\n}\n\nfunction crawlTool(\n getClient: ClientGetter,\n settings: CrawlOptions = {}\n): Tool<CrawlInput, CrawlResponse> {\n const options = { ...settings };\n return tool({\n description:\n 'Read one public webpage and return its title, source URL, and readable content. Use a URL supplied by the user or returned by search or news.',\n inputSchema: crawlSchema,\n execute: async (input, { abortSignal }) => {\n abortSignal?.throwIfAborted();\n const { url } = crawlSchema.parse(input);\n return getClient().crawl(url, options, { signal: abortSignal });\n },\n });\n}\n\nfunction askTool(getClient: ClientGetter): Tool<AskInput, AskResponse> {\n return tool({\n description:\n 'Describe what you need in natural language; Search1API chooses up to five engines and a time window, then returns at most 10 results ranked by relevance, with the engines and window it used. Costs 5 credits per call. Use search instead when you already know the engine and keywords.',\n inputSchema: askSchema,\n execute: async (input, { abortSignal }) => {\n abortSignal?.throwIfAborted();\n const { query } = askSchema.parse(input);\n return getClient().ask(query, { signal: abortSignal });\n },\n });\n}\n\nexport function search1apiSearch(\n options: Search1APISearchOptions = {}\n): Tool<SearchInput, SearchResponse> {\n return searchTool(clientGetter(options), options.search);\n}\n\nexport function search1apiNews(\n options: Search1APINewsOptions = {}\n): Tool<NewsInput, NewsResponse> {\n return newsTool(clientGetter(options), options.news);\n}\n\nexport function search1apiCrawl(\n options: Search1APICrawlOptions = {}\n): Tool<CrawlInput, CrawlResponse> {\n return crawlTool(clientGetter(options), options.crawl);\n}\n\n/** Agentic search through `POST /ask`. Costs 5 credits per call. */\nexport function search1apiAsk(\n options: Search1APIToolOptions = {}\n): Tool<AskInput, AskResponse> {\n return askTool(clientGetter(options));\n}\n\nexport interface Search1APIToolSet {\n search: Tool<SearchInput, SearchResponse>;\n news: Tool<NewsInput, NewsResponse>;\n crawl: Tool<CrawlInput, CrawlResponse>;\n ask: Tool<AskInput, AskResponse>;\n}\n\nexport type Search1APIToolName = keyof Search1APIToolSet;\n\nexport interface Search1APIToolsOptions<\n Name extends Search1APIToolName = Search1APIToolName,\n> extends Search1APIToolOptions {\n /** Include only these tools. By default all four are included. */\n only?: readonly Name[];\n search?: SearchOptions;\n news?: NewsOptions;\n crawl?: CrawlOptions;\n}\n\nexport function search1apiTools<\n Name extends Search1APIToolName = Search1APIToolName,\n>(options: Search1APIToolsOptions<Name> = {}): Pick<Search1APIToolSet, Name> {\n const getClient = clientGetter(options);\n const tools: Search1APIToolSet = {\n search: searchTool(getClient, options.search),\n news: newsTool(getClient, options.news),\n crawl: crawlTool(getClient, options.crawl),\n ask: askTool(getClient),\n };\n const names =\n options.only ?? (['search', 'news', 'crawl', 'ask'] as const);\n const selected = {} as Pick<Search1APIToolSet, Name>;\n for (const name of names) {\n if (!Object.hasOwn(tools, name)) {\n throw new Error(`Unknown Search1API tool: ${name}`);\n }\n Object.assign(selected, { [name]: tools[name] });\n }\n return selected;\n}\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,kBAAkB;AAW3B,SAAS,YAAY;AAErB,SAAS,SAAS;AA0BlB,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,IAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,YAAY,MAAyB;AAC5C,QAAM,OAAO,SAAS,WAAW,YAAY;AAC7C,SAAO;AAAA,IACL,OAAO,EACJ,OAAO,EACP,KAAK,EACL,IAAI,CAAC,EACL,SAAS,kBAAkB,IAAI,QAAQ;AAAA,IAC1C,aAAa,EACV,OAAO,EACP,IAAI,EACJ,IAAI,CAAC,EACL,IAAI,EAAE,EACN,SAAS,EACT,SAAS,qBAAqB,IAAI,YAAY;AAAA,IACjD,eAAe,EACZ,MAAM,EAAE,OAAO,CAAC,EAChB,SAAS,EACT;AAAA,MACC,SAAS,WACL,kEACA;AAAA,IACN;AAAA,IACF,eAAe,EACZ,MAAM,EAAE,OAAO,CAAC,EAChB,SAAS,EACT;AAAA,MACC,SAAS,WACL,uCACA;AAAA,IACN;AAAA,IACF,YAAY,EACT,KAAK,CAAC,OAAO,QAAQ,SAAS,MAAM,CAAC,EACrC,SAAS,EACT;AAAA,MACC,SAAS,WACL,wDACA;AAAA,IACN;AAAA,EACJ;AACF;AAIA,IAAM,eAAe,EAAE,OAAO;AAAA,EAC5B,GAAG,YAAY,QAAQ;AAAA,EACvB,gBAAgB,EACb,KAAK,cAAc,EACnB,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA,EACF,MAAM,EACH,OAAO,EACP,IAAI,EACJ,IAAI,CAAC,EACL,IAAI,GAAG,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AACJ,CAAC;AAED,IAAM,aAAa,EAAE,OAAO;AAAA,EAC1B,GAAG,YAAY,MAAM;AAAA,EACrB,gBAAgB,EACb,KAAK,YAAY,EACjB,SAAS,EACT,SAAS,2BAA2B;AACzC,CAAC;AAED,IAAM,cAAc,EAAE,OAAO;AAAA,EAC3B,KAAK,EACF,OAAO,EACP,IAAI,EACJ;AAAA,IACC,CAAC,QAAQ,CAAC,SAAS,QAAQ,EAAE,SAAS,IAAI,IAAI,GAAG,EAAE,QAAQ;AAAA,IAC3D;AAAA,EACF,EACC,SAAS,sCAAsC;AACpD,CAAC;AAED,IAAM,YAAY,EAAE,OAAO;AAAA,EACzB,OAAO,EACJ,OAAO,EACP,KAAK,EACL,IAAI,CAAC,EACL,IAAI,GAAG,EACP;AAAA,IACC;AAAA,EACF;AACJ,CAAC;AAUD,SAAS,eACP,UACA,OACS;AACT,QAAM,YAAY;AAAA,IAChB,eAAe,MAAM;AAAA,IACrB,YAAY,MAAM;AAAA,IAClB,cAAc,MAAM;AAAA,IACpB,cAAc,MAAM;AAAA,IACpB,WAAW,MAAM;AAAA,IACjB,MAAM,MAAM;AAAA,EACd;AACA,QAAM,YAAY,OAAO;AAAA,IACvB,OAAO,QAAQ,SAAS,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,UAAU,MAAS;AAAA,EACrE;AACA,SAAO,EAAE,GAAG,UAAU,GAAG,UAAU;AACrC;AAIA,SAAS,aAAa,SAA8C;AAClE,MAAI,SAAS,QAAQ;AACrB,QAAM,EAAE,QAAQ,SAAS,GAAG,SAAS,IAAI;AACzC,SAAO,MAAM;AACX,QAAI,CAAC,QAAQ;AACX,YAAM,cAAc;AAGpB,YAAM,MAAM,YAAY,SAAS;AACjC,eAAS,IAAI,WAAW;AAAA,QACtB,GAAG;AAAA,QACH,QACE,SAAS,UAAU,KAAK,sBAAsB,KAAK;AAAA,MACvD,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AACF;AAEA,SAAS,WACP,WACA,WAA0B,CAAC,GACQ;AACnC,QAAM,WAA0B;AAAA,IAC9B,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,GAAG;AAAA,EACL;AACA,SAAO,KAAK;AAAA,IACV,aACE;AAAA,IACF,aAAa;AAAA,IACb,SAAS,OAAO,OAAO,EAAE,YAAY,MAAM;AACzC,mBAAa,eAAe;AAC5B,YAAM,SAAS,aAAa,MAAM,KAAK;AACvC,aAAO,UAAU,EAAE;AAAA,QACjB,OAAO;AAAA,QACP,eAAe,UAAU,MAAM;AAAA,QAC/B,EAAE,QAAQ,YAAY;AAAA,MACxB;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,SAAS,SACP,WACA,WAAwB,CAAC,GACM;AAC/B,QAAM,WAAwB;AAAA,IAC5B,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,GAAG;AAAA,EACL;AACA,SAAO,KAAK;AAAA,IACV,aACE;AAAA,IACF,aAAa;AAAA,IACb,SAAS,OAAO,OAAO,EAAE,YAAY,MAAM;AACzC,mBAAa,eAAe;AAC5B,YAAM,SAAS,WAAW,MAAM,KAAK;AACrC,aAAO,UAAU,EAAE,KAAK,OAAO,OAAO,eAAe,UAAU,MAAM,GAAG;AAAA,QACtE,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACH;AAEA,SAAS,UACP,WACA,WAAyB,CAAC,GACO;AACjC,QAAM,UAAU,EAAE,GAAG,SAAS;AAC9B,SAAO,KAAK;AAAA,IACV,aACE;AAAA,IACF,aAAa;AAAA,IACb,SAAS,OAAO,OAAO,EAAE,YAAY,MAAM;AACzC,mBAAa,eAAe;AAC5B,YAAM,EAAE,IAAI,IAAI,YAAY,MAAM,KAAK;AACvC,aAAO,UAAU,EAAE,MAAM,KAAK,SAAS,EAAE,QAAQ,YAAY,CAAC;AAAA,IAChE;AAAA,EACF,CAAC;AACH;AAEA,SAAS,QAAQ,WAAsD;AACrE,SAAO,KAAK;AAAA,IACV,aACE;AAAA,IACF,aAAa;AAAA,IACb,SAAS,OAAO,OAAO,EAAE,YAAY,MAAM;AACzC,mBAAa,eAAe;AAC5B,YAAM,EAAE,MAAM,IAAI,UAAU,MAAM,KAAK;AACvC,aAAO,UAAU,EAAE,IAAI,OAAO,EAAE,QAAQ,YAAY,CAAC;AAAA,IACvD;AAAA,EACF,CAAC;AACH;AAEO,SAAS,iBACd,UAAmC,CAAC,GACD;AACnC,SAAO,WAAW,aAAa,OAAO,GAAG,QAAQ,MAAM;AACzD;AAEO,SAAS,eACd,UAAiC,CAAC,GACH;AAC/B,SAAO,SAAS,aAAa,OAAO,GAAG,QAAQ,IAAI;AACrD;AAEO,SAAS,gBACd,UAAkC,CAAC,GACF;AACjC,SAAO,UAAU,aAAa,OAAO,GAAG,QAAQ,KAAK;AACvD;AAGO,SAAS,cACd,UAAiC,CAAC,GACL;AAC7B,SAAO,QAAQ,aAAa,OAAO,CAAC;AACtC;AAqBO,SAAS,gBAEd,UAAwC,CAAC,GAAkC;AAC3E,QAAM,YAAY,aAAa,OAAO;AACtC,QAAM,QAA2B;AAAA,IAC/B,QAAQ,WAAW,WAAW,QAAQ,MAAM;AAAA,IAC5C,MAAM,SAAS,WAAW,QAAQ,IAAI;AAAA,IACtC,OAAO,UAAU,WAAW,QAAQ,KAAK;AAAA,IACzC,KAAK,QAAQ,SAAS;AAAA,EACxB;AACA,QAAM,QACJ,QAAQ,QAAS,CAAC,UAAU,QAAQ,SAAS,KAAK;AACpD,QAAM,WAAW,CAAC;AAClB,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,OAAO,OAAO,OAAO,IAAI,GAAG;AAC/B,YAAM,IAAI,MAAM,4BAA4B,IAAI,EAAE;AAAA,IACpD;AACA,WAAO,OAAO,UAAU,EAAE,CAAC,IAAI,GAAG,MAAM,IAAI,EAAE,CAAC;AAAA,EACjD;AACA,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@search1api/ai-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Official Search1API web search, news, and page reading tools for the Vercel AI SDK.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"prepack": "npm run build"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@search1api/client": "^0.
|
|
56
|
+
"@search1api/client": "^0.3.0"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"ai": "^5.0.0 || ^6.0.0 || ^7.0.0",
|