@pi-unipi/web-api 0.1.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 +179 -0
- package/package.json +50 -0
- package/skills/web/SKILL.md +108 -0
- package/src/cache.ts +240 -0
- package/src/commands.ts +45 -0
- package/src/index.ts +100 -0
- package/src/providers/base.ts +108 -0
- package/src/providers/duckduckgo.ts +115 -0
- package/src/providers/firecrawl.ts +105 -0
- package/src/providers/jina-reader.ts +89 -0
- package/src/providers/jina-search.ts +88 -0
- package/src/providers/llm-summarize.ts +71 -0
- package/src/providers/perplexity.ts +191 -0
- package/src/providers/registry.ts +128 -0
- package/src/providers/serpapi.ts +86 -0
- package/src/providers/tavily.ts +95 -0
- package/src/settings.ts +263 -0
- package/src/tools.ts +329 -0
- package/src/tui/provider-selector.ts +71 -0
- package/src/tui/settings-dialog.ts +177 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @unipi/web-api — Perplexity provider
|
|
3
|
+
*
|
|
4
|
+
* Paid search and summarization provider using Perplexity API.
|
|
5
|
+
* Supports search, read, and summarize capabilities.
|
|
6
|
+
* Requires API key (PERPLEXITY_API_KEY environment variable).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
WebProvider,
|
|
11
|
+
SearchResult,
|
|
12
|
+
ReadResult,
|
|
13
|
+
SummarizeResult,
|
|
14
|
+
ProviderConfig,
|
|
15
|
+
} from "./base.js";
|
|
16
|
+
import { registry } from "./registry.js";
|
|
17
|
+
|
|
18
|
+
/** Perplexity API response format */
|
|
19
|
+
interface PerplexityResponse {
|
|
20
|
+
choices: Array<{
|
|
21
|
+
message: {
|
|
22
|
+
content: string;
|
|
23
|
+
};
|
|
24
|
+
citations?: Array<{
|
|
25
|
+
url: string;
|
|
26
|
+
}>;
|
|
27
|
+
}>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Search via Perplexity.
|
|
32
|
+
* @param query - Search query
|
|
33
|
+
* @param apiKey - Perplexity API key
|
|
34
|
+
* @returns Array of search results
|
|
35
|
+
*/
|
|
36
|
+
async function searchPerplexity(query: string, apiKey: string): Promise<SearchResult[]> {
|
|
37
|
+
const response = await fetch("https://api.perplexity.ai/chat/completions", {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: {
|
|
40
|
+
"Content-Type": "application/json",
|
|
41
|
+
Authorization: `Bearer ${apiKey}`,
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify({
|
|
44
|
+
model: "llama-3.1-sonar-small-128k-online",
|
|
45
|
+
messages: [
|
|
46
|
+
{
|
|
47
|
+
role: "system",
|
|
48
|
+
content: "You are a search assistant. Return search results as a JSON array with objects containing 'title', 'url', and 'snippet' fields.",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
role: "user",
|
|
52
|
+
content: query,
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
}),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
throw new Error(`Perplexity search failed: ${response.status} ${response.statusText}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const data = (await response.json()) as PerplexityResponse;
|
|
63
|
+
const content = data.choices[0]?.message?.content || "[]";
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
// Parse JSON from response
|
|
67
|
+
const jsonMatch = content.match(/\[[\s\S]*\]/);
|
|
68
|
+
if (jsonMatch) {
|
|
69
|
+
return JSON.parse(jsonMatch[0]);
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
// Fall back to extracting from text
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Fallback: create single result from response
|
|
76
|
+
const citations = data.choices[0]?.citations || [];
|
|
77
|
+
return citations.map((citation, i) => ({
|
|
78
|
+
title: `Result ${i + 1}`,
|
|
79
|
+
url: citation.url,
|
|
80
|
+
snippet: content.substring(0, 200),
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Summarize via Perplexity.
|
|
86
|
+
* @param url - URL to summarize
|
|
87
|
+
* @param prompt - Custom prompt
|
|
88
|
+
* @param apiKey - Perplexity API key
|
|
89
|
+
* @returns Summarized content
|
|
90
|
+
*/
|
|
91
|
+
async function summarizePerplexity(
|
|
92
|
+
url: string,
|
|
93
|
+
prompt: string | undefined,
|
|
94
|
+
apiKey: string
|
|
95
|
+
): Promise<SummarizeResult> {
|
|
96
|
+
const systemPrompt = prompt || "Summarize the content of this URL concisely, highlighting key points.";
|
|
97
|
+
|
|
98
|
+
const response = await fetch("https://api.perplexity.ai/chat/completions", {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: {
|
|
101
|
+
"Content-Type": "application/json",
|
|
102
|
+
Authorization: `Bearer ${apiKey}`,
|
|
103
|
+
},
|
|
104
|
+
body: JSON.stringify({
|
|
105
|
+
model: "llama-3.1-sonar-small-128k-online",
|
|
106
|
+
messages: [
|
|
107
|
+
{
|
|
108
|
+
role: "system",
|
|
109
|
+
content: systemPrompt,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
role: "user",
|
|
113
|
+
content: `Summarize this URL: ${url}`,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
}),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
throw new Error(`Perplexity summarize failed: ${response.status} ${response.statusText}`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const data = (await response.json()) as PerplexityResponse;
|
|
124
|
+
const summary = data.choices[0]?.message?.content || "";
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
url: url,
|
|
128
|
+
summary: summary,
|
|
129
|
+
prompt: prompt,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Perplexity provider implementation */
|
|
134
|
+
const perplexityProvider: WebProvider = {
|
|
135
|
+
id: "perplexity",
|
|
136
|
+
name: "Perplexity",
|
|
137
|
+
capabilities: ["search", "read", "summarize"],
|
|
138
|
+
requiresApiKey: true,
|
|
139
|
+
apiKeyEnv: "PERPLEXITY_API_KEY",
|
|
140
|
+
ranking: {
|
|
141
|
+
search: 5,
|
|
142
|
+
read: 3,
|
|
143
|
+
summarize: 1,
|
|
144
|
+
},
|
|
145
|
+
config: {},
|
|
146
|
+
|
|
147
|
+
async search(query: string, config?: ProviderConfig): Promise<SearchResult[]> {
|
|
148
|
+
const apiKey = config?.apiKey || process.env.PERPLEXITY_API_KEY;
|
|
149
|
+
if (!apiKey) {
|
|
150
|
+
throw new Error("Perplexity requires an API key. Set PERPLEXITY_API_KEY environment variable or configure via /unipi:web-settings");
|
|
151
|
+
}
|
|
152
|
+
return searchPerplexity(query, apiKey);
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
async read(url: string, config?: ProviderConfig): Promise<ReadResult> {
|
|
156
|
+
const apiKey = config?.apiKey || process.env.PERPLEXITY_API_KEY;
|
|
157
|
+
if (!apiKey) {
|
|
158
|
+
throw new Error("Perplexity requires an API key. Set PERPLEXITY_API_KEY environment variable or configure via /unipi:web-settings");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const result = await summarizePerplexity(url, "Extract and return the full content of this URL as markdown.", apiKey);
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
url: url,
|
|
165
|
+
content: result.summary,
|
|
166
|
+
contentType: "markdown",
|
|
167
|
+
};
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
async summarize(url: string, prompt?: string, config?: ProviderConfig): Promise<SummarizeResult> {
|
|
171
|
+
const apiKey = config?.apiKey || process.env.PERPLEXITY_API_KEY;
|
|
172
|
+
if (!apiKey) {
|
|
173
|
+
throw new Error("Perplexity requires an API key. Set PERPLEXITY_API_KEY environment variable or configure via /unipi:web-settings");
|
|
174
|
+
}
|
|
175
|
+
return summarizePerplexity(url, prompt, apiKey);
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
async validateApiKey(apiKey: string): Promise<boolean> {
|
|
179
|
+
try {
|
|
180
|
+
await searchPerplexity("test", apiKey);
|
|
181
|
+
return true;
|
|
182
|
+
} catch {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
// Register provider
|
|
189
|
+
registry.register(perplexityProvider);
|
|
190
|
+
|
|
191
|
+
export { perplexityProvider };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @unipi/web-api — Provider registry
|
|
3
|
+
*
|
|
4
|
+
* Registry for managing web providers.
|
|
5
|
+
* Handles registration, retrieval, and ranked selection.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
WebProvider,
|
|
10
|
+
WebCapability,
|
|
11
|
+
ProviderConfig,
|
|
12
|
+
} from "./base.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* ProviderRegistry manages all registered web providers.
|
|
16
|
+
*
|
|
17
|
+
* Provides methods to:
|
|
18
|
+
* - Register providers
|
|
19
|
+
* - Retrieve providers by ID
|
|
20
|
+
* - Get providers for a specific capability
|
|
21
|
+
* - Get ranked providers for smart selection
|
|
22
|
+
*/
|
|
23
|
+
export class ProviderRegistry {
|
|
24
|
+
private providers: Map<string, WebProvider> = new Map();
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Register a provider.
|
|
28
|
+
* @param provider - Provider to register
|
|
29
|
+
*/
|
|
30
|
+
register(provider: WebProvider): void {
|
|
31
|
+
if (this.providers.has(provider.id)) {
|
|
32
|
+
throw new Error(`Provider "${provider.id}" is already registered`);
|
|
33
|
+
}
|
|
34
|
+
this.providers.set(provider.id, provider);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Unregister a provider.
|
|
39
|
+
* @param providerId - Provider ID to unregister
|
|
40
|
+
*/
|
|
41
|
+
unregister(providerId: string): void {
|
|
42
|
+
this.providers.delete(providerId);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get a provider by ID.
|
|
47
|
+
* @param providerId - Provider ID
|
|
48
|
+
* @returns Provider or undefined
|
|
49
|
+
*/
|
|
50
|
+
getProvider(providerId: string): WebProvider | undefined {
|
|
51
|
+
return this.providers.get(providerId);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get all registered providers.
|
|
56
|
+
* @returns Array of all providers
|
|
57
|
+
*/
|
|
58
|
+
getAllProviders(): WebProvider[] {
|
|
59
|
+
return Array.from(this.providers.values());
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get providers that support a specific capability.
|
|
64
|
+
* @param capability - Capability to filter by
|
|
65
|
+
* @returns Array of providers with the capability
|
|
66
|
+
*/
|
|
67
|
+
getProvidersForCapability(capability: WebCapability): WebProvider[] {
|
|
68
|
+
return this.getAllProviders().filter((p) =>
|
|
69
|
+
p.capabilities.includes(capability)
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get ranked providers for a specific capability.
|
|
75
|
+
* Sorted by ranking (lower = better/simpler/cheaper).
|
|
76
|
+
* @param capability - Capability to rank by
|
|
77
|
+
* @returns Array of providers sorted by ranking
|
|
78
|
+
*/
|
|
79
|
+
getRankedProviders(capability: WebCapability): WebProvider[] {
|
|
80
|
+
return this.getProvidersForCapability(capability)
|
|
81
|
+
.filter((p) => p.ranking[capability] > 0)
|
|
82
|
+
.sort((a, b) => a.ranking[capability] - b.ranking[capability]);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get the best provider for a capability (lowest rank).
|
|
87
|
+
* @param capability - Capability to find best provider for
|
|
88
|
+
* @returns Best provider or undefined
|
|
89
|
+
*/
|
|
90
|
+
getBestProvider(capability: WebCapability): WebProvider | undefined {
|
|
91
|
+
const ranked = this.getRankedProviders(capability);
|
|
92
|
+
return ranked[0];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get a provider by rank for a capability.
|
|
97
|
+
* @param capability - Capability to search
|
|
98
|
+
* @param rank - Desired rank (1-based)
|
|
99
|
+
* @returns Provider at that rank or undefined
|
|
100
|
+
*/
|
|
101
|
+
getProviderByRank(capability: WebCapability, rank: number): WebProvider | undefined {
|
|
102
|
+
const ranked = this.getRankedProviders(capability);
|
|
103
|
+
return ranked.find((p) => p.ranking[capability] === rank);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get enabled providers based on configuration.
|
|
108
|
+
* @param configMap - Map of provider ID to config
|
|
109
|
+
* @returns Array of enabled providers
|
|
110
|
+
*/
|
|
111
|
+
getEnabledProviders(configMap: Map<string, ProviderConfig>): WebProvider[] {
|
|
112
|
+
return this.getAllProviders().filter((p) => {
|
|
113
|
+
const config = configMap.get(p.id);
|
|
114
|
+
return config?.enabled !== false;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get provider count.
|
|
120
|
+
* @returns Number of registered providers
|
|
121
|
+
*/
|
|
122
|
+
get count(): number {
|
|
123
|
+
return this.providers.size;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Singleton registry instance */
|
|
128
|
+
export const registry = new ProviderRegistry();
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @unipi/web-api — SerpAPI provider
|
|
3
|
+
*
|
|
4
|
+
* Paid search provider using SerpAPI for Google search results.
|
|
5
|
+
* Requires API key (SERPAPI_KEY environment variable).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
WebProvider,
|
|
10
|
+
SearchResult,
|
|
11
|
+
ProviderConfig,
|
|
12
|
+
} from "./base.js";
|
|
13
|
+
import { registry } from "./registry.js";
|
|
14
|
+
|
|
15
|
+
/** SerpAPI response format */
|
|
16
|
+
interface SerpAPIResponse {
|
|
17
|
+
organic_results: Array<{
|
|
18
|
+
title: string;
|
|
19
|
+
link: string;
|
|
20
|
+
snippet: string;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Search via SerpAPI.
|
|
26
|
+
* @param query - Search query
|
|
27
|
+
* @param apiKey - SerpAPI key
|
|
28
|
+
* @returns Array of search results
|
|
29
|
+
*/
|
|
30
|
+
async function searchSerpAPI(query: string, apiKey: string): Promise<SearchResult[]> {
|
|
31
|
+
const url = new URL("https://serpapi.com/search");
|
|
32
|
+
url.searchParams.set("q", query);
|
|
33
|
+
url.searchParams.set("api_key", apiKey);
|
|
34
|
+
url.searchParams.set("engine", "google");
|
|
35
|
+
|
|
36
|
+
const response = await fetch(url.toString());
|
|
37
|
+
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
throw new Error(`SerpAPI search failed: ${response.status} ${response.statusText}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const data = (await response.json()) as SerpAPIResponse;
|
|
43
|
+
|
|
44
|
+
return (data.organic_results || []).map((item) => ({
|
|
45
|
+
title: item.title,
|
|
46
|
+
url: item.link,
|
|
47
|
+
snippet: item.snippet,
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** SerpAPI provider implementation */
|
|
52
|
+
const serpapiProvider: WebProvider = {
|
|
53
|
+
id: "serpapi",
|
|
54
|
+
name: "SerpAPI",
|
|
55
|
+
capabilities: ["search"],
|
|
56
|
+
requiresApiKey: true,
|
|
57
|
+
apiKeyEnv: "SERPAPI_KEY",
|
|
58
|
+
ranking: {
|
|
59
|
+
search: 3,
|
|
60
|
+
read: 0,
|
|
61
|
+
summarize: 0,
|
|
62
|
+
},
|
|
63
|
+
config: {},
|
|
64
|
+
|
|
65
|
+
async search(query: string, config?: ProviderConfig): Promise<SearchResult[]> {
|
|
66
|
+
const apiKey = config?.apiKey || process.env.SERPAPI_KEY;
|
|
67
|
+
if (!apiKey) {
|
|
68
|
+
throw new Error("SerpAPI requires an API key. Set SERPAPI_KEY environment variable or configure via /unipi:web-settings");
|
|
69
|
+
}
|
|
70
|
+
return searchSerpAPI(query, apiKey);
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
async validateApiKey(apiKey: string): Promise<boolean> {
|
|
74
|
+
try {
|
|
75
|
+
const results = await searchSerpAPI("test", apiKey);
|
|
76
|
+
return Array.isArray(results);
|
|
77
|
+
} catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Register provider
|
|
84
|
+
registry.register(serpapiProvider);
|
|
85
|
+
|
|
86
|
+
export { serpapiProvider };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @unipi/web-api — Tavily provider
|
|
3
|
+
*
|
|
4
|
+
* Paid search provider using Tavily API for AI-optimized search results.
|
|
5
|
+
* Requires API key (TAVILY_API_KEY environment variable).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
WebProvider,
|
|
10
|
+
SearchResult,
|
|
11
|
+
ProviderConfig,
|
|
12
|
+
} from "./base.js";
|
|
13
|
+
import { registry } from "./registry.js";
|
|
14
|
+
|
|
15
|
+
/** Tavily API response format */
|
|
16
|
+
interface TavilyResponse {
|
|
17
|
+
results: Array<{
|
|
18
|
+
title: string;
|
|
19
|
+
url: string;
|
|
20
|
+
content: string;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Search via Tavily.
|
|
26
|
+
* @param query - Search query
|
|
27
|
+
* @param apiKey - Tavily API key
|
|
28
|
+
* @returns Array of search results
|
|
29
|
+
*/
|
|
30
|
+
async function searchTavily(query: string, apiKey: string): Promise<SearchResult[]> {
|
|
31
|
+
const url = "https://api.tavily.com/search";
|
|
32
|
+
|
|
33
|
+
const response = await fetch(url, {
|
|
34
|
+
method: "POST",
|
|
35
|
+
headers: {
|
|
36
|
+
"Content-Type": "application/json",
|
|
37
|
+
},
|
|
38
|
+
body: JSON.stringify({
|
|
39
|
+
api_key: apiKey,
|
|
40
|
+
query: query,
|
|
41
|
+
search_depth: "basic",
|
|
42
|
+
include_answer: false,
|
|
43
|
+
include_raw_content: false,
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
throw new Error(`Tavily search failed: ${response.status} ${response.statusText}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const data = (await response.json()) as TavilyResponse;
|
|
52
|
+
|
|
53
|
+
return (data.results || []).map((item) => ({
|
|
54
|
+
title: item.title,
|
|
55
|
+
url: item.url,
|
|
56
|
+
snippet: item.content,
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Tavily provider implementation */
|
|
61
|
+
const tavilyProvider: WebProvider = {
|
|
62
|
+
id: "tavily",
|
|
63
|
+
name: "Tavily",
|
|
64
|
+
capabilities: ["search"],
|
|
65
|
+
requiresApiKey: true,
|
|
66
|
+
apiKeyEnv: "TAVILY_API_KEY",
|
|
67
|
+
ranking: {
|
|
68
|
+
search: 4,
|
|
69
|
+
read: 0,
|
|
70
|
+
summarize: 0,
|
|
71
|
+
},
|
|
72
|
+
config: {},
|
|
73
|
+
|
|
74
|
+
async search(query: string, config?: ProviderConfig): Promise<SearchResult[]> {
|
|
75
|
+
const apiKey = config?.apiKey || process.env.TAVILY_API_KEY;
|
|
76
|
+
if (!apiKey) {
|
|
77
|
+
throw new Error("Tavily requires an API key. Set TAVILY_API_KEY environment variable or configure via /unipi:web-settings");
|
|
78
|
+
}
|
|
79
|
+
return searchTavily(query, apiKey);
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
async validateApiKey(apiKey: string): Promise<boolean> {
|
|
83
|
+
try {
|
|
84
|
+
const results = await searchTavily("test", apiKey);
|
|
85
|
+
return Array.isArray(results);
|
|
86
|
+
} catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// Register provider
|
|
93
|
+
registry.register(tavilyProvider);
|
|
94
|
+
|
|
95
|
+
export { tavilyProvider };
|