pi-web-providers 3.1.0 → 3.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.
Files changed (3) hide show
  1. package/README.md +9 -2
  2. package/dist/index.js +191 -0
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -68,7 +68,7 @@ reported when the tool is actually called.
68
68
  | **Exa** | ✔ | ✔ | ✔ | ✔ | `EXA_API_KEY` |
69
69
  | **Firecrawl** | ✔ | ✔ | | | `FIRECRAWL_API_KEY` |
70
70
  | **Gemini** | ✔ | | ✔ | ✔ | `GOOGLE_API_KEY` |
71
- | **Linkup** | ✔ | ✔ | | | `LINKUP_API_KEY` |
71
+ | **Linkup** | ✔ | ✔ | || `LINKUP_API_KEY` |
72
72
  | **Ollama** | ✔ | ✔ | | | `OLLAMA_API_KEY` |
73
73
  | **OpenAI** | ✔ | | ✔ | ✔ | `OPENAI_API_KEY` |
74
74
  | **Parallel** | ✔ | ✔ | | | `PARALLEL_API_KEY` |
@@ -352,10 +352,17 @@ scope, or account ID is usually wrong.
352
352
  - SDK: `linkup-sdk`
353
353
  - Supports `web_search` via Linkup Search with fixed `searchResults` output
354
354
  - Supports `web_contents` via Linkup Fetch and always returns markdown
355
+ - Supports `web_research` via Linkup's async Research API
355
356
  - Exposes search options `depth`, `includeImages`, `includeDomains`,
356
357
  `excludeDomains`, `fromDate`, and `toDate`
357
358
  - Exposes contents options `renderJs`, `includeRawHtml`, and `extractImages`
358
- - Good fit for a simple search-plus-markdown setup without extra provider wiring
359
+ - Exposes research options `outputType`, `mode`, `reasoningDepth`,
360
+ `includeDomains`, `excludeDomains`, `fromDate`, `toDate`, and
361
+ `structuredOutputSchema`
362
+ - Research defaults to `sourcedAnswer`; provide `structuredOutputSchema` for
363
+ structured output
364
+ - Good fit for search, markdown extraction, and long-running sourced research
365
+ without extra provider wiring
359
366
 
360
367
  </details>
361
368
 
package/dist/index.js CHANGED
@@ -4241,6 +4241,45 @@ var linkupContentsOptionsSchema = Type9.Object(
4241
4241
  },
4242
4242
  { description: "Linkup fetch options." }
4243
4243
  );
4244
+ var linkupResearchOptionsSchema = Type9.Object(
4245
+ {
4246
+ outputType: Type9.Optional(
4247
+ literalUnion(["sourcedAnswer", "structured"], {
4248
+ description: "Research output type. Defaults to 'sourcedAnswer' unless structuredOutputSchema is provided."
4249
+ })
4250
+ ),
4251
+ mode: Type9.Optional(
4252
+ literalUnion(["answer", "auto", "investigate", "research"], {
4253
+ description: "Research mode. Use 'answer' for precise verified answers, 'investigate' for focused deep dives, 'research' for broad reports, or omit/auto to let Linkup classify the task."
4254
+ })
4255
+ ),
4256
+ reasoningDepth: Type9.Optional(
4257
+ literalUnion(["S", "M", "L", "XL"], {
4258
+ description: "Reasoning depth. Higher values trade latency for more thorough investigation."
4259
+ })
4260
+ ),
4261
+ includeDomains: Type9.Optional(
4262
+ Type9.Array(Type9.String(), {
4263
+ description: "Restrict research to these domains."
4264
+ })
4265
+ ),
4266
+ excludeDomains: Type9.Optional(
4267
+ Type9.Array(Type9.String(), { description: "Exclude these domains." })
4268
+ ),
4269
+ fromDate: Type9.Optional(
4270
+ Type9.String({ description: "ISO date string for earliest result date." })
4271
+ ),
4272
+ toDate: Type9.Optional(
4273
+ Type9.String({ description: "ISO date string for latest result date." })
4274
+ ),
4275
+ structuredOutputSchema: Type9.Optional(
4276
+ Type9.Record(Type9.String(), Type9.Any(), {
4277
+ description: "JSON schema object required when outputType is 'structured'."
4278
+ })
4279
+ )
4280
+ },
4281
+ { description: "Linkup research options." }
4282
+ );
4244
4283
  var linkupImplementation = {
4245
4284
  id: "linkup",
4246
4285
  label: "Linkup",
@@ -4251,6 +4290,8 @@ var linkupImplementation = {
4251
4290
  return linkupSearchOptionsSchema;
4252
4291
  case "contents":
4253
4292
  return linkupContentsOptionsSchema;
4293
+ case "research":
4294
+ return linkupResearchOptionsSchema;
4254
4295
  default:
4255
4296
  return void 0;
4256
4297
  }
@@ -4307,6 +4348,51 @@ var linkupImplementation = {
4307
4348
  })
