webfox 0.0.0-test.0 → 4.0.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/LICENSE +21 -0
- package/README.md +256 -3
- package/assets/webfox-lockup-dark.png +0 -0
- package/assets/webfox-lockup.png +0 -0
- package/dist/adapter-2YBFCBCZ.js +161 -0
- package/dist/adapter-67TIISDW.js +753 -0
- package/dist/adapter-AB7LKV7N.js +324 -0
- package/dist/adapter-AUYLTDPM.js +73 -0
- package/dist/adapter-HITGYUXN.js +494 -0
- package/dist/adapter-HPTMLH75.js +334 -0
- package/dist/adapter-I6OUKBZQ.js +492 -0
- package/dist/adapter-ISZQTZDP.js +166 -0
- package/dist/adapter-MAG7X663.js +230 -0
- package/dist/adapter-NEXSOHGT.js +232 -0
- package/dist/adapter-PADXKW43.js +162 -0
- package/dist/adapter-SDQX2533.js +123 -0
- package/dist/adapter-UAZNLDSS.js +237 -0
- package/dist/adapter-XBAQSK7I.js +282 -0
- package/dist/application-types.d.ts +34 -0
- package/dist/application.d.ts +2 -0
- package/dist/chunk-EF4VMDZL.js +85 -0
- package/dist/chunk-G2YAQEET.js +38 -0
- package/dist/chunk-I2UIACY3.js +12 -0
- package/dist/chunk-L425U55P.js +3051 -0
- package/dist/chunk-QRHDXM2L.js +31 -0
- package/dist/chunk-SMF5ALX7.js +762 -0
- package/dist/chunk-U4BLULLV.js +52 -0
- package/dist/chunk-WCTOYZVB.js +85 -0
- package/dist/chunk-X6ZBIHZC.js +21 -0
- package/dist/chunk-YZXM4U2C.js +21 -0
- package/dist/chunk-ZVTVERW5.js +20 -0
- package/dist/cli.js +641 -0
- package/dist/config.schema.json +4270 -0
- package/dist/configuration/defaults.d.ts +3 -0
- package/dist/configuration/file.d.ts +17 -0
- package/dist/configuration/options-schema.d.ts +2 -0
- package/dist/configuration/planning.d.ts +10 -0
- package/dist/configuration/schema.d.ts +9 -0
- package/dist/configuration/types.d.ts +37 -0
- package/dist/contents.d.ts +19 -0
- package/dist/domain.d.ts +89 -0
- package/dist/errors.d.ts +17 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +38 -0
- package/dist/package-metadata.d.ts +3 -0
- package/dist/pi-keybindings.d.ts +1 -0
- package/dist/pi-params.d.ts +3 -0
- package/dist/pi-render.d.ts +50 -0
- package/dist/pi-search-render.d.ts +4 -0
- package/dist/pi-text.d.ts +4 -0
- package/dist/pi-validation.d.ts +4 -0
- package/dist/pi.d.ts +2 -0
- package/dist/pi.js +563 -0
- package/dist/providers/brave/adapter.d.ts +7 -0
- package/dist/providers/brave/definition.d.ts +1 -0
- package/dist/providers/brave/types.d.ts +4 -0
- package/dist/providers/cli-json.d.ts +9 -0
- package/dist/providers/cloudflare/adapter.d.ts +5 -0
- package/dist/providers/cloudflare/definition.d.ts +1 -0
- package/dist/providers/cloudflare/types.d.ts +4 -0
- package/dist/providers/contract.d.ts +88 -0
- package/dist/providers/custom/adapter.d.ts +9 -0
- package/dist/providers/custom/definition.d.ts +1 -0
- package/dist/providers/custom/types.d.ts +9 -0
- package/dist/providers/definition.d.ts +32 -0
- package/dist/providers/exa/adapter.d.ts +8 -0
- package/dist/providers/exa/definition.d.ts +1 -0
- package/dist/providers/exa/types.d.ts +4 -0
- package/dist/providers/firecrawl/adapter.d.ts +7 -0
- package/dist/providers/firecrawl/definition.d.ts +1 -0
- package/dist/providers/firecrawl/types.d.ts +4 -0
- package/dist/providers/gemini/adapter.d.ts +14 -0
- package/dist/providers/gemini/definition.d.ts +1 -0
- package/dist/providers/gemini/types.d.ts +3 -0
- package/dist/providers/linkup/adapter.d.ts +7 -0
- package/dist/providers/linkup/definition.d.ts +1 -0
- package/dist/providers/linkup/types.d.ts +4 -0
- package/dist/providers/metadata.d.ts +15 -0
- package/dist/providers/ollama/adapter.d.ts +6 -0
- package/dist/providers/ollama/definition.d.ts +1 -0
- package/dist/providers/ollama/types.d.ts +4 -0
- package/dist/providers/openai/adapter.d.ts +7 -0
- package/dist/providers/openai/definition.d.ts +1 -0
- package/dist/providers/openai/types.d.ts +28 -0
- package/dist/providers/parallel/adapter.d.ts +6 -0
- package/dist/providers/parallel/definition.d.ts +1 -0
- package/dist/providers/parallel/types.d.ts +4 -0
- package/dist/providers/perplexity/adapter.d.ts +7 -0
- package/dist/providers/perplexity/definition.d.ts +1 -0
- package/dist/providers/perplexity/types.d.ts +4 -0
- package/dist/providers/registry.d.ts +16 -0
- package/dist/providers/serper/adapter.d.ts +5 -0
- package/dist/providers/serper/definition.d.ts +1 -0
- package/dist/providers/serper/types.d.ts +21 -0
- package/dist/providers/shared.d.ts +4 -0
- package/dist/providers/tavily/adapter.d.ts +6 -0
- package/dist/providers/tavily/definition.d.ts +1 -0
- package/dist/providers/tavily/types.d.ts +4 -0
- package/dist/providers/valyu/adapter.d.ts +8 -0
- package/dist/providers/valyu/definition.d.ts +1 -0
- package/dist/providers/valyu/types.d.ts +4 -0
- package/dist/render.d.ts +5 -0
- package/dist/runtime/credentials.d.ts +15 -0
- package/dist/runtime/duration.d.ts +2 -0
- package/dist/runtime/execute.d.ts +20 -0
- package/dist/runtime/lifecycle.d.ts +8 -0
- package/dist/runtime/outward.d.ts +11 -0
- package/dist/runtime/polling.d.ts +13 -0
- package/dist/runtime/process.d.ts +4 -0
- package/docs/cli-experience.md +177 -0
- package/docs/provider.md +177 -0
- package/docs/reference.md +181 -0
- package/example-config.yaml +36 -0
- package/examples/custom/README.md +57 -0
- package/examples/custom/provider.mjs +54 -0
- package/examples/custom/webfox.json +18 -0
- package/package.json +122 -8
- package/index.js +0 -2
|
@@ -0,0 +1,753 @@
|
|
|
1
|
+
import {
|
|
2
|
+
trimSnippet
|
|
3
|
+
} from "./chunk-X6ZBIHZC.js";
|
|
4
|
+
import {
|
|
5
|
+
httpError
|
|
6
|
+
} from "./chunk-U4BLULLV.js";
|
|
7
|
+
|
|
8
|
+
// src/providers/brave/adapter.ts
|
|
9
|
+
var DEFAULT_BASE_URL = "https://api.search.brave.com";
|
|
10
|
+
var BRAVE_API_VERSION = void 0;
|
|
11
|
+
var braveImplementation = {
|
|
12
|
+
async search(query, maxResults, config, context, options) {
|
|
13
|
+
const apiKey = requireKey(config.credentials?.search, "Brave search");
|
|
14
|
+
const callOptions = { ...options ?? {} };
|
|
15
|
+
const mode = readMode(callOptions.mode);
|
|
16
|
+
if (mode === "llm_context")
|
|
17
|
+
return await llmContext(
|
|
18
|
+
query,
|
|
19
|
+
maxResults,
|
|
20
|
+
config,
|
|
21
|
+
context,
|
|
22
|
+
apiKey,
|
|
23
|
+
callOptions
|
|
24
|
+
);
|
|
25
|
+
if (mode === "news")
|
|
26
|
+
return await news(
|
|
27
|
+
query,
|
|
28
|
+
maxResults,
|
|
29
|
+
config,
|
|
30
|
+
context,
|
|
31
|
+
apiKey,
|
|
32
|
+
callOptions
|
|
33
|
+
);
|
|
34
|
+
if (mode === "videos")
|
|
35
|
+
return await videos(
|
|
36
|
+
query,
|
|
37
|
+
maxResults,
|
|
38
|
+
config,
|
|
39
|
+
context,
|
|
40
|
+
apiKey,
|
|
41
|
+
callOptions
|
|
42
|
+
);
|
|
43
|
+
if (mode === "images")
|
|
44
|
+
return await images(
|
|
45
|
+
query,
|
|
46
|
+
maxResults,
|
|
47
|
+
config,
|
|
48
|
+
context,
|
|
49
|
+
apiKey,
|
|
50
|
+
callOptions
|
|
51
|
+
);
|
|
52
|
+
if (mode === "places")
|
|
53
|
+
return await places(
|
|
54
|
+
query,
|
|
55
|
+
maxResults,
|
|
56
|
+
config,
|
|
57
|
+
context,
|
|
58
|
+
apiKey,
|
|
59
|
+
callOptions
|
|
60
|
+
);
|
|
61
|
+
return await web(query, maxResults, config, context, apiKey, callOptions);
|
|
62
|
+
},
|
|
63
|
+
async answer(query, config, context, options) {
|
|
64
|
+
const raw = {
|
|
65
|
+
...options ?? {}
|
|
66
|
+
};
|
|
67
|
+
return await completion(query, config, context, buildAnswerRequest(raw));
|
|
68
|
+
},
|
|
69
|
+
async research(input, config, context, options) {
|
|
70
|
+
const raw = {
|
|
71
|
+
...options ?? {}
|
|
72
|
+
};
|
|
73
|
+
return await completion(input, config, context, buildResearchRequest(raw));
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
function requireKey(ref, label) {
|
|
77
|
+
const key = ref;
|
|
78
|
+
if (!key) throw new Error(`${label} is missing an API key`);
|
|
79
|
+
return key;
|
|
80
|
+
}
|
|
81
|
+
function base(config) {
|
|
82
|
+
return (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
83
|
+
}
|
|
84
|
+
function clamp(n, max = 20) {
|
|
85
|
+
return Math.max(1, Math.min(max, Math.trunc(n || 0)));
|
|
86
|
+
}
|
|
87
|
+
function readMode(v) {
|
|
88
|
+
return v === "llm_context" || v === "news" || v === "videos" || v === "images" || v === "places" ? v : "web";
|
|
89
|
+
}
|
|
90
|
+
function obj(v) {
|
|
91
|
+
return typeof v === "object" && v !== null && !Array.isArray(v) ? v : {};
|
|
92
|
+
}
|
|
93
|
+
function arr(v) {
|
|
94
|
+
return Array.isArray(v) ? v : [];
|
|
95
|
+
}
|
|
96
|
+
function str(v) {
|
|
97
|
+
return typeof v === "string" ? v : void 0;
|
|
98
|
+
}
|
|
99
|
+
function num(v) {
|
|
100
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
101
|
+
}
|
|
102
|
+
function pick(source, allowed) {
|
|
103
|
+
return Object.fromEntries(
|
|
104
|
+
allowed.filter((k) => source[k] !== void 0).map((k) => [k, source[k]])
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
function mergeOptions(options, key, allowed) {
|
|
108
|
+
return pick({ ...obj(options.common), ...obj(options[key]) }, allowed);
|
|
109
|
+
}
|
|
110
|
+
function headers(key, json = false) {
|
|
111
|
+
const result = { "X-Subscription-Token": key };
|
|
112
|
+
if (BRAVE_API_VERSION) {
|
|
113
|
+
result["Api-Version"] = BRAVE_API_VERSION;
|
|
114
|
+
}
|
|
115
|
+
if (json) {
|
|
116
|
+
result["content-type"] = "application/json";
|
|
117
|
+
}
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
function url(config, path, params) {
|
|
121
|
+
const u = new URL(`${base(config)}${path}`);
|
|
122
|
+
for (const [k, v] of Object.entries(params))
|
|
123
|
+
if (v !== void 0)
|
|
124
|
+
u.searchParams.set(k, Array.isArray(v) ? v.join(",") : String(v));
|
|
125
|
+
return u;
|
|
126
|
+
}
|
|
127
|
+
async function httpError2(response) {
|
|
128
|
+
const text = (await response.text()).trim();
|
|
129
|
+
return httpError(
|
|
130
|
+
response,
|
|
131
|
+
`Brave API request failed (${response.status}${response.statusText ? ` ${response.statusText}` : ""})${text ? `: ${text}` : "."}`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
async function web(query, maxResults, config, context, key, options) {
|
|
135
|
+
const params = {
|
|
136
|
+
q: query,
|
|
137
|
+
count: clamp(maxResults),
|
|
138
|
+
text_decorations: false,
|
|
139
|
+
...mergeOptions(options, "web", [
|
|
140
|
+
"country",
|
|
141
|
+
"search_lang",
|
|
142
|
+
"ui_lang",
|
|
143
|
+
"freshness",
|
|
144
|
+
"safesearch",
|
|
145
|
+
"spellcheck",
|
|
146
|
+
"goggles",
|
|
147
|
+
"extra_snippets",
|
|
148
|
+
"offset",
|
|
149
|
+
"enable_rich_callback"
|
|
150
|
+
])
|
|
151
|
+
};
|
|
152
|
+
const r = await fetch(url(config, "/res/v1/web/search", params), {
|
|
153
|
+
headers: headers(key),
|
|
154
|
+
signal: context.signal
|
|
155
|
+
});
|
|
156
|
+
if (!r.ok) throw await httpError2(r);
|
|
157
|
+
const p = obj(await r.json());
|
|
158
|
+
return {
|
|
159
|
+
provider: "brave",
|
|
160
|
+
results: arr(obj(p.web).results).map((e) => {
|
|
161
|
+
const x = obj(e);
|
|
162
|
+
const u = str(x.url) ?? "";
|
|
163
|
+
return {
|
|
164
|
+
title: str(x.title) || u || "Untitled",
|
|
165
|
+
url: u,
|
|
166
|
+
snippet: trimSnippet(
|
|
167
|
+
str(x.description) ?? arr(x.extra_snippets).join(" ")
|
|
168
|
+
),
|
|
169
|
+
metadata: x
|
|
170
|
+
};
|
|
171
|
+
}).slice(0, clamp(maxResults))
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
async function llmContext(query, maxResults, config, context, key, options) {
|
|
175
|
+
const params = {
|
|
176
|
+
q: query,
|
|
177
|
+
count: clamp(maxResults),
|
|
178
|
+
maximum_number_of_urls: clamp(maxResults),
|
|
179
|
+
maximum_number_of_tokens: 8192,
|
|
180
|
+
enable_source_metadata: true,
|
|
181
|
+
...mergeOptions(options, "llmContext", [
|
|
182
|
+
"count",
|
|
183
|
+
"maximum_number_of_urls",
|
|
184
|
+
"maximum_number_of_tokens",
|
|
185
|
+
"maximum_number_of_snippets",
|
|
186
|
+
"maximum_number_of_tokens_per_url",
|
|
187
|
+
"maximum_number_of_snippets_per_url",
|
|
188
|
+
"context_threshold_mode",
|
|
189
|
+
"enable_local",
|
|
190
|
+
"enable_source_metadata",
|
|
191
|
+
"country",
|
|
192
|
+
"search_lang",
|
|
193
|
+
"ui_lang",
|
|
194
|
+
"freshness",
|
|
195
|
+
"safesearch",
|
|
196
|
+
"spellcheck",
|
|
197
|
+
"goggles"
|
|
198
|
+
])
|
|
199
|
+
};
|
|
200
|
+
const r = await fetch(url(config, "/res/v1/llm/context", params), {
|
|
201
|
+
headers: headers(key),
|
|
202
|
+
signal: context.signal
|
|
203
|
+
});
|
|
204
|
+
if (!r.ok) throw await httpError2(r);
|
|
205
|
+
const p = obj(await r.json());
|
|
206
|
+
return {
|
|
207
|
+
provider: "brave",
|
|
208
|
+
results: collectLlmContextEntries(obj(p.grounding)).map((e) => {
|
|
209
|
+
const x = obj(e);
|
|
210
|
+
const snippets = arr(x.snippets).map(String);
|
|
211
|
+
const source = obj(x.source);
|
|
212
|
+
const u = str(x.url) ?? str(source.url) ?? "";
|
|
213
|
+
return {
|
|
214
|
+
title: str(x.title) ?? str(x.name) ?? str(source.title) ?? (u || "Untitled"),
|
|
215
|
+
url: u,
|
|
216
|
+
snippet: trimSnippet(snippets.join("\n\n"), 1200),
|
|
217
|
+
metadata: x
|
|
218
|
+
};
|
|
219
|
+
}).slice(0, clamp(maxResults))
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function collectLlmContextEntries(grounding) {
|
|
223
|
+
const entries = [];
|
|
224
|
+
for (const value of [grounding.generic, grounding.map]) {
|
|
225
|
+
entries.push(...arr(value).map(obj));
|
|
226
|
+
}
|
|
227
|
+
const poiEntries = Array.isArray(grounding.poi) ? grounding.poi.map(obj) : [obj(grounding.poi)];
|
|
228
|
+
entries.push(...poiEntries.filter((entry) => Object.keys(entry).length > 0));
|
|
229
|
+
return entries;
|
|
230
|
+
}
|
|
231
|
+
async function news(query, maxResults, config, context, key, options) {
|
|
232
|
+
const params = {
|
|
233
|
+
q: query,
|
|
234
|
+
count: clamp(maxResults, 50),
|
|
235
|
+
...mergeOptions(options, "news", [
|
|
236
|
+
"country",
|
|
237
|
+
"search_lang",
|
|
238
|
+
"ui_lang",
|
|
239
|
+
"freshness",
|
|
240
|
+
"safesearch",
|
|
241
|
+
"spellcheck",
|
|
242
|
+
"goggles",
|
|
243
|
+
"extra_snippets",
|
|
244
|
+
"offset",
|
|
245
|
+
"count"
|
|
246
|
+
])
|
|
247
|
+
};
|
|
248
|
+
const r = await fetch(url(config, "/res/v1/news/search", params), {
|
|
249
|
+
headers: headers(key),
|
|
250
|
+
signal: context.signal
|
|
251
|
+
});
|
|
252
|
+
if (!r.ok) throw await httpError2(r);
|
|
253
|
+
const p = obj(await r.json());
|
|
254
|
+
return {
|
|
255
|
+
provider: "brave",
|
|
256
|
+
results: arr(p.results).map((e) => {
|
|
257
|
+
const x = obj(e);
|
|
258
|
+
const u = str(x.url) ?? "";
|
|
259
|
+
const source = str(x.source) ?? str(x.source_name);
|
|
260
|
+
return {
|
|
261
|
+
title: str(x.title) || u || "Untitled",
|
|
262
|
+
url: u,
|
|
263
|
+
snippet: trimSnippet(
|
|
264
|
+
[
|
|
265
|
+
str(x.description) ?? arr(x.extra_snippets).join(" "),
|
|
266
|
+
source,
|
|
267
|
+
str(x.age) ?? str(x.page_age)
|
|
268
|
+
].filter(Boolean).join(" \u2014 ")
|
|
269
|
+
),
|
|
270
|
+
metadata: x
|
|
271
|
+
};
|
|
272
|
+
}).slice(0, clamp(maxResults, 50))
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
async function videos(query, maxResults, config, context, key, options) {
|
|
276
|
+
const params = {
|
|
277
|
+
q: query,
|
|
278
|
+
count: clamp(maxResults, 50),
|
|
279
|
+
...mergeOptions(options, "videos", [
|
|
280
|
+
"country",
|
|
281
|
+
"search_lang",
|
|
282
|
+
"ui_lang",
|
|
283
|
+
"freshness",
|
|
284
|
+
"safesearch",
|
|
285
|
+
"spellcheck",
|
|
286
|
+
"offset",
|
|
287
|
+
"count"
|
|
288
|
+
])
|
|
289
|
+
};
|
|
290
|
+
const r = await fetch(url(config, "/res/v1/videos/search", params), {
|
|
291
|
+
headers: headers(key),
|
|
292
|
+
signal: context.signal
|
|
293
|
+
});
|
|
294
|
+
if (!r.ok) throw await httpError2(r);
|
|
295
|
+
const p = obj(await r.json());
|
|
296
|
+
return {
|
|
297
|
+
provider: "brave",
|
|
298
|
+
results: arr(p.results).map((e) => {
|
|
299
|
+
const x = obj(e);
|
|
300
|
+
const u = str(x.url) ?? "";
|
|
301
|
+
const video = obj(x.video);
|
|
302
|
+
return {
|
|
303
|
+
title: str(x.title) || u || "Untitled",
|
|
304
|
+
url: u,
|
|
305
|
+
snippet: trimSnippet(
|
|
306
|
+
[
|
|
307
|
+
str(x.description),
|
|
308
|
+
str(video.creator) ?? str(x.creator),
|
|
309
|
+
str(video.duration) ?? str(x.duration),
|
|
310
|
+
num(video.views) ? `${num(video.views)} views` : void 0,
|
|
311
|
+
str(x.age) ?? str(x.page_age)
|
|
312
|
+
].filter(Boolean).join(" \u2014 ")
|
|
313
|
+
),
|
|
314
|
+
metadata: x
|
|
315
|
+
};
|
|
316
|
+
}).slice(0, clamp(maxResults, 50))
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
async function images(query, maxResults, config, context, key, options) {
|
|
320
|
+
const params = {
|
|
321
|
+
q: query,
|
|
322
|
+
count: clamp(maxResults),
|
|
323
|
+
...mergeOptions(options, "images", [
|
|
324
|
+
"country",
|
|
325
|
+
"search_lang",
|
|
326
|
+
"ui_lang",
|
|
327
|
+
"safesearch",
|
|
328
|
+
"spellcheck",
|
|
329
|
+
"count"
|
|
330
|
+
])
|
|
331
|
+
};
|
|
332
|
+
const r = await fetch(url(config, "/res/v1/images/search", params), {
|
|
333
|
+
headers: headers(key),
|
|
334
|
+
signal: context.signal
|
|
335
|
+
});
|
|
336
|
+
if (!r.ok) throw await httpError2(r);
|
|
337
|
+
const p = obj(await r.json());
|
|
338
|
+
return {
|
|
339
|
+
provider: "brave",
|
|
340
|
+
results: arr(p.results).map((e) => {
|
|
341
|
+
const x = obj(e);
|
|
342
|
+
const props = obj(x.properties);
|
|
343
|
+
const page = str(x.url) ?? str(x.source) ?? str(props.url) ?? "";
|
|
344
|
+
const image = str(props.url);
|
|
345
|
+
return {
|
|
346
|
+
title: str(x.title) || page || "Untitled",
|
|
347
|
+
url: page || image || "",
|
|
348
|
+
snippet: trimSnippet(
|
|
349
|
+
[str(x.description), str(x.publisher), image].filter(Boolean).join(" \u2014 ")
|
|
350
|
+
),
|
|
351
|
+
metadata: x
|
|
352
|
+
};
|
|
353
|
+
}).slice(0, clamp(maxResults))
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
async function places(query, maxResults, config, context, key, options) {
|
|
357
|
+
const placeOptions = obj(options.places);
|
|
358
|
+
const params = {
|
|
359
|
+
q: query,
|
|
360
|
+
count: clamp(maxResults),
|
|
361
|
+
...mergeOptions(options, "places", [
|
|
362
|
+
"country",
|
|
363
|
+
"search_lang",
|
|
364
|
+
"ui_lang",
|
|
365
|
+
"latitude",
|
|
366
|
+
"longitude",
|
|
367
|
+
"location",
|
|
368
|
+
"radius",
|
|
369
|
+
"units",
|
|
370
|
+
"safesearch",
|
|
371
|
+
"spellcheck",
|
|
372
|
+
"geoloc",
|
|
373
|
+
"count"
|
|
374
|
+
])
|
|
375
|
+
};
|
|
376
|
+
const r = await fetch(url(config, "/res/v1/local/place_search", params), {
|
|
377
|
+
headers: headers(key),
|
|
378
|
+
signal: context.signal
|
|
379
|
+
});
|
|
380
|
+
if (!r.ok) throw await httpError2(r);
|
|
381
|
+
const p = obj(await r.json());
|
|
382
|
+
const rows = arr(p.results).slice(0, clamp(maxResults));
|
|
383
|
+
const ids = rows.map((e) => str(obj(e).id)).filter((id) => id !== void 0);
|
|
384
|
+
const [detailsById, descriptionsById] = await Promise.all([
|
|
385
|
+
placeOptions.includeDetails && ids.length > 0 ? fetchPlaceDetails(
|
|
386
|
+
config,
|
|
387
|
+
context,
|
|
388
|
+
key,
|
|
389
|
+
ids,
|
|
390
|
+
pick(params, ["search_lang", "ui_lang", "units"])
|
|
391
|
+
) : Promise.resolve(/* @__PURE__ */ new Map()),
|
|
392
|
+
placeOptions.includeDescriptions && ids.length > 0 ? fetchPlaceDescriptions(config, context, key, ids) : Promise.resolve(/* @__PURE__ */ new Map())
|
|
393
|
+
]);
|
|
394
|
+
return {
|
|
395
|
+
provider: "brave",
|
|
396
|
+
results: rows.map((e) => {
|
|
397
|
+
const x = obj(e);
|
|
398
|
+
const id = str(x.id);
|
|
399
|
+
const details = id ? detailsById.get(id) : void 0;
|
|
400
|
+
const description = id ? descriptionsById.get(id) : void 0;
|
|
401
|
+
const u = str(x.url) ?? str(x.provider_url) ?? "";
|
|
402
|
+
return {
|
|
403
|
+
title: str(x.title) || u || "Untitled",
|
|
404
|
+
url: u,
|
|
405
|
+
snippet: trimSnippet(
|
|
406
|
+
[
|
|
407
|
+
placeDescriptionText(description) ?? str(x.description),
|
|
408
|
+
placeAddress(x, details),
|
|
409
|
+
arr(x.categories).join(", "),
|
|
410
|
+
placeRating(x, details)
|
|
411
|
+
].filter(Boolean).join(" \u2014 ")
|
|
412
|
+
),
|
|
413
|
+
metadata: {
|
|
414
|
+
...x,
|
|
415
|
+
...details ? { poiDetails: details } : {},
|
|
416
|
+
...description ? { poiDescription: description } : {}
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
})
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
async function fetchPlaceDetails(config, context, key, ids, options) {
|
|
423
|
+
const r = await fetch(
|
|
424
|
+
urlWithRepeatedArrays(config, "/res/v1/local/pois", {
|
|
425
|
+
ids: ids.slice(0, 20),
|
|
426
|
+
...options
|
|
427
|
+
}),
|
|
428
|
+
{
|
|
429
|
+
headers: headers(key),
|
|
430
|
+
signal: context.signal
|
|
431
|
+
}
|
|
432
|
+
);
|
|
433
|
+
if (!r.ok) throw await httpError2(r);
|
|
434
|
+
return indexById(arr(obj(await r.json()).results));
|
|
435
|
+
}
|
|
436
|
+
async function fetchPlaceDescriptions(config, context, key, ids) {
|
|
437
|
+
const r = await fetch(
|
|
438
|
+
urlWithRepeatedArrays(config, "/res/v1/local/descriptions", {
|
|
439
|
+
ids: ids.slice(0, 20)
|
|
440
|
+
}),
|
|
441
|
+
{
|
|
442
|
+
headers: headers(key),
|
|
443
|
+
signal: context.signal
|
|
444
|
+
}
|
|
445
|
+
);
|
|
446
|
+
if (!r.ok) throw await httpError2(r);
|
|
447
|
+
return indexById(arr(obj(await r.json()).results));
|
|
448
|
+
}
|
|
449
|
+
function urlWithRepeatedArrays(config, path, params) {
|
|
450
|
+
const u = new URL(`${base(config)}${path}`);
|
|
451
|
+
for (const [k, v] of Object.entries(params)) {
|
|
452
|
+
if (v === void 0) continue;
|
|
453
|
+
if (Array.isArray(v)) {
|
|
454
|
+
for (const item of v) {
|
|
455
|
+
u.searchParams.append(k, String(item));
|
|
456
|
+
}
|
|
457
|
+
} else {
|
|
458
|
+
u.searchParams.set(k, String(v));
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return u;
|
|
462
|
+
}
|
|
463
|
+
function indexById(values) {
|
|
464
|
+
const result = /* @__PURE__ */ new Map();
|
|
465
|
+
for (const value of values) {
|
|
466
|
+
const entry = obj(value);
|
|
467
|
+
const id = str(entry.id);
|
|
468
|
+
if (id) {
|
|
469
|
+
result.set(id, entry);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return result;
|
|
473
|
+
}
|
|
474
|
+
function placeDescriptionText(description) {
|
|
475
|
+
if (!description) return void 0;
|
|
476
|
+
return str(description.description) ?? str(description.text) ?? str(description.summary) ?? str(obj(description.description).text);
|
|
477
|
+
}
|
|
478
|
+
function placeAddress(place, details) {
|
|
479
|
+
return str(place.address) ?? str(obj(place.postal_address).displayAddress) ?? str(details?.address) ?? str(obj(details?.postal_address).displayAddress);
|
|
480
|
+
}
|
|
481
|
+
function placeRating(place, details) {
|
|
482
|
+
const rating = num(place.rating) ?? num(obj(place.rating).ratingValue) ?? num(details?.rating) ?? num(obj(details?.rating).ratingValue);
|
|
483
|
+
return rating === void 0 ? void 0 : `Rating: ${rating}`;
|
|
484
|
+
}
|
|
485
|
+
function buildAnswerRequest(raw) {
|
|
486
|
+
const webSearchOptions = pick(raw, [
|
|
487
|
+
"country",
|
|
488
|
+
"language",
|
|
489
|
+
"safesearch",
|
|
490
|
+
"enable_entities",
|
|
491
|
+
"enable_citations"
|
|
492
|
+
]);
|
|
493
|
+
if (webSearchOptions.enable_citations === void 0) {
|
|
494
|
+
webSearchOptions.enable_citations = true;
|
|
495
|
+
}
|
|
496
|
+
const stream = webSearchOptions.enable_citations === true || webSearchOptions.enable_entities === true;
|
|
497
|
+
return {
|
|
498
|
+
stream,
|
|
499
|
+
...pick(raw, ["model", "max_completion_tokens", "metadata", "seed"]),
|
|
500
|
+
web_search_options: webSearchOptions
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
function buildResearchRequest(raw) {
|
|
504
|
+
const webSearchOptions = pick(raw, [
|
|
505
|
+
"country",
|
|
506
|
+
"language",
|
|
507
|
+
"safesearch",
|
|
508
|
+
"enable_entities",
|
|
509
|
+
"research_allow_thinking",
|
|
510
|
+
"research_maximum_number_of_tokens_per_query",
|
|
511
|
+
"research_maximum_number_of_queries",
|
|
512
|
+
"research_maximum_number_of_iterations",
|
|
513
|
+
"research_maximum_number_of_seconds",
|
|
514
|
+
"research_maximum_number_of_results_per_query"
|
|
515
|
+
]);
|
|
516
|
+
webSearchOptions.enable_research = true;
|
|
517
|
+
webSearchOptions.enable_citations = false;
|
|
518
|
+
return {
|
|
519
|
+
stream: true,
|
|
520
|
+
...pick(raw, ["model", "max_completion_tokens", "metadata", "seed"]),
|
|
521
|
+
web_search_options: webSearchOptions
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
async function completion(input, config, context, request) {
|
|
525
|
+
const key = requireKey(config.credentials?.answers, "Brave Answers");
|
|
526
|
+
const body = {
|
|
527
|
+
model: "brave",
|
|
528
|
+
messages: [{ role: "user", content: input }],
|
|
529
|
+
...request
|
|
530
|
+
};
|
|
531
|
+
const r = await fetch(`${base(config)}/res/v1/chat/completions`, {
|
|
532
|
+
method: "POST",
|
|
533
|
+
headers: headers(key, true),
|
|
534
|
+
body: JSON.stringify(body),
|
|
535
|
+
signal: context.signal
|
|
536
|
+
});
|
|
537
|
+
if (!r.ok) throw await httpError2(r);
|
|
538
|
+
const text = await r.text();
|
|
539
|
+
const parsed = body.stream === false ? parseAnswerJson(text) : parseAnswerStream(text);
|
|
540
|
+
const lines = [parsed.answer.trim() || text.trim()];
|
|
541
|
+
if (parsed.citations.length) {
|
|
542
|
+
lines.push("", "Sources:");
|
|
543
|
+
parsed.citations.forEach((c, i) => {
|
|
544
|
+
lines.push(`${i + 1}. ${c.title ?? c.url ?? "Source"}`);
|
|
545
|
+
if (c.url) lines.push(` ${c.url}`);
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
const metadata = buildAnswerMetadata(parsed);
|
|
549
|
+
return {
|
|
550
|
+
provider: "brave",
|
|
551
|
+
text: lines.join("\n").trimEnd(),
|
|
552
|
+
itemCount: parsed.citations.length,
|
|
553
|
+
...metadata ? { metadata } : {}
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
function buildAnswerMetadata(parsed) {
|
|
557
|
+
const metadata = {};
|
|
558
|
+
if (parsed.usage) {
|
|
559
|
+
metadata.usage = parsed.usage;
|
|
560
|
+
}
|
|
561
|
+
if (parsed.entities.length) {
|
|
562
|
+
metadata.entities = parsed.entities;
|
|
563
|
+
}
|
|
564
|
+
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
565
|
+
}
|
|
566
|
+
function parseAnswerJson(text) {
|
|
567
|
+
const payload = obj(JSON.parse(text));
|
|
568
|
+
const choice = obj(arr(payload.choices)[0]);
|
|
569
|
+
const message = obj(choice.message);
|
|
570
|
+
const content = str(message.content) ?? "";
|
|
571
|
+
const tags = extractBraveTags(content);
|
|
572
|
+
return {
|
|
573
|
+
answer: tags.text,
|
|
574
|
+
citations: dedupeCitations(tags.citations),
|
|
575
|
+
entities: tags.entities,
|
|
576
|
+
usage: payload.usage ?? tags.usage
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
function parseAnswerStream(text) {
|
|
580
|
+
let rawAnswer = "";
|
|
581
|
+
for (const line of text.split(/\r?\n/)) {
|
|
582
|
+
const data = line.startsWith("data:") ? line.slice(5).trim() : line.trim();
|
|
583
|
+
if (!data || data === "[DONE]") continue;
|
|
584
|
+
try {
|
|
585
|
+
const parsed = obj(JSON.parse(data));
|
|
586
|
+
const choice = obj(arr(parsed.choices)[0]);
|
|
587
|
+
const delta = str(obj(choice.delta).content);
|
|
588
|
+
if (delta) {
|
|
589
|
+
rawAnswer += delta;
|
|
590
|
+
}
|
|
591
|
+
} catch {
|
|
592
|
+
rawAnswer += data;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
const tags = extractBraveTags(rawAnswer);
|
|
596
|
+
return {
|
|
597
|
+
answer: tags.text,
|
|
598
|
+
citations: dedupeCitations(tags.citations),
|
|
599
|
+
entities: tags.entities,
|
|
600
|
+
usage: tags.usage
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
function extractBraveTags(text) {
|
|
604
|
+
const citations = [];
|
|
605
|
+
const entities = [];
|
|
606
|
+
let usage;
|
|
607
|
+
let cleaned = "";
|
|
608
|
+
let offset = 0;
|
|
609
|
+
while (offset < text.length) {
|
|
610
|
+
const tagStart = text.indexOf("<", offset);
|
|
611
|
+
if (tagStart === -1) {
|
|
612
|
+
cleaned += text.slice(offset);
|
|
613
|
+
break;
|
|
614
|
+
}
|
|
615
|
+
const parsedTag = readBraveTagStart(text, tagStart);
|
|
616
|
+
if (!parsedTag) {
|
|
617
|
+
cleaned += text.slice(offset, tagStart + 1);
|
|
618
|
+
offset = tagStart + 1;
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
const jsonEnd = findJsonValueEnd(text, parsedTag.jsonStart);
|
|
622
|
+
if (jsonEnd === -1) {
|
|
623
|
+
cleaned += text.slice(offset);
|
|
624
|
+
break;
|
|
625
|
+
}
|
|
626
|
+
cleaned += text.slice(offset, tagStart);
|
|
627
|
+
const json = text.slice(parsedTag.jsonStart, jsonEnd + 1);
|
|
628
|
+
try {
|
|
629
|
+
const parsed = JSON.parse(json);
|
|
630
|
+
if (parsedTag.tag === "answer") {
|
|
631
|
+
cleaned += str(parsed.answer) ?? str(parsed.text) ?? "";
|
|
632
|
+
} else if (parsedTag.tag === "citation") {
|
|
633
|
+
citations.push({
|
|
634
|
+
title: str(parsed.title),
|
|
635
|
+
url: str(parsed.url)
|
|
636
|
+
});
|
|
637
|
+
} else if (parsedTag.tag === "enum_item") {
|
|
638
|
+
const item = obj(parsed);
|
|
639
|
+
entities.push(item);
|
|
640
|
+
cleaned += str(item.original_tokens) ?? str(item.name) ?? str(item.href) ?? "";
|
|
641
|
+
} else if (parsedTag.tag === "usage") {
|
|
642
|
+
usage = parsed;
|
|
643
|
+
}
|
|
644
|
+
} catch {
|
|
645
|
+
}
|
|
646
|
+
const closing = `</${parsedTag.tag}>`;
|
|
647
|
+
const abbreviatedClosing = `</${parsedTag.tag}`;
|
|
648
|
+
if (text.startsWith(closing, jsonEnd + 1)) {
|
|
649
|
+
offset = jsonEnd + 1 + closing.length;
|
|
650
|
+
} else if (text.startsWith(abbreviatedClosing, jsonEnd + 1)) {
|
|
651
|
+
offset = jsonEnd + 1 + abbreviatedClosing.length;
|
|
652
|
+
} else {
|
|
653
|
+
offset = jsonEnd + 1;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
return { text: cleaned, citations, entities, usage };
|
|
657
|
+
}
|
|
658
|
+
var BRAVE_STRUCTURED_TAGS = /* @__PURE__ */ new Set([
|
|
659
|
+
"analyzing",
|
|
660
|
+
"answer",
|
|
661
|
+
"blindspots",
|
|
662
|
+
"citation",
|
|
663
|
+
"enum_item",
|
|
664
|
+
"progress",
|
|
665
|
+
"queries",
|
|
666
|
+
"thinking",
|
|
667
|
+
"usage"
|
|
668
|
+
]);
|
|
669
|
+
function readBraveTagStart(text, tagStart) {
|
|
670
|
+
const match = /^<([A-Za-z_][A-Za-z0-9_-]*)(>)?[{[]/.exec(
|
|
671
|
+
text.slice(tagStart)
|
|
672
|
+
);
|
|
673
|
+
if (!match) return void 0;
|
|
674
|
+
const tag = match[1];
|
|
675
|
+
if (!BRAVE_STRUCTURED_TAGS.has(tag)) return void 0;
|
|
676
|
+
return {
|
|
677
|
+
tag,
|
|
678
|
+
jsonStart: tagStart + match[0].length - 1
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
function findJsonValueEnd(text, start) {
|
|
682
|
+
const first = text[start];
|
|
683
|
+
const closing = first === "{" ? "}" : first === "[" ? "]" : void 0;
|
|
684
|
+
if (!closing) return -1;
|
|
685
|
+
const stack = [];
|
|
686
|
+
let inString = false;
|
|
687
|
+
let escaped = false;
|
|
688
|
+
for (let index = start; index < text.length; index++) {
|
|
689
|
+
const char = text[index];
|
|
690
|
+
if (escaped) {
|
|
691
|
+
escaped = false;
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
if (char === "\\") {
|
|
695
|
+
escaped = true;
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
if (char === '"') {
|
|
699
|
+
inString = !inString;
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
if (inString) continue;
|
|
703
|
+
if (char === "{") {
|
|
704
|
+
stack.push("}");
|
|
705
|
+
} else if (char === "[") {
|
|
706
|
+
stack.push("]");
|
|
707
|
+
} else if (char === "}" || char === "]") {
|
|
708
|
+
if (stack.pop() !== char) return -1;
|
|
709
|
+
if (stack.length === 0) return index;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
return -1;
|
|
713
|
+
}
|
|
714
|
+
function dedupeCitations(citations) {
|
|
715
|
+
const seen = /* @__PURE__ */ new Set();
|
|
716
|
+
return citations.filter((citation) => {
|
|
717
|
+
const key = citation.url ?? citation.title;
|
|
718
|
+
if (!key) return false;
|
|
719
|
+
if (seen.has(key)) return false;
|
|
720
|
+
seen.add(key);
|
|
721
|
+
return true;
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
var adapter = {
|
|
725
|
+
async search(input, config, context) {
|
|
726
|
+
return await braveImplementation.search(
|
|
727
|
+
input.query,
|
|
728
|
+
input.maxResults,
|
|
729
|
+
config,
|
|
730
|
+
context,
|
|
731
|
+
input.options
|
|
732
|
+
);
|
|
733
|
+
},
|
|
734
|
+
async answer(input, config, context) {
|
|
735
|
+
return await braveImplementation.answer(
|
|
736
|
+
input.query,
|
|
737
|
+
config,
|
|
738
|
+
context,
|
|
739
|
+
input.options
|
|
740
|
+
);
|
|
741
|
+
},
|
|
742
|
+
async research(input, config, context) {
|
|
743
|
+
return await braveImplementation.research(
|
|
744
|
+
input.input,
|
|
745
|
+
config,
|
|
746
|
+
context,
|
|
747
|
+
input.options
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
export {
|
|
752
|
+
adapter
|
|
753
|
+
};
|