webfox 4.8.0 → 4.10.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 +2 -0
- package/dist/{adapter-YOHLAGYM.js → adapter-2UVAARTV.js} +1 -1
- package/dist/{adapter-UAZNLDSS.js → adapter-764OQFKY.js} +13 -9
- package/dist/{adapter-67TIISDW.js → adapter-A5WYGGZI.js} +1 -1
- package/dist/{adapter-I6OUKBZQ.js → adapter-C2VZHFUB.js} +1 -1
- package/dist/{adapter-QU2G7O25.js → adapter-EIDSGLOP.js} +3 -3
- package/dist/{adapter-2YBFCBCZ.js → adapter-G3I7JEO5.js} +1 -1
- package/dist/{adapter-PADXKW43.js → adapter-HRI5ZMGK.js} +82 -26
- package/dist/{adapter-ISZQTZDP.js → adapter-HTMXAJTU.js} +1 -1
- package/dist/adapter-JJSMYZXG.js +91 -0
- package/dist/{adapter-HPTMLH75.js → adapter-K6AMSUCR.js} +70 -30
- package/dist/{adapter-XBAQSK7I.js → adapter-L6BFKWGX.js} +65 -67
- package/dist/{adapter-SLTIAAML.js → adapter-OK2PTP6R.js} +2 -1
- package/dist/{adapter-SDQX2533.js → adapter-UIFBJKKF.js} +1 -1
- package/dist/{adapter-HITGYUXN.js → adapter-VMMS4YQG.js} +1 -1
- package/dist/{adapter-VW4FMMJC.js → adapter-ZYDUANQL.js} +1 -1
- package/dist/{chunk-IRKSK34I.js → chunk-3WBE6UEW.js} +3 -3
- package/dist/{chunk-GPACLYNT.js → chunk-57OOEOXI.js} +1 -0
- package/dist/{chunk-QERSNL3J.js → chunk-5FP7KNMC.js} +83 -15
- package/dist/chunk-FSTCLBSF.js +76 -0
- package/dist/{chunk-G7LMBPW4.js → chunk-TGULKHB5.js} +9 -12
- package/dist/cli.js +4 -4
- package/dist/config.schema.json +112 -0
- package/dist/document-schema.d.ts +4 -4
- package/dist/domain.d.ts +1 -1
- package/dist/durable.js +4 -4
- package/dist/index.js +4 -4
- package/dist/pi.js +4 -4
- package/dist/providers/contract.d.ts +1 -0
- package/dist/providers/keenable/adapter.d.ts +5 -0
- package/dist/providers/keenable/definition.d.ts +1 -0
- package/dist/providers/keenable/types.d.ts +4 -0
- package/dist/providers/registry.d.ts +1 -0
- package/dist/providers/shared.d.ts +16 -0
- package/docs/provider.md +59 -1
- package/package.json +7 -10
- package/dist/chunk-X6ZBIHZC.js +0 -21
package/README.md
CHANGED
|
@@ -144,6 +144,7 @@ Use different providers for different tasks:
|
|
|
144
144
|
| [Exa] | ✔︎ | ✔︎ | ✔︎ | ✔︎ |
|
|
145
145
|
| [Firecrawl] | ✔︎ | ✔︎ | ✔︎ | |
|
|
146
146
|
| [Gemini] | | | ✔︎ | ✔︎ |
|
|
147
|
+
| [Keenable] | ✔︎ | | | |
|
|
147
148
|
| [Linkup] | ✔︎ | ✔︎ | | ✔︎ |
|
|
148
149
|
| [Ollama] | ✔︎ | ✔︎ | | |
|
|
149
150
|
| [OpenAI] | ✔︎ | | ✔︎ | ✔︎ |
|
|
@@ -161,6 +162,7 @@ Use different providers for different tasks:
|
|
|
161
162
|
[Exa]: ./docs/provider.md#exa
|
|
162
163
|
[Firecrawl]: ./docs/provider.md#firecrawl
|
|
163
164
|
[Gemini]: ./docs/provider.md#gemini
|
|
165
|
+
[Keenable]: ./docs/provider.md#keenable
|
|
164
166
|
[Linkup]: ./docs/provider.md#linkup
|
|
165
167
|
[Ollama]: ./docs/provider.md#ollama
|
|
166
168
|
[OpenAI]: ./docs/provider.md#openai
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
import {
|
|
10
10
|
formatJson,
|
|
11
11
|
trimSnippet
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-FSTCLBSF.js";
|
|
13
13
|
import {
|
|
14
14
|
orderedContents
|
|
15
15
|
} from "./chunk-YZXM4U2C.js";
|
|
@@ -78,14 +78,18 @@ var valyuImplementation = {
|
|
|
78
78
|
error: "No content returned for this URL."
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
81
|
+
if (result.status === "failed" || result.status === "not_found") {
|
|
82
|
+
return {
|
|
83
|
+
url,
|
|
84
|
+
error: result.error ?? formatJson(result)
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const page = result;
|
|
88
|
+
return {
|
|
89
|
+
url: page.url,
|
|
90
|
+
...typeof page.content === "string" || typeof page.content === "number" ? { content: String(page.content) } : {},
|
|
91
|
+
...page.summary !== void 0 ? { summary: page.summary } : {},
|
|
92
|
+
metadata: page
|
|
89
93
|
};
|
|
90
94
|
})
|
|
91
95
|
};
|
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
SERPBASE_MODES,
|
|
3
3
|
serpbaseProvider,
|
|
4
4
|
validateOptions
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-5FP7KNMC.js";
|
|
6
6
|
import {
|
|
7
7
|
trimSnippet
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-FSTCLBSF.js";
|
|
9
|
+
import "./chunk-57OOEOXI.js";
|
|
10
10
|
import {
|
|
11
11
|
WebfoxError,
|
|
12
12
|
httpError
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
formatJson,
|
|
3
|
+
joinUrl,
|
|
4
|
+
requestJson,
|
|
3
5
|
trimSnippet
|
|
4
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-FSTCLBSF.js";
|
|
5
7
|
import {
|
|
6
8
|
orderedContents
|
|
7
9
|
} from "./chunk-YZXM4U2C.js";
|
|
@@ -10,15 +12,19 @@ import {
|
|
|
10
12
|
} from "./chunk-U4BLULLV.js";
|
|
11
13
|
|
|
12
14
|
// src/providers/exa/adapter.ts
|
|
13
|
-
|
|
15
|
+
var DEFAULT_BASE_URL = "https://api.exa.ai";
|
|
16
|
+
var DEFAULT_MAX_CHARACTERS = 1e4;
|
|
14
17
|
var exaImplementation = {
|
|
15
|
-
async search(query, maxResults, config,
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
async search(query, maxResults, config, context, searchOptions) {
|
|
19
|
+
const response = await exaRequest(
|
|
20
|
+
config,
|
|
21
|
+
"/search",
|
|
22
|
+
buildSearchBody(query, {
|
|
23
|
+
...searchOptions ?? {},
|
|
24
|
+
numResults: maxResults
|
|
25
|
+
}),
|
|
26
|
+
context
|
|
27
|
+
);
|
|
22
28
|
return {
|
|
23
29
|
provider: "exa",
|
|
24
30
|
results: (response.results ?? []).slice(0, maxResults).map((result) => ({
|
|
@@ -31,9 +37,13 @@ var exaImplementation = {
|
|
|
31
37
|
}))
|
|
32
38
|
};
|
|
33
39
|
},
|
|
34
|
-
async contents(urls, config,
|
|
35
|
-
const
|
|
36
|
-
|
|
40
|
+
async contents(urls, config, context, options) {
|
|
41
|
+
const response = await exaRequest(
|
|
42
|
+
config,
|
|
43
|
+
"/contents",
|
|
44
|
+
{ urls, ...options ?? {} },
|
|
45
|
+
context
|
|
46
|
+
);
|
|
37
47
|
const results = response.results ?? [];
|
|
38
48
|
return {
|
|
39
49
|
provider: "exa",
|
|
@@ -54,9 +64,21 @@ var exaImplementation = {
|
|
|
54
64
|
})
|
|
55
65
|
};
|
|
56
66
|
},
|
|
57
|
-
async answer(query, config,
|
|
58
|
-
const
|
|
59
|
-
|
|
67
|
+
async answer(query, config, context, options) {
|
|
68
|
+
const response = await exaRequest(
|
|
69
|
+
config,
|
|
70
|
+
"/answer",
|
|
71
|
+
{
|
|
72
|
+
query,
|
|
73
|
+
stream: false,
|
|
74
|
+
text: options?.text ?? false,
|
|
75
|
+
model: options?.model ?? "exa",
|
|
76
|
+
systemPrompt: options?.systemPrompt,
|
|
77
|
+
outputSchema: options?.outputSchema,
|
|
78
|
+
userLocation: options?.userLocation
|
|
79
|
+
},
|
|
80
|
+
context
|
|
81
|
+
);
|
|
60
82
|
const lines = [];
|
|
61
83
|
lines.push(
|
|
62
84
|
typeof response.answer === "string" ? response.answer : formatJson(response.answer)
|
|
@@ -81,15 +103,19 @@ var exaImplementation = {
|
|
|
81
103
|
async research(input, config, context, options) {
|
|
82
104
|
context.signal?.throwIfAborted();
|
|
83
105
|
context.onProgress?.("Exa is researching and synthesizing the report.");
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
type: "
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
106
|
+
const result = await exaRequest(
|
|
107
|
+
config,
|
|
108
|
+
"/search",
|
|
109
|
+
buildSearchBody(input, {
|
|
110
|
+
...options ?? {},
|
|
111
|
+
type: "deep-reasoning",
|
|
112
|
+
outputSchema: {
|
|
113
|
+
type: "text",
|
|
114
|
+
description: "A detailed Markdown research report answering the query. Cite sources with links, distinguish evidence from uncertainty, and include a conclusion."
|
|
115
|
+
}
|
|
116
|
+
}),
|
|
117
|
+
context
|
|
118
|
+
);
|
|
93
119
|
const content = result.output?.content;
|
|
94
120
|
if (typeof content !== "string" || !content.trim())
|
|
95
121
|
throw new WebfoxError(
|
|
@@ -113,12 +139,42 @@ var exaImplementation = {
|
|
|
113
139
|
};
|
|
114
140
|
}
|
|
115
141
|
};
|
|
116
|
-
function
|
|
142
|
+
function buildSearchBody(query, options) {
|
|
143
|
+
const { stream: _stream, ...rest } = options;
|
|
144
|
+
if (!("contents" in rest)) {
|
|
145
|
+
return {
|
|
146
|
+
query,
|
|
147
|
+
...rest,
|
|
148
|
+
contents: { text: { maxCharacters: DEFAULT_MAX_CHARACTERS } }
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
if (rest.contents === false || rest.contents == null) {
|
|
152
|
+
const { contents: _contents, ...withoutContents } = rest;
|
|
153
|
+
return { query, ...withoutContents };
|
|
154
|
+
}
|
|
155
|
+
return { query, ...rest };
|
|
156
|
+
}
|
|
157
|
+
async function exaRequest(config, path, body, context) {
|
|
117
158
|
const apiKey = config.credentials?.api;
|
|
118
159
|
if (!apiKey) {
|
|
119
160
|
throw new Error("is missing an API key");
|
|
120
161
|
}
|
|
121
|
-
return
|
|
162
|
+
return await requestJson(
|
|
163
|
+
joinUrl(config.baseUrl ?? DEFAULT_BASE_URL, path),
|
|
164
|
+
{ headers: { "x-api-key": apiKey }, body, signal: context.signal },
|
|
165
|
+
readExaError
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
function readExaError(payload, response) {
|
|
169
|
+
if (typeof payload !== "object" || payload === null) {
|
|
170
|
+
const text = typeof payload === "string" ? payload.trim().slice(0, 500) : "";
|
|
171
|
+
return text ? `Request failed with status ${response.status}: ${text}` : `Request failed with status ${response.status}.`;
|
|
172
|
+
}
|
|
173
|
+
const record = payload;
|
|
174
|
+
const envelope = typeof record.error === "object" && record.error !== null ? record.error : void 0;
|
|
175
|
+
const parts = envelope ? [envelope.message ?? record.message] : [record.error, record.message];
|
|
176
|
+
const message = parts.filter((part) => typeof part === "string" && part !== "").join(". ");
|
|
177
|
+
return message || `Request failed with status ${response.status}.`;
|
|
122
178
|
}
|
|
123
179
|
var adapter = {
|
|
124
180
|
async search(input, config, context) {
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import {
|
|
2
|
+
joinUrl,
|
|
3
|
+
requestJson,
|
|
4
|
+
trimSnippet
|
|
5
|
+
} from "./chunk-FSTCLBSF.js";
|
|
6
|
+
import {
|
|
7
|
+
WebfoxError
|
|
8
|
+
} from "./chunk-U4BLULLV.js";
|
|
9
|
+
|
|
10
|
+
// src/providers/keenable/adapter.ts
|
|
11
|
+
var DEFAULT_BASE_URL = "https://api.keenable.ai";
|
|
12
|
+
var MAX_RESULTS = 50;
|
|
13
|
+
var FORWARDED_OPTIONS = [
|
|
14
|
+
"mode",
|
|
15
|
+
"site",
|
|
16
|
+
"published_after",
|
|
17
|
+
"published_before",
|
|
18
|
+
"acquired_after",
|
|
19
|
+
"acquired_before",
|
|
20
|
+
"query_time",
|
|
21
|
+
"snippet_max_length"
|
|
22
|
+
];
|
|
23
|
+
var adapter = {
|
|
24
|
+
async search(request, config, context) {
|
|
25
|
+
const apiKey = config.credentials?.api;
|
|
26
|
+
if (!apiKey) throw new Error("Keenable search is missing an API key");
|
|
27
|
+
const maxResults = Math.max(
|
|
28
|
+
1,
|
|
29
|
+
Math.min(MAX_RESULTS, Math.trunc(request.maxResults || 0))
|
|
30
|
+
);
|
|
31
|
+
const options = asRecord(request.options) ?? {};
|
|
32
|
+
const payload = asRecord(
|
|
33
|
+
await requestJson(
|
|
34
|
+
joinUrl(config.baseUrl ?? DEFAULT_BASE_URL, "v1/search"),
|
|
35
|
+
{
|
|
36
|
+
method: "POST",
|
|
37
|
+
headers: { "X-API-Key": apiKey, "X-Keenable-Title": "webfox" },
|
|
38
|
+
body: {
|
|
39
|
+
query: request.query,
|
|
40
|
+
max_results: maxResults,
|
|
41
|
+
...Object.fromEntries(
|
|
42
|
+
FORWARDED_OPTIONS.flatMap(
|
|
43
|
+
(key) => options[key] === void 0 ? [] : [[key, options[key]]]
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
},
|
|
47
|
+
signal: context.signal
|
|
48
|
+
},
|
|
49
|
+
describeError
|
|
50
|
+
)
|
|
51
|
+
);
|
|
52
|
+
const entries = payload?.results;
|
|
53
|
+
if (!payload || !Array.isArray(entries)) {
|
|
54
|
+
throw new WebfoxError(
|
|
55
|
+
"PROVIDER_FAILURE",
|
|
56
|
+
"Keenable returned an invalid search response.",
|
|
57
|
+
{ retryable: false }
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
const results = entries.map(toSearchResult).filter((result) => result !== void 0);
|
|
61
|
+
return { provider: "keenable", results: results.slice(0, maxResults) };
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
function describeError(payload, response) {
|
|
65
|
+
const record = asRecord(payload);
|
|
66
|
+
const detail = text(record?.detail) ?? text(record?.error) ?? text(record?.message) ?? (typeof payload === "string" ? text(payload) : void 0) ?? (record ? JSON.stringify(record) : void 0);
|
|
67
|
+
const trimmed = detail?.trim().slice(0, 500);
|
|
68
|
+
return `Keenable search failed (HTTP ${response.status})${trimmed ? `: ${trimmed}` : "."}`;
|
|
69
|
+
}
|
|
70
|
+
function toSearchResult(entry) {
|
|
71
|
+
const record = asRecord(entry);
|
|
72
|
+
const url = text(record?.url);
|
|
73
|
+
if (!record || !url) return void 0;
|
|
74
|
+
const { title, url: _url, description, snippet, ...metadata } = record;
|
|
75
|
+
const excerpt = text(snippet) ?? text(description);
|
|
76
|
+
return {
|
|
77
|
+
title: text(title) ?? url,
|
|
78
|
+
url,
|
|
79
|
+
snippet: excerpt ? trimSnippet(excerpt) : "",
|
|
80
|
+
...Object.keys(metadata).length > 0 ? { metadata } : {}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function asRecord(value) {
|
|
84
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
85
|
+
}
|
|
86
|
+
function text(value) {
|
|
87
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
88
|
+
}
|
|
89
|
+
export {
|
|
90
|
+
adapter
|
|
91
|
+
};
|
|
@@ -4,45 +4,45 @@ import {
|
|
|
4
4
|
import "./chunk-I2UIACY3.js";
|
|
5
5
|
import "./chunk-WCTOYZVB.js";
|
|
6
6
|
import {
|
|
7
|
+
expectJsonBody,
|
|
7
8
|
formatJson,
|
|
9
|
+
joinUrl,
|
|
10
|
+
readResponseBody,
|
|
8
11
|
trimSnippet
|
|
9
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-FSTCLBSF.js";
|
|
10
13
|
import {
|
|
11
14
|
orderedContents
|
|
12
15
|
} from "./chunk-YZXM4U2C.js";
|
|
13
16
|
import {
|
|
17
|
+
WebfoxError,
|
|
14
18
|
asWebfoxError
|
|
15
19
|
} from "./chunk-U4BLULLV.js";
|
|
16
20
|
|
|
17
21
|
// src/providers/linkup/adapter.ts
|
|
18
|
-
|
|
19
|
-
LinkupClient
|
|
20
|
-
} from "linkup-sdk";
|
|
22
|
+
var DEFAULT_BASE_URL = "https://api.linkup.so/v1";
|
|
21
23
|
var linkupImplementation = {
|
|
22
|
-
async search(query, maxResults, config,
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
buildSearchParams(query, maxResults, {
|
|
24
|
+
async search(query, maxResults, config, context, options) {
|
|
25
|
+
const response = await linkupRequest(config, context, "/search", {
|
|
26
|
+
body: buildSearchParams(query, maxResults, {
|
|
26
27
|
...options ?? {}
|
|
27
28
|
})
|
|
28
|
-
);
|
|
29
|
+
});
|
|
29
30
|
return {
|
|
30
31
|
provider: "linkup",
|
|
31
32
|
results: (response.results ?? []).map(toSearchResult).filter((result) => result !== null).slice(0, maxResults)
|
|
32
33
|
};
|
|
33
34
|
},
|
|
34
|
-
async contents(urls, config,
|
|
35
|
-
const client = createClient(config);
|
|
35
|
+
async contents(urls, config, context, options) {
|
|
36
36
|
return {
|
|
37
37
|
provider: "linkup",
|
|
38
38
|
answers: await Promise.all(
|
|
39
39
|
urls.map(async (url) => {
|
|
40
40
|
try {
|
|
41
|
-
const response = await
|
|
42
|
-
buildFetchParams(url, {
|
|
41
|
+
const response = await linkupRequest(config, context, "/fetch", {
|
|
42
|
+
body: buildFetchParams(url, {
|
|
43
43
|
...options ?? {}
|
|
44
44
|
})
|
|
45
|
-
);
|
|
45
|
+
});
|
|
46
46
|
const metadata = Object.fromEntries(
|
|
47
47
|
Object.entries(response).filter(
|
|
48
48
|
([key, value]) => [
|
|
@@ -86,18 +86,20 @@ var linkupImplementation = {
|
|
|
86
86
|
poll: (id, researchContext) => linkupImplementation.pollResearch(id, config, researchContext)
|
|
87
87
|
});
|
|
88
88
|
},
|
|
89
|
-
async startResearch(input, config,
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
buildResearchParams(input, {
|
|
89
|
+
async startResearch(input, config, context, options) {
|
|
90
|
+
const task = await linkupRequest(config, context, "/research", {
|
|
91
|
+
body: buildResearchParams(input, {
|
|
93
92
|
...options ?? {}
|
|
94
93
|
})
|
|
95
|
-
);
|
|
94
|
+
});
|
|
96
95
|
return { id: task.id };
|
|
97
96
|
},
|
|
98
|
-
async pollResearch(id, config,
|
|
99
|
-
const
|
|
100
|
-
|
|
97
|
+
async pollResearch(id, config, context) {
|
|
98
|
+
const task = await linkupRequest(
|
|
99
|
+
config,
|
|
100
|
+
context,
|
|
101
|
+
`/research/${encodeURIComponent(id)}`
|
|
102
|
+
);
|
|
101
103
|
if (task.status === "completed") {
|
|
102
104
|
return {
|
|
103
105
|
status: "completed",
|
|
@@ -135,7 +137,7 @@ function buildSearchParams(query, maxResults, options) {
|
|
|
135
137
|
);
|
|
136
138
|
}
|
|
137
139
|
return {
|
|
138
|
-
query,
|
|
140
|
+
q: query,
|
|
139
141
|
depth: searchOptions.depth ?? "standard",
|
|
140
142
|
outputType: "searchResults",
|
|
141
143
|
maxResults,
|
|
@@ -179,7 +181,7 @@ function buildResearchParams(input, options) {
|
|
|
179
181
|
);
|
|
180
182
|
}
|
|
181
183
|
const commonParams = {
|
|
182
|
-
|
|
184
|
+
q: input,
|
|
183
185
|
...researchOptions.includeDomains !== void 0 ? { includeDomains: researchOptions.includeDomains } : {},
|
|
184
186
|
...researchOptions.excludeDomains !== void 0 ? { excludeDomains: researchOptions.excludeDomains } : {},
|
|
185
187
|
...researchOptions.fromDate !== void 0 ? { fromDate: toDate(researchOptions.fromDate, "fromDate") } : {},
|
|
@@ -191,7 +193,9 @@ function buildResearchParams(input, options) {
|
|
|
191
193
|
return {
|
|
192
194
|
...commonParams,
|
|
193
195
|
outputType,
|
|
194
|
-
structuredOutputSchema:
|
|
196
|
+
structuredOutputSchema: serializeSchema(
|
|
197
|
+
researchOptions.structuredOutputSchema
|
|
198
|
+
)
|
|
195
199
|
};
|
|
196
200
|
}
|
|
197
201
|
return {
|
|
@@ -199,14 +203,50 @@ function buildResearchParams(input, options) {
|
|
|
199
203
|
outputType
|
|
200
204
|
};
|
|
201
205
|
}
|
|
202
|
-
function
|
|
206
|
+
function serializeSchema(schema) {
|
|
207
|
+
return typeof schema === "string" ? schema : JSON.stringify(schema);
|
|
208
|
+
}
|
|
209
|
+
async function linkupRequest(config, context, path, init = {}) {
|
|
203
210
|
const apiKey = config.credentials?.api;
|
|
204
211
|
if (!apiKey) {
|
|
205
212
|
throw new Error("is missing an API key");
|
|
206
213
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
214
|
+
const response = await fetch(
|
|
215
|
+
joinUrl(config.baseUrl ?? DEFAULT_BASE_URL, path),
|
|
216
|
+
{
|
|
217
|
+
method: init.body === void 0 ? "GET" : "POST",
|
|
218
|
+
headers: {
|
|
219
|
+
Accept: "application/json",
|
|
220
|
+
Authorization: `Bearer ${apiKey}`,
|
|
221
|
+
...init.body === void 0 ? {} : { "Content-Type": "application/json" }
|
|
222
|
+
},
|
|
223
|
+
...init.body === void 0 ? {} : { body: JSON.stringify(init.body) },
|
|
224
|
+
signal: context.signal
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
const payload = await readResponseBody(response);
|
|
228
|
+
if (!response.ok) {
|
|
229
|
+
throw linkupError(response, payload);
|
|
230
|
+
}
|
|
231
|
+
return expectJsonBody(payload, response);
|
|
232
|
+
}
|
|
233
|
+
var NON_RETRYABLE_CODES = /* @__PURE__ */ new Set([
|
|
234
|
+
"INSUFFICIENT_FUNDS_CREDITS",
|
|
235
|
+
"EXCEED_BUDGET_LIMIT"
|
|
236
|
+
]);
|
|
237
|
+
function linkupError(response, payload) {
|
|
238
|
+
const error = asRecord(asRecord(payload)?.error);
|
|
239
|
+
const code = readString(error?.code);
|
|
240
|
+
const details = Array.isArray(error?.details) ? error.details : [];
|
|
241
|
+
const message = [
|
|
242
|
+
readString(error?.message) ?? (typeof payload === "string" && payload.trim() ? payload.trim().slice(0, 500) : `Linkup request failed with status ${response.status}.`),
|
|
243
|
+
...details.flatMap((detail) => {
|
|
244
|
+
const text = readString(asRecord(detail)?.message);
|
|
245
|
+
return text ? [text] : [];
|
|
246
|
+
})
|
|
247
|
+
].join(" ");
|
|
248
|
+
return new WebfoxError("PROVIDER_FAILURE", message, {
|
|
249
|
+
retryable: [408, 429, 500, 502, 503, 504].includes(response.status) && !(code && NON_RETRYABLE_CODES.has(code))
|
|
210
250
|
});
|
|
211
251
|
}
|
|
212
252
|
function formatResearchTaskOutput(task) {
|
|
@@ -298,7 +338,7 @@ function toDate(value, name) {
|
|
|
298
338
|
`Linkup option '${name}' must be a valid date string, timestamp, or Date.`
|
|
299
339
|
);
|
|
300
340
|
}
|
|
301
|
-
return date;
|
|
341
|
+
return date.toISOString().slice(0, 10);
|
|
302
342
|
}
|
|
303
343
|
var adapter = {
|
|
304
344
|
async search(input, config, context) {
|