@oh-my-pi/pi-coding-agent 15.10.7 → 15.10.8
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/CHANGELOG.md +14 -1
- package/dist/types/config/model-registry.d.ts +4 -2
- package/dist/types/config/model-resolver.d.ts +2 -0
- package/dist/types/config/settings-schema.d.ts +9 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +3 -1
- package/dist/types/mcp/oauth-discovery.d.ts +4 -1
- package/dist/types/mcp/oauth-flow.d.ts +6 -1
- package/dist/types/tools/fetch.d.ts +2 -1
- package/dist/types/tools/index.d.ts +3 -1
- package/dist/types/tools/report-tool-issue.d.ts +5 -0
- package/dist/types/web/kagi.d.ts +2 -1
- package/dist/types/web/parallel.d.ts +3 -0
- package/dist/types/web/search/providers/anthropic.d.ts +2 -1
- package/dist/types/web/search/providers/base.d.ts +2 -1
- package/dist/types/web/search/providers/brave.d.ts +2 -1
- package/dist/types/web/search/providers/codex.d.ts +2 -1
- package/dist/types/web/search/providers/exa.d.ts +2 -1
- package/dist/types/web/search/providers/gemini.d.ts +2 -1
- package/dist/types/web/search/providers/jina.d.ts +7 -2
- package/dist/types/web/search/providers/kagi.d.ts +7 -2
- package/dist/types/web/search/providers/kimi.d.ts +7 -2
- package/dist/types/web/search/providers/parallel.d.ts +2 -1
- package/dist/types/web/search/providers/perplexity.d.ts +2 -1
- package/dist/types/web/search/providers/searxng.d.ts +2 -1
- package/dist/types/web/search/providers/synthetic.d.ts +7 -3
- package/dist/types/web/search/providers/tavily.d.ts +2 -1
- package/dist/types/web/search/providers/zai.d.ts +2 -1
- package/package.json +9 -9
- package/src/config/model-registry.ts +13 -7
- package/src/config/model-resolver.ts +57 -2
- package/src/config/settings-schema.ts +6 -0
- package/src/extensibility/custom-tools/types.ts +3 -1
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/mcp/oauth-discovery.ts +8 -3
- package/src/mcp/oauth-flow.ts +12 -5
- package/src/modes/components/assistant-message.ts +28 -6
- package/src/tools/fetch.ts +22 -5
- package/src/tools/image-gen.ts +33 -11
- package/src/tools/index.ts +4 -2
- package/src/tools/report-tool-issue.ts +7 -1
- package/src/web/kagi.ts +5 -2
- package/src/web/parallel.ts +7 -3
- package/src/web/search/providers/anthropic.ts +5 -1
- package/src/web/search/providers/base.ts +2 -1
- package/src/web/search/providers/brave.ts +5 -2
- package/src/web/search/providers/codex.ts +6 -2
- package/src/web/search/providers/exa.ts +91 -8
- package/src/web/search/providers/gemini.ts +6 -0
- package/src/web/search/providers/jina.ts +15 -5
- package/src/web/search/providers/kagi.ts +9 -2
- package/src/web/search/providers/kimi.ts +18 -4
- package/src/web/search/providers/parallel.ts +6 -2
- package/src/web/search/providers/perplexity.ts +7 -4
- package/src/web/search/providers/searxng.ts +6 -2
- package/src/web/search/providers/synthetic.ts +9 -5
- package/src/web/search/providers/tavily.ts +4 -2
- package/src/web/search/providers/zai.ts +15 -4
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import {
|
|
12
12
|
ANTIGRAVITY_SYSTEM_INSTRUCTION,
|
|
13
13
|
type AuthStorage,
|
|
14
|
+
type FetchImpl,
|
|
14
15
|
getAntigravityUserAgent,
|
|
15
16
|
getGeminiCliHeaders,
|
|
16
17
|
} from "@oh-my-pi/pi-ai";
|
|
@@ -51,6 +52,7 @@ export interface GeminiSearchParams extends GeminiToolParams {
|
|
|
51
52
|
signal?: AbortSignal;
|
|
52
53
|
authStorage: AuthStorage;
|
|
53
54
|
sessionId?: string;
|
|
55
|
+
fetch?: FetchImpl;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
export function buildGeminiRequestTools(params: GeminiToolParams): Array<Record<string, Record<string, unknown>>> {
|
|
@@ -156,6 +158,7 @@ async function callGeminiSearch(
|
|
|
156
158
|
maxOutputTokens: number | undefined,
|
|
157
159
|
temperature: number | undefined,
|
|
158
160
|
toolParams: GeminiToolParams,
|
|
161
|
+
fetchImpl: FetchImpl | undefined,
|
|
159
162
|
signal: AbortSignal | undefined,
|
|
160
163
|
): Promise<{
|
|
161
164
|
answer: string;
|
|
@@ -237,6 +240,7 @@ async function callGeminiSearch(
|
|
|
237
240
|
|
|
238
241
|
const response = await fetchWithRetry(urlFor, {
|
|
239
242
|
...buildInit(),
|
|
243
|
+
fetch: fetchImpl,
|
|
240
244
|
maxAttempts: MAX_RETRIES + 1,
|
|
241
245
|
defaultDelayMs: attempt => BASE_DELAY_MS * 2 ** attempt,
|
|
242
246
|
maxDelayMs: RATE_LIMIT_BUDGET_MS,
|
|
@@ -405,6 +409,7 @@ export async function searchGemini(params: GeminiSearchParams): Promise<SearchRe
|
|
|
405
409
|
code_execution: params.code_execution,
|
|
406
410
|
url_context: params.url_context,
|
|
407
411
|
},
|
|
412
|
+
params.fetch,
|
|
408
413
|
params.signal,
|
|
409
414
|
);
|
|
410
415
|
|
|
@@ -450,6 +455,7 @@ export class GeminiProvider extends SearchProvider {
|
|
|
450
455
|
signal: params.signal,
|
|
451
456
|
authStorage: params.authStorage,
|
|
452
457
|
sessionId: params.sessionId,
|
|
458
|
+
fetch: params.fetch,
|
|
453
459
|
});
|
|
454
460
|
}
|
|
455
461
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* cleaned content.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { type AuthStorage, getEnvApiKey } from "@oh-my-pi/pi-ai";
|
|
8
|
+
import { type AuthStorage, type FetchImpl, getEnvApiKey } from "@oh-my-pi/pi-ai";
|
|
9
9
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
10
10
|
import { SearchProviderError } from "../../../web/search/types";
|
|
11
11
|
import type { SearchParams } from "./base";
|
|
@@ -13,11 +13,13 @@ import { SearchProvider } from "./base";
|
|
|
13
13
|
import { classifyProviderHttpError, withHardTimeout } from "./utils";
|
|
14
14
|
|
|
15
15
|
const JINA_SEARCH_URL = "https://s.jina.ai";
|
|
16
|
+
type SearchParamsWithFetch = SearchParams & { fetch?: FetchImpl };
|
|
16
17
|
|
|
17
18
|
export interface JinaSearchParams {
|
|
18
19
|
query: string;
|
|
19
20
|
num_results?: number;
|
|
20
21
|
signal?: AbortSignal;
|
|
22
|
+
fetch?: FetchImpl;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
interface JinaSearchResult {
|
|
@@ -34,9 +36,14 @@ export function findApiKey(): string | null {
|
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
/** Call Jina Reader search API. */
|
|
37
|
-
async function callJinaSearch(
|
|
39
|
+
async function callJinaSearch(
|
|
40
|
+
apiKey: string,
|
|
41
|
+
query: string,
|
|
42
|
+
signal?: AbortSignal,
|
|
43
|
+
fetchImpl: FetchImpl = fetch,
|
|
44
|
+
): Promise<JinaSearchResponse> {
|
|
38
45
|
const requestUrl = `${JINA_SEARCH_URL}/${encodeURIComponent(query)}`;
|
|
39
|
-
const response = await
|
|
46
|
+
const response = await fetchImpl(requestUrl, {
|
|
40
47
|
headers: {
|
|
41
48
|
Accept: "application/json",
|
|
42
49
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -62,7 +69,7 @@ export async function searchJina(params: JinaSearchParams): Promise<SearchRespon
|
|
|
62
69
|
throw new Error("JINA_API_KEY not found. Set it in environment or .env file.");
|
|
63
70
|
}
|
|
64
71
|
|
|
65
|
-
const response = await callJinaSearch(apiKey, params.query, params.signal);
|
|
72
|
+
const response = await callJinaSearch(apiKey, params.query, params.signal, params.fetch);
|
|
66
73
|
const sources: SearchSource[] = [];
|
|
67
74
|
|
|
68
75
|
for (const result of response) {
|
|
@@ -91,11 +98,14 @@ export class JinaProvider extends SearchProvider {
|
|
|
91
98
|
return !!findApiKey();
|
|
92
99
|
}
|
|
93
100
|
|
|
94
|
-
search(params:
|
|
101
|
+
search(params: SearchParamsWithFetch): Promise<SearchResponse> {
|
|
102
|
+
const fetchImpl = params.fetch;
|
|
103
|
+
|
|
95
104
|
return searchJina({
|
|
96
105
|
query: params.query,
|
|
97
106
|
num_results: params.numSearchResults ?? params.limit,
|
|
98
107
|
signal: params.signal,
|
|
108
|
+
fetch: fetchImpl,
|
|
99
109
|
});
|
|
100
110
|
}
|
|
101
111
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Thin wrapper that adapts shared Kagi API utilities to SearchResponse shape.
|
|
5
5
|
*/
|
|
6
|
-
import type { AuthStorage } from "@oh-my-pi/pi-ai";
|
|
6
|
+
import type { AuthStorage, FetchImpl } from "@oh-my-pi/pi-ai";
|
|
7
7
|
import type { SearchResponse } from "../../../web/search/types";
|
|
8
8
|
import { SearchProviderError } from "../../../web/search/types";
|
|
9
9
|
import { KagiApiError, searchWithKagi } from "../../kagi";
|
|
@@ -12,6 +12,8 @@ import type { SearchParams } from "./base";
|
|
|
12
12
|
import { SearchProvider } from "./base";
|
|
13
13
|
import { classifyProviderHttpError, toSearchSources } from "./utils";
|
|
14
14
|
|
|
15
|
+
type SearchParamsWithFetch = SearchParams & { fetch?: FetchImpl };
|
|
16
|
+
|
|
15
17
|
const DEFAULT_NUM_RESULTS = 10;
|
|
16
18
|
const MAX_NUM_RESULTS = 40;
|
|
17
19
|
|
|
@@ -23,6 +25,7 @@ export async function searchKagi(params: {
|
|
|
23
25
|
signal?: AbortSignal;
|
|
24
26
|
authStorage: AuthStorage;
|
|
25
27
|
sessionId?: string;
|
|
28
|
+
fetch?: FetchImpl;
|
|
26
29
|
}): Promise<SearchResponse> {
|
|
27
30
|
const numResults = clampNumResults(params.num_results, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS);
|
|
28
31
|
|
|
@@ -34,6 +37,7 @@ export async function searchKagi(params: {
|
|
|
34
37
|
recency: params.recency,
|
|
35
38
|
sessionId: params.sessionId,
|
|
36
39
|
signal: params.signal,
|
|
40
|
+
fetch: params.fetch,
|
|
37
41
|
},
|
|
38
42
|
params.authStorage,
|
|
39
43
|
);
|
|
@@ -66,7 +70,9 @@ export class KagiProvider extends SearchProvider {
|
|
|
66
70
|
return authStorage.hasAuth("kagi");
|
|
67
71
|
}
|
|
68
72
|
|
|
69
|
-
search(params:
|
|
73
|
+
search(params: SearchParamsWithFetch): Promise<SearchResponse> {
|
|
74
|
+
const fetchImpl = params.fetch;
|
|
75
|
+
|
|
70
76
|
return searchKagi({
|
|
71
77
|
query: params.query,
|
|
72
78
|
num_results: params.numSearchResults ?? params.limit,
|
|
@@ -74,6 +80,7 @@ export class KagiProvider extends SearchProvider {
|
|
|
74
80
|
signal: params.signal,
|
|
75
81
|
authStorage: params.authStorage,
|
|
76
82
|
sessionId: params.sessionId,
|
|
83
|
+
fetch: fetchImpl,
|
|
77
84
|
});
|
|
78
85
|
}
|
|
79
86
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Uses Moonshot Kimi Code search API to retrieve web results.
|
|
5
5
|
* Endpoint: POST https://api.kimi.com/coding/v1/search
|
|
6
6
|
*/
|
|
7
|
-
import { type ApiKey, type AuthStorage, withAuth } from "@oh-my-pi/pi-ai";
|
|
7
|
+
import { type ApiKey, type AuthStorage, type FetchImpl, withAuth } from "@oh-my-pi/pi-ai";
|
|
8
8
|
import { $env } from "@oh-my-pi/pi-utils";
|
|
9
9
|
|
|
10
10
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
@@ -14,6 +14,8 @@ import type { SearchParams } from "./base";
|
|
|
14
14
|
import { SearchProvider } from "./base";
|
|
15
15
|
import { classifyProviderHttpError, withHardTimeout } from "./utils";
|
|
16
16
|
|
|
17
|
+
type SearchParamsWithFetch = SearchParams & { fetch?: FetchImpl };
|
|
18
|
+
|
|
17
19
|
const KIMI_SEARCH_URL = "https://api.kimi.com/coding/v1/search";
|
|
18
20
|
|
|
19
21
|
const DEFAULT_NUM_RESULTS = 10;
|
|
@@ -27,6 +29,7 @@ export interface KimiSearchParams {
|
|
|
27
29
|
signal?: AbortSignal;
|
|
28
30
|
authStorage: AuthStorage;
|
|
29
31
|
sessionId?: string;
|
|
32
|
+
fetch?: FetchImpl;
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
interface KimiSearchResult {
|
|
@@ -78,9 +81,16 @@ async function resolveKey(
|
|
|
78
81
|
|
|
79
82
|
async function callKimiSearch(
|
|
80
83
|
apiKey: string,
|
|
81
|
-
params: {
|
|
84
|
+
params: {
|
|
85
|
+
query: string;
|
|
86
|
+
limit: number;
|
|
87
|
+
includeContent: boolean;
|
|
88
|
+
signal?: AbortSignal;
|
|
89
|
+
fetch?: FetchImpl;
|
|
90
|
+
},
|
|
82
91
|
): Promise<{ response: KimiSearchResponse; requestId?: string }> {
|
|
83
|
-
const
|
|
92
|
+
const fetchImpl = params.fetch ?? fetch;
|
|
93
|
+
const response = await fetchImpl(resolveBaseUrl(), {
|
|
84
94
|
method: "POST",
|
|
85
95
|
headers: {
|
|
86
96
|
Accept: "application/json",
|
|
@@ -130,6 +140,7 @@ export async function searchKimi(params: KimiSearchParams): Promise<SearchRespon
|
|
|
130
140
|
limit,
|
|
131
141
|
includeContent: params.include_content ?? false,
|
|
132
142
|
signal: params.signal,
|
|
143
|
+
fetch: params.fetch,
|
|
133
144
|
}),
|
|
134
145
|
{ signal: params.signal },
|
|
135
146
|
);
|
|
@@ -170,13 +181,16 @@ export class KimiProvider extends SearchProvider {
|
|
|
170
181
|
);
|
|
171
182
|
}
|
|
172
183
|
|
|
173
|
-
search(params:
|
|
184
|
+
search(params: SearchParamsWithFetch): Promise<SearchResponse> {
|
|
185
|
+
const fetchImpl = params.fetch;
|
|
186
|
+
|
|
174
187
|
return searchKimi({
|
|
175
188
|
query: params.query,
|
|
176
189
|
num_results: params.numSearchResults ?? params.limit,
|
|
177
190
|
signal: params.signal,
|
|
178
191
|
authStorage: params.authStorage,
|
|
179
192
|
sessionId: params.sessionId,
|
|
193
|
+
fetch: fetchImpl,
|
|
180
194
|
});
|
|
181
195
|
}
|
|
182
196
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ApiKey, type AuthStorage, getEnvApiKey, withAuth } from "@oh-my-pi/pi-ai";
|
|
1
|
+
import { type ApiKey, type AuthStorage, type FetchImpl, getEnvApiKey, withAuth } from "@oh-my-pi/pi-ai";
|
|
2
2
|
import type { SearchResponse } from "../../../web/search/types";
|
|
3
3
|
import { SearchProviderError } from "../../../web/search/types";
|
|
4
4
|
import { ParallelApiError, type ParallelSearchResult, type ParallelSearchSource } from "../../parallel";
|
|
@@ -112,6 +112,7 @@ async function searchWithAuthStorage(
|
|
|
112
112
|
queries: string[],
|
|
113
113
|
params: {
|
|
114
114
|
signal?: AbortSignal;
|
|
115
|
+
fetch?: FetchImpl;
|
|
115
116
|
},
|
|
116
117
|
authStorage: AuthStorage,
|
|
117
118
|
sessionId?: string,
|
|
@@ -131,7 +132,7 @@ async function searchWithAuthStorage(
|
|
|
131
132
|
return withAuth(
|
|
132
133
|
keyOrResolver,
|
|
133
134
|
async key => {
|
|
134
|
-
const response = await fetch(PARALLEL_SEARCH_URL, {
|
|
135
|
+
const response = await (params.fetch ?? fetch)(PARALLEL_SEARCH_URL, {
|
|
135
136
|
method: "POST",
|
|
136
137
|
headers: {
|
|
137
138
|
Accept: "application/json",
|
|
@@ -165,6 +166,7 @@ export async function searchParallel(
|
|
|
165
166
|
query: string;
|
|
166
167
|
num_results?: number;
|
|
167
168
|
signal?: AbortSignal;
|
|
169
|
+
fetch?: FetchImpl;
|
|
168
170
|
},
|
|
169
171
|
authStorage: AuthStorage,
|
|
170
172
|
sessionId?: string,
|
|
@@ -177,6 +179,7 @@ export async function searchParallel(
|
|
|
177
179
|
[params.query],
|
|
178
180
|
{
|
|
179
181
|
signal: params.signal,
|
|
182
|
+
fetch: params.fetch,
|
|
180
183
|
},
|
|
181
184
|
authStorage,
|
|
182
185
|
sessionId,
|
|
@@ -213,6 +216,7 @@ export class ParallelProvider extends SearchProvider {
|
|
|
213
216
|
query: params.query,
|
|
214
217
|
num_results: params.numSearchResults ?? params.limit,
|
|
215
218
|
signal: params.signal,
|
|
219
|
+
fetch: params.fetch,
|
|
216
220
|
},
|
|
217
221
|
params.authStorage,
|
|
218
222
|
params.sessionId,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* - Anonymous via `www.perplexity.ai/rest/sse/perplexity_ask`
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { type AuthStorage, getEnvApiKey } from "@oh-my-pi/pi-ai";
|
|
11
|
+
import { type AuthStorage, type FetchImpl, getEnvApiKey } from "@oh-my-pi/pi-ai";
|
|
12
12
|
import { $env, readSseJson } from "@oh-my-pi/pi-utils";
|
|
13
13
|
import type {
|
|
14
14
|
PerplexityMessageOutput,
|
|
@@ -275,6 +275,7 @@ export interface PerplexitySearchParams {
|
|
|
275
275
|
num_search_results?: number;
|
|
276
276
|
authStorage: AuthStorage;
|
|
277
277
|
sessionId?: string;
|
|
278
|
+
fetch?: FetchImpl;
|
|
278
279
|
}
|
|
279
280
|
|
|
280
281
|
/** Find PERPLEXITY_API_KEY from environment or .env files (also checks PPLX_API_KEY) */
|
|
@@ -356,9 +357,10 @@ async function findPerplexityAuth(
|
|
|
356
357
|
async function callPerplexityApi(
|
|
357
358
|
apiKey: string,
|
|
358
359
|
request: PerplexityRequest,
|
|
360
|
+
fetchImpl: FetchImpl | undefined,
|
|
359
361
|
signal?: AbortSignal,
|
|
360
362
|
): Promise<PerplexityResponse> {
|
|
361
|
-
const response = await fetch(PERPLEXITY_API_URL, {
|
|
363
|
+
const response = await (fetchImpl ?? fetch)(PERPLEXITY_API_URL, {
|
|
362
364
|
method: "POST",
|
|
363
365
|
headers: {
|
|
364
366
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -505,7 +507,7 @@ async function callPerplexityAsk(
|
|
|
505
507
|
requestParams.source = "default";
|
|
506
508
|
}
|
|
507
509
|
|
|
508
|
-
const response = await fetch(PERPLEXITY_OAUTH_ASK_URL, {
|
|
510
|
+
const response = await (params.fetch ?? fetch)(PERPLEXITY_OAUTH_ASK_URL, {
|
|
509
511
|
method: "POST",
|
|
510
512
|
headers,
|
|
511
513
|
body: JSON.stringify({
|
|
@@ -686,7 +688,7 @@ export async function searchPerplexity(params: PerplexitySearchParams): Promise<
|
|
|
686
688
|
request.search_recency_filter = params.search_recency_filter;
|
|
687
689
|
}
|
|
688
690
|
|
|
689
|
-
const response = await callPerplexityApi(auth.token, request, params.signal);
|
|
691
|
+
const response = await callPerplexityApi(auth.token, request, params.fetch, params.signal);
|
|
690
692
|
const result = parseResponse(response);
|
|
691
693
|
result.authMode = "api_key";
|
|
692
694
|
return applySourceLimit(result, params.num_results);
|
|
@@ -722,6 +724,7 @@ export class PerplexityProvider extends SearchProvider {
|
|
|
722
724
|
num_results: params.limit,
|
|
723
725
|
authStorage: params.authStorage,
|
|
724
726
|
sessionId: params.sessionId,
|
|
727
|
+
fetch: params.fetch,
|
|
725
728
|
});
|
|
726
729
|
}
|
|
727
730
|
}
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* Reference: https://docs.searxng.org/dev/search_api.html
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
import type { AuthStorage } from "@oh-my-pi/pi-ai";
|
|
28
|
+
import type { AuthStorage, FetchImpl } from "@oh-my-pi/pi-ai";
|
|
29
29
|
|
|
30
30
|
import { settings } from "../../../config/settings";
|
|
31
31
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
@@ -207,12 +207,13 @@ async function callSearXNGSearch(
|
|
|
207
207
|
categories?: string;
|
|
208
208
|
language?: string;
|
|
209
209
|
signal?: AbortSignal;
|
|
210
|
+
fetch?: FetchImpl;
|
|
210
211
|
},
|
|
211
212
|
auth: SearXNGAuth | null,
|
|
212
213
|
): Promise<SearXNGResponse> {
|
|
213
214
|
const { url, headers } = buildRequest(endpoint, params, auth);
|
|
214
215
|
|
|
215
|
-
const response = await fetch(url, {
|
|
216
|
+
const response = await (params.fetch ?? fetch)(url, {
|
|
216
217
|
headers,
|
|
217
218
|
signal: withHardTimeout(params.signal),
|
|
218
219
|
});
|
|
@@ -233,6 +234,7 @@ export async function searchSearXNG(params: {
|
|
|
233
234
|
num_results?: number;
|
|
234
235
|
recency?: "day" | "week" | "month" | "year";
|
|
235
236
|
signal?: AbortSignal;
|
|
237
|
+
fetch?: FetchImpl;
|
|
236
238
|
}): Promise<SearchResponse> {
|
|
237
239
|
const numResults = clampNumResults(params.num_results, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS);
|
|
238
240
|
|
|
@@ -260,6 +262,7 @@ export async function searchSearXNG(params: {
|
|
|
260
262
|
...params,
|
|
261
263
|
categories,
|
|
262
264
|
language,
|
|
265
|
+
fetch: params.fetch,
|
|
263
266
|
},
|
|
264
267
|
auth,
|
|
265
268
|
);
|
|
@@ -304,6 +307,7 @@ export class SearXNGProvider extends SearchProvider {
|
|
|
304
307
|
num_results: params.numSearchResults ?? params.limit,
|
|
305
308
|
recency: params.recency,
|
|
306
309
|
signal: params.signal,
|
|
310
|
+
fetch: params.fetch,
|
|
307
311
|
});
|
|
308
312
|
}
|
|
309
313
|
}
|
|
@@ -5,13 +5,15 @@
|
|
|
5
5
|
* Endpoint: POST https://api.synthetic.new/v2/search
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { type ApiKey, type AuthStorage, getEnvApiKey, withAuth } from "@oh-my-pi/pi-ai";
|
|
8
|
+
import { type ApiKey, type AuthStorage, type FetchImpl, getEnvApiKey, withAuth } from "@oh-my-pi/pi-ai";
|
|
9
9
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
10
10
|
import { SearchProviderError } from "../../../web/search/types";
|
|
11
11
|
import type { SearchParams } from "./base";
|
|
12
12
|
import { SearchProvider } from "./base";
|
|
13
13
|
import { classifyProviderHttpError, withHardTimeout } from "./utils";
|
|
14
14
|
|
|
15
|
+
type SearchParamsWithFetch = SearchParams & { fetch?: FetchImpl };
|
|
16
|
+
|
|
15
17
|
const SYNTHETIC_SEARCH_URL = "https://api.synthetic.new/v2/search";
|
|
16
18
|
|
|
17
19
|
interface SyntheticSearchResult {
|
|
@@ -39,8 +41,9 @@ async function callSyntheticSearch(
|
|
|
39
41
|
apiKey: string,
|
|
40
42
|
query: string,
|
|
41
43
|
signal?: AbortSignal,
|
|
44
|
+
fetchImpl: FetchImpl = fetch,
|
|
42
45
|
): Promise<SyntheticSearchResponse> {
|
|
43
|
-
const response = await
|
|
46
|
+
const response = await fetchImpl(SYNTHETIC_SEARCH_URL, {
|
|
44
47
|
method: "POST",
|
|
45
48
|
headers: {
|
|
46
49
|
"Content-Type": "application/json",
|
|
@@ -65,12 +68,13 @@ async function callSyntheticSearch(
|
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
/** Execute Synthetic web search. */
|
|
68
|
-
export async function searchSynthetic(params:
|
|
71
|
+
export async function searchSynthetic(params: SearchParamsWithFetch): Promise<SearchResponse> {
|
|
69
72
|
const keyOrResolver: ApiKey = params.authStorage.resolver("synthetic", {
|
|
70
73
|
sessionId: params.sessionId,
|
|
71
74
|
});
|
|
72
75
|
|
|
73
|
-
const
|
|
76
|
+
const fetchImpl = params.fetch;
|
|
77
|
+
const data = await withAuth(keyOrResolver, key => callSyntheticSearch(key, params.query, params.signal, fetchImpl), {
|
|
74
78
|
signal: params.signal,
|
|
75
79
|
missingKeyMessage: "Synthetic credentials not found. Set SYNTHETIC_API_KEY or login with 'omp /login synthetic'.",
|
|
76
80
|
});
|
|
@@ -104,7 +108,7 @@ export class SyntheticProvider extends SearchProvider {
|
|
|
104
108
|
return authStorage.hasAuth("synthetic") || !!getEnvApiKey("synthetic");
|
|
105
109
|
}
|
|
106
110
|
|
|
107
|
-
search(params:
|
|
111
|
+
search(params: SearchParamsWithFetch): Promise<SearchResponse> {
|
|
108
112
|
return searchSynthetic(params);
|
|
109
113
|
}
|
|
110
114
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Uses Tavily's agent-focused search API to return structured results with an
|
|
5
5
|
* optional synthesized answer.
|
|
6
6
|
*/
|
|
7
|
-
import { type ApiKey, type AuthStorage, getEnvApiKey, withAuth } from "@oh-my-pi/pi-ai";
|
|
7
|
+
import { type ApiKey, type AuthStorage, type FetchImpl, getEnvApiKey, withAuth } from "@oh-my-pi/pi-ai";
|
|
8
8
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
9
9
|
import { SearchProviderError } from "../../../web/search/types";
|
|
10
10
|
import { clampNumResults, dateToAgeSeconds } from "../utils";
|
|
@@ -21,6 +21,7 @@ export interface TavilySearchParams {
|
|
|
21
21
|
num_results?: number;
|
|
22
22
|
recency?: "day" | "week" | "month" | "year";
|
|
23
23
|
signal?: AbortSignal;
|
|
24
|
+
fetch?: FetchImpl;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
interface TavilySearchResult {
|
|
@@ -89,7 +90,7 @@ export function buildRequestBody(params: TavilySearchParams): Record<string, unk
|
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
async function callTavilySearch(apiKey: string, params: TavilySearchParams): Promise<TavilySearchResponse> {
|
|
92
|
-
const response = await fetch(TAVILY_SEARCH_URL, {
|
|
93
|
+
const response = await (params.fetch ?? fetch)(TAVILY_SEARCH_URL, {
|
|
93
94
|
method: "POST",
|
|
94
95
|
headers: {
|
|
95
96
|
"Content-Type": "application/json",
|
|
@@ -126,6 +127,7 @@ export async function searchTavily(params: SearchParams): Promise<SearchResponse
|
|
|
126
127
|
num_results: params.numSearchResults ?? params.limit,
|
|
127
128
|
recency: params.recency,
|
|
128
129
|
signal: params.signal,
|
|
130
|
+
fetch: params.fetch,
|
|
129
131
|
};
|
|
130
132
|
const keyOrResolver: ApiKey = params.authStorage.resolver("tavily", {
|
|
131
133
|
sessionId: params.sessionId,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Calls Z.AI's remote MCP server (`webSearchPrime`) and adapts results into
|
|
5
5
|
* the unified SearchResponse shape used by the web search tool.
|
|
6
6
|
*/
|
|
7
|
-
import { type ApiKey, type AuthStorage, getEnvApiKey, withAuth } from "@oh-my-pi/pi-ai";
|
|
7
|
+
import { type ApiKey, type AuthStorage, type FetchImpl, getEnvApiKey, withAuth } from "@oh-my-pi/pi-ai";
|
|
8
8
|
import { asRecord, asString } from "../../../web/scrapers/utils";
|
|
9
9
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
10
10
|
import { SearchProviderError } from "../../../web/search/types";
|
|
@@ -21,6 +21,7 @@ export interface ZaiSearchParams {
|
|
|
21
21
|
query: string;
|
|
22
22
|
num_results?: number;
|
|
23
23
|
signal?: AbortSignal;
|
|
24
|
+
fetch?: FetchImpl;
|
|
24
25
|
authStorage: AuthStorage;
|
|
25
26
|
sessionId?: string;
|
|
26
27
|
}
|
|
@@ -62,8 +63,13 @@ export async function findApiKey(
|
|
|
62
63
|
return (await authStorage.getApiKey("zai", sessionId, { signal })) ?? null;
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
async function callZaiTool(
|
|
66
|
-
|
|
66
|
+
async function callZaiTool(
|
|
67
|
+
apiKey: string,
|
|
68
|
+
args: Record<string, unknown>,
|
|
69
|
+
signal: AbortSignal | undefined,
|
|
70
|
+
fetchImpl: FetchImpl,
|
|
71
|
+
): Promise<unknown> {
|
|
72
|
+
const response = await fetchImpl(ZAI_MCP_URL, {
|
|
67
73
|
method: "POST",
|
|
68
74
|
headers: {
|
|
69
75
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -158,6 +164,7 @@ async function callZaiTool(apiKey: string, args: Record<string, unknown>, signal
|
|
|
158
164
|
|
|
159
165
|
async function callZaiSearch(apiKey: string, params: ZaiSearchParams): Promise<unknown> {
|
|
160
166
|
const count = params.num_results ?? DEFAULT_NUM_RESULTS;
|
|
167
|
+
const fetchImpl = params.fetch ?? fetch;
|
|
161
168
|
const attempts: Record<string, unknown>[] = [
|
|
162
169
|
{ query: params.query, count },
|
|
163
170
|
{ search_query: params.query, count },
|
|
@@ -167,7 +174,7 @@ async function callZaiSearch(apiKey: string, params: ZaiSearchParams): Promise<u
|
|
|
167
174
|
let lastError: unknown;
|
|
168
175
|
for (let i = 0; i < attempts.length; i++) {
|
|
169
176
|
try {
|
|
170
|
-
return await callZaiTool(apiKey, attempts[i], params.signal);
|
|
177
|
+
return await callZaiTool(apiKey, attempts[i], params.signal, fetchImpl);
|
|
171
178
|
} catch (error) {
|
|
172
179
|
lastError = error;
|
|
173
180
|
const isLastAttempt = i === attempts.length - 1;
|
|
@@ -301,6 +308,8 @@ export async function searchZai(params: ZaiSearchParams): Promise<SearchResponse
|
|
|
301
308
|
};
|
|
302
309
|
}
|
|
303
310
|
|
|
311
|
+
type ZaiProviderSearchParams = SearchParams & { fetch?: FetchImpl };
|
|
312
|
+
|
|
304
313
|
/** Search provider for Z.AI web search MCP. */
|
|
305
314
|
export class ZaiProvider extends SearchProvider {
|
|
306
315
|
readonly id = "zai";
|
|
@@ -311,12 +320,14 @@ export class ZaiProvider extends SearchProvider {
|
|
|
311
320
|
}
|
|
312
321
|
|
|
313
322
|
search(params: SearchParams): Promise<SearchResponse> {
|
|
323
|
+
const { fetch: fetchOverride } = params as ZaiProviderSearchParams;
|
|
314
324
|
return searchZai({
|
|
315
325
|
query: params.query,
|
|
316
326
|
num_results: params.numSearchResults ?? params.limit,
|
|
317
327
|
signal: params.signal,
|
|
318
328
|
authStorage: params.authStorage,
|
|
319
329
|
sessionId: params.sessionId,
|
|
330
|
+
fetch: fetchOverride,
|
|
320
331
|
});
|
|
321
332
|
}
|
|
322
333
|
}
|