pi-web-providers 2.3.0 → 2.4.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.
Files changed (3) hide show
  1. package/README.md +33 -0
  2. package/dist/index.js +329 -69
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -13,6 +13,9 @@ off entirely.
13
13
 
14
14
  ## ✨ Features
15
15
 
16
+ - **Multiple providers**: Claude, Cloudflare, Codex, Exa, Firecrawl,
17
+ Gemini, Linkup, OpenAI, Perplexity, Parallel, Serper,
18
+ [Tavily](https://tavily.com), Valyu
16
19
  - **Provider-aware tool options**: pi only exposes the provider settings that
17
20
  actually apply to the backend you selected, so tool calls are easier to
18
21
  discover and harder to get wrong
@@ -61,6 +64,7 @@ Each tool can be routed to any compatible provider:
61
64
  | **OpenAI** | ✔ | | ✔ | ✔ | `OPENAI_API_KEY` |
62
65
  | **Parallel** | ✔ | ✔ | | | `PARALLEL_API_KEY` |
63
66
  | **Perplexity** | ✔ | | ✔ | ✔ | `PERPLEXITY_API_KEY` |
67
+ | **Serper** | ✔ | | | | `SERPER_API_KEY` |
64
68
  | **Tavily** | ✔ | ✔ | | | `TAVILY_API_KEY` |
65
69
  | **Valyu** | ✔ | ✔ | ✔ | ✔ | `VALYU_API_KEY` |
66
70
 
@@ -377,6 +381,35 @@ call.
377
381
 
378
382
  </details>
379
383
 
384
+ <details>
385
+ <summary><strong>Serper</strong></summary>
386
+
387
+ - API: Serper HTTP API
388
+ - Supports `web_search` via Serper's Google search endpoint
389
+ - Good fit for fast, straightforward Google-style organic search results
390
+ - Exposes search options `gl`, `hl`, `location`, `page`, and `autocorrect`
391
+ - Preserves rich metadata from Serper responses, including ranking position,
392
+ sitelinks, attributes, and top-level response context such as
393
+ `knowledgeGraph`, `answerBox`, `peopleAlsoAsk`, and `relatedSearches`
394
+ - Optional `baseUrl` overrides are supported for proxies and testing
395
+
396
+ Minimal config:
397
+
398
+ ```json
399
+ {
400
+ "tools": {
401
+ "search": "serper"
402
+ },
403
+ "providers": {
404
+ "serper": {
405
+ "apiKey": "SERPER_API_KEY"
406
+ }
407
+ }
408
+ }
409
+ ```
410
+
411
+ </details>
412
+
380
413
  <details>
381
414
  <summary><strong>Tavily</strong></summary>
382
415
 
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  visibleWidth,
24
24
  wrapTextWithAnsi
25
25
  } from "@mariozechner/pi-tui";
26
- import { Type as Type15 } from "@sinclair/typebox";
26
+ import { Type as Type16 } from "@sinclair/typebox";
27
27
 
28
28
  // src/config.ts
29
29
  import { execSync } from "node:child_process";
@@ -44,6 +44,7 @@ var PROVIDER_TOOLS_BY_ID = {
44
44
  openai: ["search", "answer", "research"],
45
45
  parallel: ["search", "contents"],
46
46
  perplexity: ["search", "answer", "research"],
47
+ serper: ["search"],
47
48
  tavily: ["search", "contents"],
48
49
  valyu: ["search", "contents", "answer", "research"]
49
50
  };
@@ -90,6 +91,7 @@ var PROVIDER_IDS = [
90
91
  "openai",
91
92
  "parallel",
92
93
  "perplexity",
94
+ "serper",
93
95
  "tavily",
94
96
  "valyu"
95
97
  ];