4308
4349
  )
4309
4350
  };
4351
+ },
4352
+ async research(input, config, context, options) {
4353
+ return await executeAsyncResearch({
4354
+ providerLabel: linkupImplementation.label,
4355
+ providerId: linkupImplementation.id,
4356
+ context,
4357
+ start: (researchContext) => linkupImplementation.startResearch(
4358
+ input,
4359
+ config,
4360
+ researchContext,
4361
+ options
4362
+ ),
4363
+ poll: (id, researchContext) => linkupImplementation.pollResearch(id, config, researchContext)
4364
+ });
4365
+ },
4366
+ async startResearch(input, config, _context, options) {
4367
+ const client = createClient4(config);
4368
+ const defaults = asJsonObject(config.options?.research) ?? {};
4369
+ const task = await client.research(
4370
+ buildResearchParams(input, {
4371
+ ...defaults,
4372
+ ...options ?? {}
4373
+ })
4374
+ );
4375
+ return { id: task.id };
4376
+ },
4377
+ async pollResearch(id, config, _context) {
4378
+ const client = createClient4(config);
4379
+ const task = await client.getResearch(id);
4380
+ if (task.status === "completed") {
4381
+ return {
4382
+ status: "completed",
4383
+ output: formatResearchTaskOutput(task)
4384
+ };
4385
+ }
4386
+ if (task.status === "failed") {
4387
+ return {
4388
+ status: "failed",
4389
+ error: task.error ?? "research failed"
4390
+ };
4391
+ }
4392
+ return {
4393
+ status: "in_progress",
4394
+ statusText: task.status
4395
+ };
4310
4396
  }
4311
4397
  };
4312
4398
  function buildSearchParams(query2, maxResults, options) {
@@ -4351,6 +4437,45 @@ function buildFetchParams(url2, options) {
4351
4437
  ...fetchOptions.extractImages !== void 0 ? { extractImages: fetchOptions.extractImages } : {}
4352
4438
  };
4353
4439
  }
4440
+ function buildResearchParams(input, options) {
4441
+ const researchOptions = options;
4442
+ if (researchOptions.q !== void 0 || researchOptions.query !== void 0 || researchOptions.input !== void 0) {
4443
+ throw new Error(
4444
+ "Linkup research options cannot override the managed input."
4445
+ );
4446
+ }
4447
+ const outputType = researchOptions.outputType ?? (researchOptions.structuredOutputSchema !== void 0 ? "structured" : "sourcedAnswer");
4448
+ if (outputType === "structured" && researchOptions.structuredOutputSchema === void 0) {
4449
+ throw new Error(
4450
+ "Linkup research outputType 'structured' requires structuredOutputSchema."
4451
+ );
4452
+ }
4453
+ if (outputType === "sourcedAnswer" && researchOptions.structuredOutputSchema !== void 0) {
4454
+ throw new Error(
4455
+ "Linkup research structuredOutputSchema requires outputType 'structured'."
4456
+ );
4457
+ }
4458
+ const commonParams = {
4459
+ query: input,
4460
+ ...researchOptions.includeDomains !== void 0 ? { includeDomains: researchOptions.includeDomains } : {},
4461
+ ...researchOptions.excludeDomains !== void 0 ? { excludeDomains: researchOptions.excludeDomains } : {},
4462
+ ...researchOptions.fromDate !== void 0 ? { fromDate: toDate(researchOptions.fromDate, "fromDate") } : {},
4463
+ ...researchOptions.toDate !== void 0 ? { toDate: toDate(researchOptions.toDate, "toDate") } : {},
4464
+ ...researchOptions.mode !== void 0 ? { mode: researchOptions.mode } : {},
4465
+ ...researchOptions.reasoningDepth !== void 0 ? { reasoningDepth: researchOptions.reasoningDepth } : {}
4466
+ };
4467
+ if (outputType === "structured") {
4468
+ return {
4469
+ ...commonParams,
4470
+ outputType,
4471
+ structuredOutputSchema: researchOptions.structuredOutputSchema
4472
+ };
4473
+ }
4474
+ return {
4475
+ ...commonParams,
4476
+ outputType
4477
+ };
4478
+ }
4354
4479
  function createClient4(config) {
4355
4480
  const apiKey = resolveConfigValue(config.credentials?.api);
4356
4481
  if (!apiKey) {
@@ -4361,6 +4486,40 @@ function createClient4(config) {
4361
4486
  baseUrl: resolveConfigValue(config.baseUrl)
4362
4487
  });
4363
4488
  }
