feat: add Firecrawl provider support

This commit is contained in:
pi
2026-04-12 02:53:10 +01:00
parent 01d4411903
commit 98a966cade
20 changed files with 1570 additions and 366 deletions

View File

@@ -93,3 +93,101 @@ test("loadWebSearchConfig rejects a missing file with a helpful example message"
error.message.includes('"providers"'),
);
});
test("loadWebSearchConfig accepts self-hosted Firecrawl without an apiKey and normalizes its baseUrl", async () => {
const file = await writeTempConfig({
defaultProvider: "firecrawl-main",
providers: [
{
name: "firecrawl-main",
type: "firecrawl",
baseUrl: "https://firecrawl.internal.example/v2/",
fallbackProviders: ["exa-fallback"],
},
{
name: "exa-fallback",
type: "exa",
apiKey: "exa-test-key",
},
],
});
const config = await loadWebSearchConfig(file);
const provider = config.providersByName.get("firecrawl-main");
assert.equal(provider?.type, "firecrawl");
assert.equal(provider?.baseUrl, "https://firecrawl.internal.example/v2");
assert.equal(provider?.apiKey, undefined);
assert.deepEqual(provider?.fallbackProviders, ["exa-fallback"]);
});
test("loadWebSearchConfig rejects Firecrawl cloud config without an apiKey", async () => {
const file = await writeTempConfig({
defaultProvider: "firecrawl-main",
providers: [
{
name: "firecrawl-main",
type: "firecrawl",
},
],
});
await assert.rejects(
() => loadWebSearchConfig(file),
(error) =>
error instanceof WebSearchConfigError &&
/Firecrawl provider \"firecrawl-main\"/.test(error.message) &&
/apiKey/.test(error.message),
);
});
test("loadWebSearchConfig rejects unknown fallback providers", async () => {
const file = await writeTempConfig({
defaultProvider: "firecrawl-main",
providers: [
{
name: "firecrawl-main",
type: "firecrawl",
apiKey: "fc-test-key",
fallbackProviders: ["missing-provider"],
},
],
});
await assert.rejects(
() => loadWebSearchConfig(file),
(error) =>
error instanceof WebSearchConfigError &&
/fallback provider/.test(error.message) &&
/missing-provider/.test(error.message),
);
});
test("loadWebSearchConfig rejects fallback cycles", async () => {
const file = await writeTempConfig({
defaultProvider: "firecrawl-main",
providers: [
{
name: "firecrawl-main",
type: "firecrawl",
apiKey: "fc-test-key",
fallbackProviders: ["exa-fallback"],
},
{
name: "exa-fallback",
type: "exa",
apiKey: "exa-test-key",
fallbackProviders: ["firecrawl-main"],
},
],
});
await assert.rejects(
() => loadWebSearchConfig(file),
(error) =>
error instanceof WebSearchConfigError &&
/cycle/i.test(error.message) &&
/firecrawl-main/.test(error.message) &&
/exa-fallback/.test(error.message),
);
});