pi-web-providers 3.1.0 → 3.3.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 +17 -4
- package/dist/index.js +371 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -66,9 +66,9 @@ reported when the tool is actually called.
|
|
|
66
66
|
| **Brave** | ✔ | | ✔ | ✔ | `BRAVE_SEARCH_API_KEY` / `BRAVE_ANSWERS_API_KEY` |
|
|
67
67
|
| **Cloudflare** | | ✔ | | | `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` |
|
|
68
68
|
| **Exa** | ✔ | ✔ | ✔ | ✔ | `EXA_API_KEY` |
|
|
69
|
-
| **Firecrawl** | ✔ | ✔ |
|
|
69
|
+
| **Firecrawl** | ✔ | ✔ | ✔ | | `FIRECRAWL_API_KEY` |
|
|
70
70
|
| **Gemini** | ✔ | | ✔ | ✔ | `GOOGLE_API_KEY` |
|
|
71
|
-
| **Linkup** | ✔ | ✔ | |
|
|
71
|
+
| **Linkup** | ✔ | ✔ | | ✔ | `LINKUP_API_KEY` |
|
|
72
72
|
| **Ollama** | ✔ | ✔ | | | `OLLAMA_API_KEY` |
|
|
73
73
|
| **OpenAI** | ✔ | | ✔ | ✔ | `OPENAI_API_KEY` |
|
|
74
74
|
| **Parallel** | ✔ | ✔ | | | `PARALLEL_API_KEY` |
|
|
@@ -315,13 +315,19 @@ scope, or account ID is usually wrong.
|
|
|
315
315
|
<summary><strong>Firecrawl</strong></summary>
|
|
316
316
|
|
|
317
317
|
- SDK: `@mendable/firecrawl-js`
|
|
318
|
-
- Supports `web_search` and `
|
|
318
|
+
- Supports `web_search`, `web_contents`, and page-scoped `web_answer`
|
|
319
319
|
- Search can optionally include Firecrawl scrape-backed result enrichment
|
|
320
320
|
- Contents extraction uses Firecrawl scrape with markdown-first defaults
|
|
321
|
+
- Answers use Firecrawl scrape's `question` format against one explicit page URL;
|
|
322
|
+
set `options.url` in the `web_answer` call or
|
|
323
|
+
`providers.firecrawl.options.answer.url` as a default
|
|
321
324
|
- Exposes search options such as `lang`, `country`, `sources`, `categories`,
|
|
322
325
|
`location`, `timeout`, and `scrapeOptions`
|
|
323
326
|
- Exposes contents options such as `formats`, `onlyMainContent`, `includeTags`,
|
|
324
327
|
`excludeTags`, `waitFor`, `headers`, `location`, `mobile`, and `proxy`
|
|
328
|
+
- Exposes answer options `url`, `onlyMainContent`, `includeTags`,
|
|
329
|
+
`excludeTags`, `waitFor`, `headers`, `location`, `mobile`, and `proxy`
|
|
330
|
+
- Firecrawl charges 5 credits per page for the `question` format
|
|
325
331
|
- Optional `baseUrl` overrides are supported for self-hosted Firecrawl
|
|
326
332
|
instances, proxies, and testing. API keys are required for Firecrawl Cloud,
|
|
327
333
|
but can be omitted for self-hosted endpoints that do not enforce
|
|
@@ -352,10 +358,17 @@ scope, or account ID is usually wrong.
|
|
|
352
358
|
- SDK: `linkup-sdk`
|
|
353
359
|
- Supports `web_search` via Linkup Search with fixed `searchResults` output
|
|
354
360
|
- Supports `web_contents` via Linkup Fetch and always returns markdown
|
|
361
|
+
- Supports `web_research` via Linkup's async Research API
|
|
355
362
|
- Exposes search options `depth`, `includeImages`, `includeDomains`,
|
|
356
363
|
`excludeDomains`, `fromDate`, and `toDate`
|
|
357
364
|
- Exposes contents options `renderJs`, `includeRawHtml`, and `extractImages`
|
|
358
|
-
-
|
|
365
|
+
- Exposes research options `outputType`, `mode`, `reasoningDepth`,
|
|
366
|
+
`includeDomains`, `excludeDomains`, `fromDate`, `toDate`, and
|
|
367
|
+
`structuredOutputSchema`
|
|
368
|
+
- Research defaults to `sourcedAnswer`; provide `structuredOutputSchema` for
|
|
369
|
+
structured output
|
|
370
|
+
- Good fit for search, markdown extraction, and long-running sourced research
|
|
371
|
+
without extra provider wiring
|
|
359
372
|
|
|
360
373
|
</details>
|
|
361
374
|
|
package/dist/index.js
CHANGED
|
@@ -3097,6 +3097,8 @@ var exaProvider = defineProvider({
|
|
|
3097
3097
|
import FirecrawlClient from "@mendable/firecrawl-js";
|
|
3098
3098
|
import { Type as Type7 } from "typebox";
|
|
3099
3099
|
var FIRECRAWL_CLOUD_HOST = "api.firecrawl.dev";
|
|
3100
|
+
var FIRECRAWL_DEFAULT_API_URL = "https://api.firecrawl.dev";
|
|
3101
|
+
var FIRECRAWL_QUESTION_LIMIT = 1e4;
|
|
3100
3102
|
var firecrawlSearchOptionsSchema = Type7.Object(
|
|
3101
3103
|
{
|
|
3102
3104
|
lang: Type7.Optional(
|
|
@@ -3203,6 +3205,55 @@ var firecrawlScrapeOptionsSchema = Type7.Object(
|
|
|
3203
3205
|
},
|
|
3204
3206
|
{ description: "Firecrawl scrape options." }
|
|
3205
3207
|
);
|
|
3208
|
+
var firecrawlAnswerOptionsSchema = Type7.Object(
|
|
3209
|
+
{
|
|
3210
|
+
url: Type7.String({
|
|
3211
|
+
minLength: 1,
|
|
3212
|
+
description: "URL of the page to ask about."
|
|
3213
|
+
}),
|
|
3214
|
+
onlyMainContent: Type7.Optional(
|
|
3215
|
+
Type7.Boolean({ description: "Extract only the main content." })
|
|
3216
|
+
),
|
|
3217
|
+
includeTags: Type7.Optional(
|
|
3218
|
+
Type7.Array(Type7.String(), { description: "CSS selectors to include." })
|
|
3219
|
+
),
|
|
3220
|
+
excludeTags: Type7.Optional(
|
|
3221
|
+
Type7.Array(Type7.String(), { description: "CSS selectors to exclude." })
|
|
3222
|
+
),
|
|
3223
|
+
waitFor: Type7.Optional(
|
|
3224
|
+
Type7.Integer({
|
|
3225
|
+
minimum: 0,
|
|
3226
|
+
description: "Milliseconds to wait before scraping."
|
|
3227
|
+
})
|
|
3228
|
+
),
|
|
3229
|
+
headers: Type7.Optional(
|
|
3230
|
+
Type7.Record(Type7.String(), Type7.String(), {
|
|
3231
|
+
description: "Headers to send when scraping."
|
|
3232
|
+
})
|
|
3233
|
+
),
|
|
3234
|
+
location: Type7.Optional(
|
|
3235
|
+
Type7.Object(
|
|
3236
|
+
{
|
|
3237
|
+
country: Type7.Optional(Type7.String({ description: "Country hint." })),
|
|
3238
|
+
region: Type7.Optional(Type7.String({ description: "Region hint." })),
|
|
3239
|
+
city: Type7.Optional(Type7.String({ description: "City hint." }))
|
|
3240
|
+
},
|
|
3241
|
+
{ description: "Location hint for scraping." }
|
|
3242
|
+
)
|
|
3243
|
+
),
|
|
3244
|
+
mobile: Type7.Optional(
|
|
3245
|
+
Type7.Boolean({ description: "Use a mobile browser profile." })
|
|
3246
|
+
),
|
|
3247
|
+
proxy: Type7.Optional(
|
|
3248
|
+
Type7.String({
|
|
3249
|
+
description: "Proxy mode passed through to Firecrawl."
|
|
3250
|
+
})
|
|
3251
|
+
)
|
|
3252
|
+
},
|
|
3253
|
+
{
|
|
3254
|
+
description: "Firecrawl page-question options. The URL is required; the question comes from the web_answer query."
|
|
3255
|
+
}
|
|
3256
|
+
);
|
|
3206
3257
|
var firecrawlImplementation = {
|
|
3207
3258
|
id: "firecrawl",
|
|
3208
3259
|
label: "Firecrawl",
|
|
@@ -3213,6 +3264,8 @@ var firecrawlImplementation = {
|
|
|
3213
3264
|
return firecrawlSearchOptionsSchema;
|
|
3214
3265
|
case "contents":
|
|
3215
3266
|
return firecrawlScrapeOptionsSchema;
|
|
3267
|
+
case "answer":
|
|
3268
|
+
return firecrawlAnswerOptionsSchema;
|
|
3216
3269
|
default:
|
|
3217
3270
|
return void 0;
|
|
3218
3271
|
}
|
|
@@ -3279,6 +3332,34 @@ var firecrawlImplementation = {
|
|
|
3279
3332
|
})
|
|
3280
3333
|
)
|
|
3281
3334
|
};
|
|
3335
|
+
},
|
|
3336
|
+
async answer(query2, config, _context, options) {
|
|
3337
|
+
const question = validateQuestion(query2);
|
|
3338
|
+
const defaults = asJsonObject(config.options?.scrape);
|
|
3339
|
+
const answerDefaults = asJsonObject(config.options?.answer);
|
|
3340
|
+
const mergedOptions = {
|
|
3341
|
+
onlyMainContent: true,
|
|
3342
|
+
...defaults,
|
|
3343
|
+
...answerDefaults,
|
|
3344
|
+
...options ?? {}
|
|
3345
|
+
};
|
|
3346
|
+
const url2 = validateUrl(mergedOptions.url);
|
|
3347
|
+
const scrapeOptions = stripAnswerOnlyOptions(mergedOptions);
|
|
3348
|
+
const response = await scrapeQuestion(config, url2, question, scrapeOptions);
|
|
3349
|
+
const document = getFirecrawlDocument(response);
|
|
3350
|
+
const answer = readString2(document.answer);
|
|
3351
|
+
if (!answer?.trim()) {
|
|
3352
|
+
throw new Error("No answer returned for this URL.");
|
|
3353
|
+
}
|
|
3354
|
+
return {
|
|
3355
|
+
provider: firecrawlImplementation.id,
|
|
3356
|
+
text: answer.trim(),
|
|
3357
|
+
itemCount: 1,
|
|
3358
|
+
metadata: {
|
|
3359
|
+
url: url2,
|
|
3360
|
+
...asRecord(document.metadata) ? { metadata: document.metadata } : {}
|
|
3361
|
+
}
|
|
3362
|
+
};
|
|
3282
3363
|
}
|
|
3283
3364
|
};
|
|
3284
3365
|
function createClient3(config) {
|
|
@@ -3302,6 +3383,88 @@ function getFirecrawlCapabilityStatus(config, options) {
|
|
|
3302
3383
|
function isFirecrawlCloudApiUrl(apiUrl) {
|
|
3303
3384
|
return !apiUrl || apiUrl.includes(FIRECRAWL_CLOUD_HOST);
|
|
3304
3385
|
}
|
|
3386
|
+
function validateQuestion(query2) {
|
|
3387
|
+
const question = query2.trim();
|
|
3388
|
+
if (!question) {
|
|
3389
|
+
throw new Error("question must be a non-empty string.");
|
|
3390
|
+
}
|
|
3391
|
+
if (question.length > FIRECRAWL_QUESTION_LIMIT) {
|
|
3392
|
+
throw new Error(
|
|
3393
|
+
`Firecrawl question must be at most ${FIRECRAWL_QUESTION_LIMIT} characters.`
|
|
3394
|
+
);
|
|
3395
|
+
}
|
|
3396
|
+
return question;
|
|
3397
|
+
}
|
|
3398
|
+
function validateUrl(value) {
|
|
3399
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
3400
|
+
throw new Error("Firecrawl answer requires options.url.");
|
|
3401
|
+
}
|
|
3402
|
+
return value.trim();
|
|
3403
|
+
}
|
|
3404
|
+
function stripAnswerOnlyOptions(options) {
|
|
3405
|
+
const { url: _url, formats: _formats, ...scrapeOptions } = options;
|
|
3406
|
+
return scrapeOptions;
|
|
3407
|
+
}
|
|
3408
|
+
async function scrapeQuestion(config, url2, question, options) {
|
|
3409
|
+
const apiUrl = resolveConfigValue(config.baseUrl) ?? FIRECRAWL_DEFAULT_API_URL;
|
|
3410
|
+
const apiKey = resolveConfigValue(config.credentials?.api);
|
|
3411
|
+
if (isFirecrawlCloudApiUrl(apiUrl) && !apiKey) {
|
|
3412
|
+
throw new Error("is missing an API key");
|
|
3413
|
+
}
|
|
3414
|
+
const response = await fetch(joinUrl(apiUrl, "/v2/scrape"), {
|
|
3415
|
+
method: "POST",
|
|
3416
|
+
headers: {
|
|
3417
|
+
"Content-Type": "application/json",
|
|
3418
|
+
...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
|
|
3419
|
+
},
|
|
3420
|
+
body: JSON.stringify({
|
|
3421
|
+
...options,
|
|
3422
|
+
url: url2,
|
|
3423
|
+
formats: [{ type: "question", question }]
|
|
3424
|
+
})
|
|
3425
|
+
});
|
|
3426
|
+
const payload = await readJsonResponse(response);
|
|
3427
|
+
if (!response.ok) {
|
|
3428
|
+
throw new Error(readFirecrawlError(payload, response.statusText));
|
|
3429
|
+
}
|
|
3430
|
+
if (isFirecrawlFailure(payload)) {
|
|
3431
|
+
throw new Error(readFirecrawlError(payload, "Firecrawl scrape failed."));
|
|
3432
|
+
}
|
|
3433
|
+
return payload;
|
|
3434
|
+
}
|
|
3435
|
+
function joinUrl(baseUrl, path) {
|
|
3436
|
+
return `${baseUrl.replace(/\/+$/g, "")}/${path.replace(/^\/+/g, "")}`;
|
|
3437
|
+
}
|
|
3438
|
+
async function readJsonResponse(response) {
|
|
3439
|
+
const text = await response.text();
|
|
3440
|
+
if (!text) {
|
|
3441
|
+
return {};
|
|
3442
|
+
}
|
|
3443
|
+
try {
|
|
3444
|
+
return JSON.parse(text);
|
|
3445
|
+
} catch {
|
|
3446
|
+
return text;
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
function isFirecrawlFailure(value) {
|
|
3450
|
+
const record = asRecord(value);
|
|
3451
|
+
return record?.success === false || record?.error !== void 0;
|
|
3452
|
+
}
|
|
3453
|
+
function readFirecrawlError(value, fallback) {
|
|
3454
|
+
const record = asRecord(value);
|
|
3455
|
+
return readString2(record?.error) ?? readString2(record?.message) ?? (typeof value === "string" ? value : void 0) ?? fallback;
|
|
3456
|
+
}
|
|
3457
|
+
function getFirecrawlDocument(value) {
|
|
3458
|
+
const record = asRecord(value);
|
|
3459
|
+
const data = asRecord(record?.data);
|
|
3460
|
+
if (data) {
|
|
3461
|
+
return data;
|
|
3462
|
+
}
|
|
3463
|
+
if (record) {
|
|
3464
|
+
return record;
|
|
3465
|
+
}
|
|
3466
|
+
throw new Error(`Unexpected Firecrawl response: ${formatJson(value)}`);
|
|
3467
|
+
}
|
|
3305
3468
|
function flattenSearchResults(response) {
|
|
3306
3469
|
return ["web", "news", "images"].flatMap(
|
|
3307
3470
|
(source) => (response[source] ?? []).map((entry) => toSearchResult(source, entry)).filter((entry) => entry !== null)
|
|
@@ -3389,6 +3552,21 @@ var firecrawlProvider = defineProvider({
|
|
|
3389
3552
|
input.options
|
|
3390
3553
|
);
|
|
3391
3554
|
}
|
|
3555
|
+
}),
|
|
3556
|
+
answer: defineCapability({
|
|
3557
|
+
options: firecrawlImplementation.getToolOptionsSchema?.("answer"),
|
|
3558
|
+
promptGuidelines: [
|
|
3559
|
+
"Firecrawl web_answer is page-scoped: set options.url to the specific page URL to ask about.",
|
|
3560
|
+
"Do not use Firecrawl web_answer for general multi-source answers; use web_search plus web_contents or web_research instead."
|
|
3561
|
+
],
|
|
3562
|
+
async execute(input, ctx) {
|
|
3563
|
+
return await firecrawlImplementation.answer(
|
|
3564
|
+
input.query,
|
|
3565
|
+
ctx.config,
|
|
3566
|
+
ctx,
|
|
3567
|
+
input.options
|
|
3568
|
+
);
|
|
3569
|
+
}
|
|
3392
3570
|
})
|
|
3393
3571
|
}
|
|
3394
3572
|
});
|
|
@@ -4241,6 +4419,45 @@ var linkupContentsOptionsSchema = Type9.Object(
|
|
|
4241
4419
|
},
|
|
4242
4420
|
{ description: "Linkup fetch options." }
|
|
4243
4421
|
);
|
|
4422
|
+
var linkupResearchOptionsSchema = Type9.Object(
|
|
4423
|
+
{
|
|
4424
|
+
outputType: Type9.Optional(
|
|
4425
|
+
literalUnion(["sourcedAnswer", "structured"], {
|
|
4426
|
+
description: "Research output type. Defaults to 'sourcedAnswer' unless structuredOutputSchema is provided."
|
|
4427
|
+
})
|
|
4428
|
+
),
|
|
4429
|
+
mode: Type9.Optional(
|
|
4430
|
+
literalUnion(["answer", "auto", "investigate", "research"], {
|
|
4431
|
+
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."
|
|
4432
|
+
})
|
|
4433
|
+
),
|
|
4434
|
+
reasoningDepth: Type9.Optional(
|
|
4435
|
+
literalUnion(["S", "M", "L", "XL"], {
|
|
4436
|
+
description: "Reasoning depth. Higher values trade latency for more thorough investigation."
|
|
4437
|
+
})
|
|
4438
|
+
),
|
|
4439
|
+
includeDomains: Type9.Optional(
|
|
4440
|
+
Type9.Array(Type9.String(), {
|
|
4441
|
+
description: "Restrict research to these domains."
|
|
4442
|
+
})
|
|
4443
|
+
),
|
|
4444
|
+
excludeDomains: Type9.Optional(
|
|
4445
|
+
Type9.Array(Type9.String(), { description: "Exclude these domains." })
|
|
4446
|
+
),
|
|
4447
|
+
fromDate: Type9.Optional(
|
|
4448
|
+
Type9.String({ description: "ISO date string for earliest result date." })
|
|
4449
|
+
),
|
|
4450
|
+
toDate: Type9.Optional(
|
|
4451
|
+
Type9.String({ description: "ISO date string for latest result date." })
|
|
4452
|
+
),
|
|
4453
|
+
structuredOutputSchema: Type9.Optional(
|
|
4454
|
+
Type9.Record(Type9.String(), Type9.Any(), {
|
|
4455
|
+
description: "JSON schema object required when outputType is 'structured'."
|
|
4456
|
+
})
|
|
4457
|
+
)
|
|
4458
|
+
},
|
|
4459
|
+
{ description: "Linkup research options." }
|
|
4460
|
+
);
|
|
4244
4461
|
var linkupImplementation = {
|
|
4245
4462
|
id: "linkup",
|
|
4246
4463
|
label: "Linkup",
|
|
@@ -4251,6 +4468,8 @@ var linkupImplementation = {
|
|
|
4251
4468
|
return linkupSearchOptionsSchema;
|
|
4252
4469
|
case "contents":
|
|
4253
4470
|
return linkupContentsOptionsSchema;
|
|
4471
|
+
case "research":
|
|
4472
|
+
return linkupResearchOptionsSchema;
|
|
4254
4473
|
default:
|
|
4255
4474
|
return void 0;
|
|
4256
4475
|
}
|
|
@@ -4307,6 +4526,51 @@ var linkupImplementation = {
|
|
|
4307
4526
|
})
|
|
4308
4527
|
)
|
|
4309
4528
|
};
|
|
4529
|
+
},
|
|
4530
|
+
async research(input, config, context, options) {
|
|
4531
|
+
return await executeAsyncResearch({
|
|
4532
|
+
providerLabel: linkupImplementation.label,
|
|
4533
|
+
providerId: linkupImplementation.id,
|
|
4534
|
+
context,
|
|
4535
|
+
start: (researchContext) => linkupImplementation.startResearch(
|
|
4536
|
+
input,
|
|
4537
|
+
config,
|
|
4538
|
+
researchContext,
|
|
4539
|
+
options
|
|
4540
|
+
),
|
|
4541
|
+
poll: (id, researchContext) => linkupImplementation.pollResearch(id, config, researchContext)
|
|
4542
|
+
});
|
|
4543
|
+
},
|
|
4544
|
+
async startResearch(input, config, _context, options) {
|
|
4545
|
+
const client = createClient4(config);
|
|
4546
|
+
const defaults = asJsonObject(config.options?.research) ?? {};
|
|
4547
|
+
const task = await client.research(
|
|
4548
|
+
buildResearchParams(input, {
|
|
4549
|
+
...defaults,
|
|
4550
|
+
...options ?? {}
|
|
4551
|
+
})
|
|
4552
|
+
);
|
|
4553
|
+
return { id: task.id };
|
|
4554
|
+
},
|
|
4555
|
+
async pollResearch(id, config, _context) {
|
|
4556
|
+
const client = createClient4(config);
|
|
4557
|
+
const task = await client.getResearch(id);
|
|
4558
|
+
if (task.status === "completed") {
|
|
4559
|
+
return {
|
|
4560
|
+
status: "completed",
|
|
4561
|
+
output: formatResearchTaskOutput(task)
|
|
4562
|
+
};
|
|
4563
|
+
}
|
|
4564
|
+
if (task.status === "failed") {
|
|
4565
|
+
return {
|
|
4566
|
+
status: "failed",
|
|
4567
|
+
error: task.error ?? "research failed"
|
|
4568
|
+
};
|
|
4569
|
+
}
|
|
4570
|
+
return {
|
|
4571
|
+
status: "in_progress",
|
|
4572
|
+
statusText: task.status
|
|
4573
|
+
};
|
|
4310
4574
|
}
|
|
4311
4575
|
};
|
|
4312
4576
|
function buildSearchParams(query2, maxResults, options) {
|
|
@@ -4351,6 +4615,45 @@ function buildFetchParams(url2, options) {
|
|
|
4351
4615
|
...fetchOptions.extractImages !== void 0 ? { extractImages: fetchOptions.extractImages } : {}
|
|
4352
4616
|
};
|
|
4353
4617
|
}
|
|
4618
|
+
function buildResearchParams(input, options) {
|
|
4619
|
+
const researchOptions = options;
|
|
4620
|
+
if (researchOptions.q !== void 0 || researchOptions.query !== void 0 || researchOptions.input !== void 0) {
|
|
4621
|
+
throw new Error(
|
|
4622
|
+
"Linkup research options cannot override the managed input."
|
|
4623
|
+
);
|
|
4624
|
+
}
|
|
4625
|
+
const outputType = researchOptions.outputType ?? (researchOptions.structuredOutputSchema !== void 0 ? "structured" : "sourcedAnswer");
|
|
4626
|
+
if (outputType === "structured" && researchOptions.structuredOutputSchema === void 0) {
|
|
4627
|
+
throw new Error(
|
|
4628
|
+
"Linkup research outputType 'structured' requires structuredOutputSchema."
|
|
4629
|
+
);
|
|
4630
|
+
}
|
|
4631
|
+
if (outputType === "sourcedAnswer" && researchOptions.structuredOutputSchema !== void 0) {
|
|
4632
|
+
throw new Error(
|
|
4633
|
+
"Linkup research structuredOutputSchema requires outputType 'structured'."
|
|
4634
|
+
);
|
|
4635
|
+
}
|
|
4636
|
+
const commonParams = {
|
|
4637
|
+
query: input,
|
|
4638
|
+
...researchOptions.includeDomains !== void 0 ? { includeDomains: researchOptions.includeDomains } : {},
|
|
4639
|
+
...researchOptions.excludeDomains !== void 0 ? { excludeDomains: researchOptions.excludeDomains } : {},
|
|
4640
|
+
...researchOptions.fromDate !== void 0 ? { fromDate: toDate(researchOptions.fromDate, "fromDate") } : {},
|
|
4641
|
+
...researchOptions.toDate !== void 0 ? { toDate: toDate(researchOptions.toDate, "toDate") } : {},
|
|
4642
|
+
...researchOptions.mode !== void 0 ? { mode: researchOptions.mode } : {},
|
|
4643
|
+
...researchOptions.reasoningDepth !== void 0 ? { reasoningDepth: researchOptions.reasoningDepth } : {}
|
|
4644
|
+
};
|
|
4645
|
+
if (outputType === "structured") {
|
|
4646
|
+
return {
|
|
4647
|
+
...commonParams,
|
|
4648
|
+
outputType,
|
|
4649
|
+
structuredOutputSchema: researchOptions.structuredOutputSchema
|
|
4650
|
+
};
|
|
4651
|
+
}
|
|
4652
|
+
return {
|
|
4653
|
+
...commonParams,
|
|
4654
|
+
outputType
|
|
4655
|
+
};
|
|
4656
|
+
}
|
|
4354
4657
|
function createClient4(config) {
|
|
4355
4658
|
const apiKey = resolveConfigValue(config.credentials?.api);
|
|
4356
4659
|
if (!apiKey) {
|
|
@@ -4361,6 +4664,40 @@ function createClient4(config) {
|
|
|
4361
4664
|
baseUrl: resolveConfigValue(config.baseUrl)
|
|
4362
4665
|
});
|
|
4363
4666
|
}
|
|
4667
|
+
function formatResearchTaskOutput(task) {
|
|
4668
|
+
const output = task.output;
|
|
4669
|
+
if (!output) {
|
|
4670
|
+
return {
|
|
4671
|
+
provider: linkupImplementation.id,
|
|
4672
|
+
text: "Linkup research completed without textual output."
|
|
4673
|
+
};
|
|
4674
|
+
}
|
|
4675
|
+
const outputRecord = asRecord2(output);
|
|
4676
|
+
const inputRecord = asRecord2(task.input);
|
|
4677
|
+
const outputType = inputRecord ? readString3(inputRecord.outputType) : void 0;
|
|
4678
|
+
const answer = outputRecord ? readString3(outputRecord.answer) : void 0;
|
|
4679
|
+
const sources = outputRecord ? readSources(outputRecord.sources) : [];
|
|
4680
|
+
if (outputType !== "structured" && answer !== void 0) {
|
|
4681
|
+
const lines = [answer];
|
|
4682
|
+
if (sources.length > 0) {
|
|
4683
|
+
lines.push("");
|
|
4684
|
+
lines.push("Sources:");
|
|
4685
|
+
for (const [index, source] of sources.entries()) {
|
|
4686
|
+
lines.push(`${index + 1}. ${source.title}`);
|
|
4687
|
+
lines.push(` ${source.url}`);
|
|
4688
|
+
}
|
|
4689
|
+
}
|
|
4690
|
+
return {
|
|
4691
|
+
provider: linkupImplementation.id,
|
|
4692
|
+
text: lines.join("\n").trimEnd(),
|
|
4693
|
+
itemCount: sources.length
|
|
4694
|
+
};
|
|
4695
|
+
}
|
|
4696
|
+
return {
|
|
4697
|
+
provider: linkupImplementation.id,
|
|
4698
|
+
text: formatJson(output)
|
|
4699
|
+
};
|
|
4700
|
+
}
|
|
4364
4701
|
function toSearchResult2(value) {
|
|
4365
4702
|
const entry = asRecord2(value);
|
|
4366
4703
|
if (!entry) {
|
|
@@ -4382,6 +4719,27 @@ function toSearchResult2(value) {
|
|
|
4382
4719
|
metadata: Object.keys(metadata).length > 0 ? metadata : void 0
|
|
4383
4720
|
};
|
|
4384
4721
|
}
|
|
4722
|
+
function readSources(value) {
|
|
4723
|
+
if (!Array.isArray(value)) {
|
|
4724
|
+
return [];
|
|
4725
|
+
}
|
|
4726
|
+
return value.flatMap((entry) => {
|
|
4727
|
+
const source = asRecord2(entry);
|
|
4728
|
+
if (!source) {
|
|
4729
|
+
return [];
|
|
4730
|
+
}
|
|
4731
|
+
const url2 = readString3(source.url);
|
|
4732
|
+
if (!url2) {
|
|
4733
|
+
return [];
|
|
4734
|
+
}
|
|
4735
|
+
return [
|
|
4736
|
+
{
|
|
4737
|
+
title: readString3(source.name) ?? url2,
|
|
4738
|
+
url: url2
|
|
4739
|
+
}
|
|
4740
|
+
];
|
|
4741
|
+
});
|
|
4742
|
+
}
|
|
4385
4743
|
function asRecord2(value) {
|
|
4386
4744
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
4387
4745
|
}
|
|
@@ -4435,6 +4793,17 @@ var linkupProvider = defineProvider({
|
|
|
4435
4793
|
input.options
|
|
4436
4794
|
);
|
|
4437
4795
|
}
|
|
4796
|
+
}),
|
|
4797
|
+
research: defineCapability({
|
|
4798
|
+
options: linkupImplementation.getToolOptionsSchema?.("research"),
|
|
4799
|
+
async execute(input, ctx) {
|
|
4800
|
+
return await linkupImplementation.research(
|
|
4801
|
+
input.input,
|
|
4802
|
+
ctx.config,
|
|
4803
|
+
ctx,
|
|
4804
|
+
input.options
|
|
4805
|
+
);
|
|
4806
|
+
}
|
|
4438
4807
|
})
|
|
4439
4808
|
}
|
|
4440
4809
|
});
|
|
@@ -5849,7 +6218,7 @@ var serperImplementation = {
|
|
|
5849
6218
|
requestOptions
|
|
5850
6219
|
);
|
|
5851
6220
|
const response = await fetch(
|
|
5852
|
-
|
|
6221
|
+
joinUrl2(resolveConfigValue(config.baseUrl), requestOptions.mode),
|
|
5853
6222
|
{
|
|
5854
6223
|
method: "POST",
|
|
5855
6224
|
headers: {
|
|
@@ -5884,7 +6253,7 @@ var serperImplementation = {
|
|
|
5884
6253
|
};
|
|
5885
6254
|
}
|
|
5886
6255
|
};
|
|
5887
|
-
function
|
|
6256
|
+
function joinUrl2(baseUrl, mode = "search") {
|
|
5888
6257
|
const base2 = (baseUrl ?? DEFAULT_BASE_URL3).replace(/\/+$/, "");
|
|
5889
6258
|
if (mode === "webpage" && base2 === DEFAULT_BASE_URL3) {
|
|
5890
6259
|
return DEFAULT_SCRAPE_URL;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-web-providers",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.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.
|
|
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"
|