4489
+ function formatResearchTaskOutput(task) {
4490
+ const output = task.output;
4491
+ if (!output) {
4492
+ return {
4493
+ provider: linkupImplementation.id,
4494
+ text: "Linkup research completed without textual output."
4495
+ };
4496
+ }
4497
+ const outputRecord = asRecord2(output);
4498
+ const inputRecord = asRecord2(task.input);
4499
+ const outputType = inputRecord ? readString3(inputRecord.outputType) : void 0;
4500
+ const answer = outputRecord ? readString3(outputRecord.answer) : void 0;
4501
+ const sources = outputRecord ? readSources(outputRecord.sources) : [];
4502
+ if (outputType !== "structured" && answer !== void 0) {
4503
+ const lines = [answer];
4504
+ if (sources.length > 0) {
4505
+ lines.push("");
4506
+ lines.push("Sources:");
4507
+ for (const [index, source] of sources.entries()) {
4508
+ lines.push(`${index + 1}. ${source.title}`);
4509
+ lines.push(` ${source.url}`);
4510
+ }
4511
+ }
4512
+ return {
4513
+ provider: linkupImplementation.id,
4514
+ text: lines.join("\n").trimEnd(),
4515
+ itemCount: sources.length
4516
+ };
4517
+ }
4518
+ return {
4519
+ provider: linkupImplementation.id,
4520
+ text: formatJson(output)
4521
+ };
4522
+ }
4364
4523
  function toSearchResult2(value) {
4365
4524
  const entry = asRecord2(value);
4366
4525
  if (!entry) {
@@ -4382,6 +4541,27 @@ function toSearchResult2(value) {
4382
4541
  metadata: Object.keys(metadata).length > 0 ? metadata : void 0
4383
4542
  };
4384
4543
  }
4544
+ function readSources(value) {
4545
+ if (!Array.isArray(value)) {
4546
+ return [];
4547
+ }
4548
+ return value.flatMap((entry) => {
4549
+ const source = asRecord2(entry);
4550
+ if (!source) {
4551
+ return [];
4552
+ }
4553
+ const url2 = readString3(source.url);
4554
+ if (!url2) {
4555
+ return [];
4556
+ }
4557
+ return [
4558
+ {
4559
+ title: readString3(source.name) ?? url2,
4560
+ url: url2
4561
+ }
4562
+ ];
4563
+ });
4564
+ }
4385
4565
  function asRecord2(value) {
4386
4566
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
4387
4567
  }
@@ -4435,6 +4615,17 @@ var linkupProvider = defineProvider({
4435
4615
  input.options
4436
4616
  );
4437
4617
  }
4618
+ }),
4619
+ research: defineCapability({
4620
+ options: linkupImplementation.getToolOptionsSchema?.("research"),
4621
+ async execute(input, ctx) {
4622
+ return await linkupImplementation.research(
4623
+ input.input,
4624
+ ctx.config,
4625
+ ctx,
4626
+ input.options
4627
+ );
4628
+ }
4438
4629
  })
4439
4630
  }
4440
4631
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-web-providers",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "Configurable web access extension for pi with per-tool provider routing and explicit provider option schemas for search, contents, quick grounded answers, and research.",
5
5
  "type": "module",
6
6
  "files": [
@@ -73,7 +73,7 @@
73
73
  "@tavily/core": "^0.7.3",
74
74
  "cloudflare": "^5.2.0",
75
75
  "exa-js": "^2.12.1",
76
- "linkup-sdk": "^2.7.0",
76
+ "linkup-sdk": "^3.2.0",
77
77
  "openai": "^6.35.0",
78
78
  "parallel-web": "^0.4.1",
79
79
  "valyu-js": "^2.7.14"