@@ -289,6 +291,29 @@ function normalizeProvider(providerId, raw, source) {
289
291
  case "linkup":
290
292
  case "parallel":
291
293
  case "perplexity":
294
+ return parseProviderWithShape(
295
+ raw,
296
+ source,
297
+ providerId,
298
+ {
299
+ apiKey: readOptionalString,
300
+ baseUrl: readOptionalString,
301
+ options: readOptionalObject,
302
+ settings: parseOptionalExecutionSettings
303
+ }
304
+ );
305
+ case "serper":
306
+ return parseProviderWithShape(raw, source, providerId, {
307
+ apiKey: readOptionalString,
308
+ baseUrl: readOptionalString,
309
+ options: (value, innerSource, field) => parseOptionalCapabilityOptions(
310
+ value,
311
+ innerSource,
312
+ field,
313
+ ["search"]
314
+ ),
315
+ settings: parseOptionalExecutionSettings
316
+ });
292
317
  case "tavily":
293
318
  return parseProviderWithShape(raw, source, providerId, {
294
319
  apiKey: readOptionalString,
@@ -5107,56 +5132,287 @@ function buildRequestOptions4(context) {
5107
5132
  return context.signal ? { signal: context.signal } : void 0;
5108
5133
  }
5109
5134
 
5110
- // src/providers/tavily.ts
5135
+ // src/providers/serper.ts
5111
5136
  import { Type as Type13 } from "@sinclair/typebox";
5137
+ var DEFAULT_BASE_URL = "https://google.serper.dev";
5138
+ var serperSearchOptionsSchema = Type13.Object(
5139
+ {
5140
+ gl: Type13.Optional(
5141
+ Type13.String({
5142
+ description: "Country code hint for Google results (for example 'us')."
5143
+ })
5144
+ ),
5145
+ hl: Type13.Optional(
5146
+ Type13.String({
5147
+ description: "Language code hint for Google results (for example 'en')."
5148
+ })
5149
+ ),
5150
+ location: Type13.Optional(
5151
+ Type13.String({
5152
+ description: "Geographic location hint for Google results."
5153
+ })
5154
+ ),
5155
+ page: Type13.Optional(
5156
+ Type13.Integer({
5157
+ minimum: 1,
5158
+ description: "1-based results page to request from Serper."
5159
+ })
5160
+ ),
5161
+ autocorrect: Type13.Optional(
5162
+ Type13.Boolean({
5163
+ description: "Enable or disable Serper query autocorrection."
5164
+ })
5165
+ )
5166
+ },
5167
+ { description: "Serper search options." }
5168
+ );
5169
+ var serperAdapter = {
5170
+ id: "serper",
5171
+ label: "Serper",
5172
+ docsUrl: "https://serper.dev/",
5173
+ tools: ["search"],
5174
+ getToolOptionsSchema(capability) {
5175
+ switch (capability) {
5176
+ case "search":
5177
+ return serperSearchOptionsSchema;
5178
+ default:
5179
+ return void 0;
5180
+ }
5181
+ },
5182
+ createTemplate() {
5183
+ return {
5184
+ apiKey: "SERPER_API_KEY",
5185
+ options: {}
5186
+ };
5187
+ },
5188
+ getConfigForCapability(capability, config) {
5189
+ switch (capability) {
5190
+ case "search":
5191
+ return {
5192
+ apiKey: config.apiKey,
5193
+ baseUrl: config.baseUrl,
5194
+ options: config.options?.search,
5195
+ settings: config.settings
5196
+ };
5197
+ default:
5198
+ return config;
5199
+ }
5200
+ },
5201
+ getCapabilityStatus(config) {
5202
+ return getApiKeyStatus(config?.apiKey);
5203
+ },
5204
+ buildPlan(request, config) {
5205
+ return buildProviderPlan({
5206
+ request,
5207
+ config,
5208
+ providerId: serperAdapter.id,
5209
+ providerLabel: serperAdapter.label,
5210
+ handlers: {
5211
+ search: {
5212
+ execute: (searchRequest, providerConfig, context) => serperAdapter.search(
5213
+ searchRequest.query,
5214
+ searchRequest.maxResults,
5215
+ providerConfig,
5216
+ context,
5217
+ searchRequest.options
5218
+ )
5219
+ }
5220
+ }
5221
+ });
5222
+ },
5223
+ async search(query2, maxResults, config, context, options) {
5224
+ const apiKey = resolveConfigValue(config.apiKey);
5225
+ if (!apiKey) {
5226
+ throw new Error("is missing an API key");
5227
+ }
5228
+ const defaults = stripLocalExecutionOptions(asJsonObject(config.options?.search)) ?? {};
5229
+ const runtimeOptions = stripLocalExecutionOptions(asJsonObject(options));
5230
+ const {
5231
+ q: _ignoredQuery,
5232
+ num: _ignoredNum,
5233
+ ...providerOptions
5234
+ } = {
5235
+ ...defaults,
5236
+ ...runtimeOptions ?? {}
5237
+ };
5238
+ const response = await fetch(joinUrl(resolveConfigValue(config.baseUrl)), {
5239
+ method: "POST",
5240
+ headers: {
5241
+ "content-type": "application/json",
5242
+ "x-api-key": apiKey
5243
+ },
5244
+ body: JSON.stringify({
5245
+ q: query2,
5246
+ num: clampMaxResults(maxResults),
5247
+ ...providerOptions
5248
+ }),
5249
+ signal: context.signal
5250
+ });
5251
+ if (!response.ok) {
5252
+ throw new Error(await buildHttpError(response));
5253
+ }
5254
+ const payload = await response.json();
5255
+ const responseRecord = asRecord3(payload) ?? {};
5256
+ const organic = asArray(responseRecord.organic) ?? [];
5257
+ const searchContext = buildSearchContext(responseRecord);
5258
+ return {
5259
+ provider: serperAdapter.id,
5260
+ results: organic.map((entry) => toSearchResult3(entry, searchContext)).filter(
5261
+ (result) => result !== null
5262
+ ).slice(0, clampMaxResults(maxResults))
5263
+ };
5264
+ }
5265
+ };
5266
+ function joinUrl(baseUrl) {
5267
+ const base = (baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
5268
+ return `${base}/search`;
5269
+ }
5270
+ function clampMaxResults(value) {
5271
+ return Math.max(1, Math.min(20, Math.trunc(value || 0)));
5272
+ }
5273
+ async function buildHttpError(response) {
5274
+ const detail = await readErrorDetail(response);
5275
+ const status = `${response.status}${response.statusText ? ` ${response.statusText}` : ""}`;
5276
+ return detail ? `Serper API request failed (${status}): ${detail}` : `Serper API request failed (${status}).`;
5277
+ }
5278
+ async function readErrorDetail(response) {
5279
+ const text = (await response.text()).trim();
5280
+ if (!text) {
5281
+ return void 0;
5282
+ }
5283
+ try {
5284
+ const parsed = JSON.parse(text);
5285
+ const record = asRecord3(parsed);
5286
+ const detail = readString4(record?.message) ?? readString4(record?.error) ?? readString4(record?.detail);
5287
+ if (detail) {
5288
+ return detail;
5289
+ }
5290
+ return JSON.stringify(parsed);
5291
+ } catch {
5292
+ return text;
5293
+ }
5294
+ }
5295
+ function toSearchResult3(entry, searchContext) {
5296
+ const record = asRecord3(entry);
5297
+ if (!record) {
5298
+ return null;
5299
+ }
5300
+ const url = readString4(record.link) ?? "";
5301
+ const title = readString4(record.title) || url || "Untitled";
5302
+ const snippet = trimSnippet(
5303
+ readString4(record.snippet) ?? readString4(record.richSnippet) ?? readString4(record.date) ?? ""
5304
+ );
5305
+ const metadata = omitUndefined({
5306
+ source: "organic",
5307
+ position: readNumber(record.position),
5308
+ date: readString4(record.date),
5309
+ attributes: asRecord3(record.attributes),
5310
+ sitelinks: asArray(record.sitelinks),
5311
+ rating: readNumber(record.rating),
5312
+ ratingCount: readNumber(record.ratingCount),
5313
+ cid: readString4(record.cid),
5314
+ ...extractExtraMetadata(record, ["title", "link", "snippet"]),
5315
+ ...searchContext ? { searchContext } : {}
5316
+ });
5317
+ return {
5318
+ title,
5319
+ url,
5320
+ snippet,
5321
+ ...Object.keys(metadata).length > 0 ? { metadata } : {}
5322
+ };
5323
+ }
5324
+ function buildSearchContext(response) {
5325
+ const context = omitUndefined({
5326
+ searchParameters: asRecord3(response.searchParameters),
5327
+ searchInformation: asRecord3(response.searchInformation),
5328
+ credits: readNumber(response.credits),
5329
+ answerBox: asRecord3(response.answerBox),
5330
+ knowledgeGraph: asRecord3(response.knowledgeGraph),
5331
+ peopleAlsoAsk: asArray(response.peopleAlsoAsk),
5332
+ relatedSearches: asArray(response.relatedSearches),
5333
+ topStories: asArray(response.topStories),
5334
+ news: asArray(response.news),
5335
+ images: asArray(response.images),
5336
+ videos: asArray(response.videos),
5337
+ places: asArray(response.places)
5338
+ });
5339
+ return Object.keys(context).length > 0 ? context : void 0;
5340
+ }
5341
+ function extractExtraMetadata(record, ignoredKeys) {
5342
+ return Object.fromEntries(
5343
+ Object.entries(record).filter(
5344
+ ([key, value]) => !ignoredKeys.includes(key) && value !== void 0
5345
+ )
5346
+ );
5347
+ }
5348
+ function omitUndefined(value) {
5349
+ return Object.fromEntries(
5350
+ Object.entries(value).filter(([, entry]) => entry !== void 0)
5351
+ );
5352
+ }
5353
+ function asRecord3(value) {
5354
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
5355
+ }
5356
+ function asArray(value) {
5357
+ return Array.isArray(value) ? value : void 0;
5358
+ }
5359
+ function readString4(value) {
5360
+ return typeof value === "string" ? value : void 0;
5361
+ }
5362
+ function readNumber(value) {
5363
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
5364
+ }
5365
+
5366
+ // src/providers/tavily.ts
5367
+ import { Type as Type14 } from "@sinclair/typebox";
5112
5368
  import {
5113
5369
  tavily
5114
5370
  } from "@tavily/core";
5115
- var tavilySearchOptionsSchema = Type13.Object(
5371
+ var tavilySearchOptionsSchema = Type14.Object(
5116
5372
  {
5117
- topic: Type13.Optional(
5373
+ topic: Type14.Optional(
5118
5374
  literalUnion(["general", "news", "finance"], {
5119
5375
  description: "Category of the search query."
5120
5376
  })
5121
5377
  ),
5122
- searchDepth: Type13.Optional(
5378
+ searchDepth: Type14.Optional(
5123
5379
  literalUnion(["basic", "advanced"], {
5124
5380
  description: "Depth of the search. 'advanced' is slower but more thorough."
5125
5381
  })
5126
5382
  ),
5127
- timeRange: Type13.Optional(
5128
- Type13.String({ description: "Named time range filter." })
5383
+ timeRange: Type14.Optional(
5384
+ Type14.String({ description: "Named time range filter." })
5129
5385
  ),
5130
- country: Type13.Optional(
5131
- Type13.String({ description: "Country hint for search results." })
5386
+ country: Type14.Optional(
5387
+ Type14.String({ description: "Country hint for search results." })
5132
5388
  ),
5133
- exactMatch: Type13.Optional(
5134
- Type13.Boolean({ description: "Prefer exact matches." })
5389
+ exactMatch: Type14.Optional(
5390
+ Type14.Boolean({ description: "Prefer exact matches." })
5135
5391
  ),
5136
- includeAnswer: Type13.Optional(
5137
- Type13.Boolean({ description: "Include a short AI-generated answer." })
5392
+ includeAnswer: Type14.Optional(
5393
+ Type14.Boolean({ description: "Include a short AI-generated answer." })
5138
5394
  ),
5139
- includeRawContent: Type13.Optional(
5140
- Type13.Boolean({ description: "Include raw page content in results." })
5395
+ includeRawContent: Type14.Optional(
5396
+ Type14.Boolean({ description: "Include raw page content in results." })
5141
5397
  ),
5142
- includeImages: Type13.Optional(
5143
- Type13.Boolean({ description: "Include related images." })
5398
+ includeImages: Type14.Optional(
5399
+ Type14.Boolean({ description: "Include related images." })
5144
5400
  ),
5145
- includeFavicon: Type13.Optional(
5146
- Type13.Boolean({ description: "Include favicon URLs." })
5401
+ includeFavicon: Type14.Optional(
5402
+ Type14.Boolean({ description: "Include favicon URLs." })
5147
5403
  ),
5148
- includeDomains: Type13.Optional(
5149
- Type13.Array(Type13.String(), {
5404
+ includeDomains: Type14.Optional(
5405
+ Type14.Array(Type14.String(), {
5150
5406
  description: "Restrict results to these domains."
5151
5407
  })
5152
5408
  ),
5153
- excludeDomains: Type13.Optional(
5154
- Type13.Array(Type13.String(), {
5409
+ excludeDomains: Type14.Optional(
5410
+ Type14.Array(Type14.String(), {
5155
5411
  description: "Exclude these domains from results."
5156
5412
  })
5157
5413
  ),
5158
- days: Type13.Optional(
5159
- Type13.Integer({
5414
+ days: Type14.Optional(
5415
+ Type14.Integer({
5160
5416
  minimum: 1,
5161
5417
  description: "Limit results to the last N days."
5162
5418
  })
@@ -5164,27 +5420,27 @@ var tavilySearchOptionsSchema = Type13.Object(
5164
5420
  },
5165
5421
  { description: "Tavily search options." }
5166
5422
  );
5167
- var tavilyExtractOptionsSchema = Type13.Object(
5423
+ var tavilyExtractOptionsSchema = Type14.Object(
5168
5424
  {
5169
- extractDepth: Type13.Optional(
5170
- Type13.String({ description: "Depth setting for extraction." })
5425
+ extractDepth: Type14.Optional(
5426
+ Type14.String({ description: "Depth setting for extraction." })
5171
5427
  ),
5172
- format: Type13.Optional(
5428
+ format: Type14.Optional(
5173
5429
  literalUnion(["markdown", "text"], {
5174
5430
  description: "Output format for extracted content."
5175
5431
  })
5176
5432
  ),
5177
- includeImages: Type13.Optional(
5178
- Type13.Boolean({ description: "Include extracted images." })
5433
+ includeImages: Type14.Optional(
5434
+ Type14.Boolean({ description: "Include extracted images." })
5179
5435
  ),
5180
- query: Type13.Optional(
5181
- Type13.String({ description: "Optional query to focus extraction." })
5436
+ query: Type14.Optional(
5437
+ Type14.String({ description: "Optional query to focus extraction." })
5182
5438
  ),
5183
- chunksPerSource: Type13.Optional(
5184
- Type13.Integer({ minimum: 1, description: "Maximum chunks per source." })
5439
+ chunksPerSource: Type14.Optional(
5440
+ Type14.Integer({ minimum: 1, description: "Maximum chunks per source." })
5185
5441
  ),
5186
- includeFavicon: Type13.Optional(
5187
- Type13.Boolean({ description: "Include favicon URLs." })
5442
+ includeFavicon: Type14.Optional(
5443
+ Type14.Boolean({ description: "Include favicon URLs." })
5188
5444
  )
5189
5445
  },
5190
5446
  { description: "Tavily extract options." }
@@ -5358,48 +5614,48 @@ function buildExtractMetadata(response, result) {
5358
5614
  }
5359
5615
 
5360
5616
  // src/providers/valyu.ts
5361
- import { Type as Type14 } from "@sinclair/typebox";
5617
+ import { Type as Type15 } from "@sinclair/typebox";
5362
5618
  import { Valyu as ValyuClient } from "valyu-js";
5363
- var valyuSearchOptionsSchema = Type14.Object(
5619
+ var valyuSearchOptionsSchema = Type15.Object(
5364
5620
  {
5365
- searchType: Type14.Optional(
5621
+ searchType: Type15.Optional(
5366
5622
  literalUnion(["all", "web", "proprietary", "news"], {
5367
5623
  description: "Valyu search type."
5368
5624
  })
5369
5625
  ),
5370
- responseLength: Type14.Optional(
5626
+ responseLength: Type15.Optional(
5371
5627
  literalUnion(["short", "medium", "large", "max"], {
5372
5628
  description: "Response length."
5373
5629
  })
5374
5630
  ),
5375
- countryCode: Type14.Optional(
5376
- Type14.String({ description: "Country code to scope search results." })
5631
+ countryCode: Type15.Optional(
5632
+ Type15.String({ description: "Country code to scope search results." })
5377
5633
  )
5378
5634
  },
5379
5635
  { description: "Valyu search options." }
5380
5636
  );
5381
- var valyuAnswerOptionsSchema = Type14.Object(
5637
+ var valyuAnswerOptionsSchema = Type15.Object(
5382
5638
  {
5383
- responseLength: Type14.Optional(
5639
+ responseLength: Type15.Optional(
5384
5640
  literalUnion(["short", "medium", "large", "max"], {
5385
5641
  description: "Response length for answers."
5386
5642
  })
5387
5643
  ),
5388
- countryCode: Type14.Optional(
5389
- Type14.String({ description: "Country code to scope answer results." })
5644
+ countryCode: Type15.Optional(
5645
+ Type15.String({ description: "Country code to scope answer results." })
5390
5646
  )
5391
5647
  },
5392
5648
  { description: "Valyu answer options." }
5393
5649
  );
5394
- var valyuResearchOptionsSchema = Type14.Object(
5650
+ var valyuResearchOptionsSchema = Type15.Object(
5395
5651
  {
5396
- responseLength: Type14.Optional(
5652
+ responseLength: Type15.Optional(
5397
5653
  literalUnion(["short", "medium", "large", "max"], {
5398
5654
  description: "Response length for research."
5399
5655
  })
5400
5656
  ),
5401
- countryCode: Type14.Optional(
5402
- Type14.String({ description: "Country code to scope research results." })
5657
+ countryCode: Type15.Optional(
5658
+ Type15.String({ description: "Country code to scope research results." })
5403
5659
  )
5404
5660
  },
5405
5661
  { description: "Valyu research options." }
@@ -5684,6 +5940,7 @@ var ADAPTERS_BY_ID = {
5684
5940
  openai: openaiAdapter,
5685
5941
  parallel: parallelAdapter,
5686
5942
  perplexity: perplexityAdapter,
5943
+ serper: serperAdapter,
5687
5944
  tavily: tavilyAdapter,
5688
5945
  valyu: valyuAdapter
5689
5946
  };
@@ -6780,7 +7037,7 @@ var PROVIDER_CONFIG_MANIFESTS = {
6780
7037
  "deep-reasoning",
6781
7038
  "deep-max"
6782
7039
  ],
6783
- getValue: (config) => readString4(getExaSearchOptions(config)?.type) ?? "default",
7040
+ getValue: (config) => readString5(getExaSearchOptions(config)?.type) ?? "default",
6784
7041
  setValue: (config, value) => {
6785
7042
  const options = ensureExaSearchOptions(config);
6786
7043
  if (value === "default") {
@@ -7004,7 +7261,7 @@ var PROVIDER_CONFIG_MANIFESTS = {
7004
7261
  label: "Search mode",
7005
7262
  help: "Parallel search mode. 'default' uses the SDK default.",
7006
7263
  values: ["default", "agentic", "one-shot"],
7007
- getValue: (config) => readString4(getParallelOptions(config)?.search?.mode) ?? "default",
7264
+ getValue: (config) => readString5(getParallelOptions(config)?.search?.mode) ?? "default",
7008
7265
  setValue: (config, value) => {
7009
7266
  const options = ensureParallelOptions(config);
7010
7267
  options.search = asJsonObject2(options.search) ?? {};
@@ -7058,6 +7315,9 @@ var PROVIDER_CONFIG_MANIFESTS = {
7058
7315
  })
7059
7316
  ]
7060
7317
  },
7318
+ serper: {
7319
+ settings: [apiKeySetting(), baseUrlSetting()]
7320
+ },
7061
7321
  tavily: {
7062
7322
  settings: [apiKeySetting(), baseUrlSetting()]
7063
7323
  },
@@ -7070,7 +7330,7 @@ var PROVIDER_CONFIG_MANIFESTS = {
7070
7330
  label: "Search type",
7071
7331
  help: "Valyu search type. 'default' uses the SDK default.",
7072
7332
  values: ["default", "all", "web", "proprietary", "news"],
7073
- getValue: (config) => readString4(getValyuCapabilityOptions(config, "search")?.searchType) ?? "default",
7333
+ getValue: (config) => readString5(getValyuCapabilityOptions(config, "search")?.searchType) ?? "default",
7074
7334
  setValue: (config, value) => {
7075
7335
  const options = ensureValyuCapabilityOptions(config, "search");
7076
7336
  if (value === "default") {
@@ -7086,7 +7346,7 @@ var PROVIDER_CONFIG_MANIFESTS = {
7086
7346
  label: "Search response length",
7087
7347
  help: "Valyu search response length. 'default' uses the SDK default.",
7088
7348
  values: ["default", "short", "medium", "large", "max"],
7089
- getValue: (config) => readString4(
7349
+ getValue: (config) => readString5(
7090
7350
  getValyuCapabilityOptions(config, "search")?.responseLength
7091
7351
  ) ?? "default",
7092
7352
  setValue: (config, value) => {
@@ -7098,7 +7358,7 @@ var PROVIDER_CONFIG_MANIFESTS = {
7098
7358
  label: "Answer response length",
7099
7359
  help: "Valyu answer response length. 'default' uses the SDK default.",
7100
7360
  values: ["default", "short", "medium", "large", "max"],
7101
- getValue: (config) => readString4(
7361
+ getValue: (config) => readString5(
7102
7362
  getValyuCapabilityOptions(config, "answer")?.responseLength
7103
7363
  ) ?? "default",
7104
7364
  setValue: (config, value) => {
@@ -7110,7 +7370,7 @@ var PROVIDER_CONFIG_MANIFESTS = {
7110
7370
  label: "Research response length",
7111
7371
  help: "Valyu research response length. 'default' uses the SDK default.",
7112
7372
  values: ["default", "short", "medium", "large", "max"],
7113
- getValue: (config) => readString4(
7373
+ getValue: (config) => readString5(
7114
7374
  getValyuCapabilityOptions(config, "research")?.responseLength
7115
7375
  ) ?? "default",
7116
7376
  setValue: (config, value) => {
@@ -7202,7 +7462,7 @@ function getIntegerString(value) {
7202
7462
  function getBooleanValue(value) {
7203
7463
  return typeof value === "boolean" ? String(value) : "default";
7204
7464
  }
7205
- function readString4(value) {
7465
+ function readString5(value) {
7206
7466
  return typeof value === "string" ? value : void 0;
7207
7467
  }
7208
7468
  function asJsonObject2(value) {
@@ -7577,14 +7837,14 @@ function registerWebSearchTool(pi, providerIds) {
7577
7837
  promptGuidelines: [
7578
7838
  "Batch related searches when grouped comparison matters; use separate sibling web_search calls when independent results should surface as soon as they are ready."
7579
7839
  ],
7580
- parameters: Type15.Object({
7581
- queries: Type15.Array(Type15.String({ minLength: 1 }), {
7840
+ parameters: Type16.Object({
7841
+ queries: Type16.Array(Type16.String({ minLength: 1 }), {
7582
7842
  minItems: 1,
7583
7843
  maxItems: MAX_SEARCH_QUERIES,
7584
7844
  description: `One or more search queries to run in one call (max ${MAX_SEARCH_QUERIES})`
7585
7845
  }),
7586
- maxResults: Type15.Optional(
7587
- Type15.Integer({
7846
+ maxResults: Type16.Optional(
7847
+ Type16.Integer({
7588
7848
  minimum: 1,
7589
7849
  maximum: MAX_ALLOWED_RESULTS,
7590
7850
  description: `Maximum number of results to return (default: ${DEFAULT_MAX_RESULTS})`
@@ -7633,8 +7893,8 @@ function registerWebContentsTool(pi, providerIds) {
7633
7893
  name: "web_contents",
7634
7894
  label: "Web Contents",
7635
7895
  description: "Read and extract the main contents of one or more web pages. Batch related pages together, or use separate sibling calls when each page can be acted on independently.",
7636
- parameters: Type15.Object({
7637
- urls: Type15.Array(Type15.String({ minLength: 1 }), {
7896
+ parameters: Type16.Object({
7897
+ urls: Type16.Array(Type16.String({ minLength: 1 }), {
7638
7898
  minItems: 1,
7639
7899
  description: "One or more URLs to extract"
7640
7900
  }),
@@ -7684,8 +7944,8 @@ function registerWebAnswerTool(pi, providerIds) {
7684
7944
  name: "web_answer",
7685
7945
  label: "Web Answer",
7686
7946
  description: `Answer one or more questions using web-grounded evidence (up to ${MAX_SEARCH_QUERIES} per call).`,
7687
- parameters: Type15.Object({
7688
- queries: Type15.Array(Type15.String({ minLength: 1 }), {
7947
+ parameters: Type16.Object({
7948
+ queries: Type16.Array(Type16.String({ minLength: 1 }), {
7689
7949
  minItems: 1,
7690
7950
  maxItems: MAX_SEARCH_QUERIES,
7691
7951
  description: `One or more questions to answer in one call (max ${MAX_SEARCH_QUERIES})`
@@ -7739,8 +7999,8 @@ function registerWebResearchTool(pi, webResearchLifecycle, providerIds) {
7739
7999
  name: "web_research",
7740
8000
  label: "Web Research",
7741
8001
  description: "Start a long-running web research job. Returns immediately with a dispatch notice; the final report is saved to a file and posted later as a custom message.",
7742
- parameters: Type15.Object({
7743
- input: Type15.String({ description: "Research brief or question" }),
8002
+ parameters: Type16.Object({
8003
+ input: Type16.String({ description: "Research brief or question" }),
7744
8004
  ...optionalField(
7745
8005
  "options",
7746
8006
  buildStructuredOptionsSchema("research", selectedProviderId)
@@ -7883,7 +8143,7 @@ function optionalField(name, schema) {
7883
8143
  function buildStructuredOptionsSchema(capability, providerId) {
7884
8144
  const providerSchema = resolveProviderOptionsSchema(capability, providerId);
7885
8145
  const schema = buildToolOptionsSchema(capability, providerSchema);
7886
- return schema ? Type15.Optional(schema) : void 0;
8146
+ return schema ? Type16.Optional(schema) : void 0;
7887
8147
  }
7888
8148
  function resolveProviderOptionsSchema(capability, providerId) {
7889
8149
  if (!providerId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-web-providers",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "Configurable web access extension for pi with per-tool provider routing and explicit provider option schemas for search, contents, answers, and research.",
5
5
  "type": "module",
6
6
  "files": [
@@ -30,6 +30,7 @@
30
30
  "openai",
31
31
  "parallel",
32
32
  "perplexity",
33
+ "serper",
33
34
  "tavily",
34
35
  "valyu"
35
36
  ],