chat-agent-toolkit 1.2.32 → 1.2.34

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.
Files changed (67) hide show
  1. package/dist/research-agent.cjs.js +1 -1
  2. package/dist/research-agent.cjs.js.map +1 -1
  3. package/dist/research-agent.es.js +1 -1
  4. package/dist/research-agent.es.js.map +1 -1
  5. package/package.json +1 -1
  6. package/src/config/config-manager.ts +295 -295
  7. package/src/config/config-types.ts +65 -65
  8. package/src/config/environment-variables.ts +16 -16
  9. package/src/config/index.ts +26 -26
  10. package/src/config/language-models-database.ts +1434 -1434
  11. package/src/config/mcp-server-registry.ts +24 -24
  12. package/src/config/model-registry.ts +271 -271
  13. package/src/config/model-tester.ts +211 -211
  14. package/src/config/model-utils.ts +193 -193
  15. package/src/config/provider-ui-config.ts +196 -196
  16. package/src/connectors/open-connector.json +130 -130
  17. package/src/index.ts +26 -26
  18. package/src/memory/ARCHITECTURE.md +302 -302
  19. package/src/memory/README.md +224 -224
  20. package/src/memory/agent-memory-manager.ts +416 -416
  21. package/src/memory/example.ts +343 -343
  22. package/src/memory/index.ts +47 -47
  23. package/src/memory/mastra-integration.ts +604 -604
  24. package/src/memory/storage/drizzle-storage.ts +236 -236
  25. package/src/memory/storage/in-memory-storage.ts +551 -551
  26. package/src/memory/storage/storage-interface.ts +68 -68
  27. package/src/memory/types.ts +125 -125
  28. package/src/models/types.ts +4 -4
  29. package/src/provider-logos/01-ai.png +0 -0
  30. package/src/provider-logos/anthropic.png +0 -0
  31. package/src/provider-logos/aws-bedrock.png +0 -0
  32. package/src/provider-logos/aws-sagemaker.png +0 -0
  33. package/src/provider-logos/azure-openai-service.png +0 -0
  34. package/src/provider-logos/chatglm.png +0 -0
  35. package/src/provider-logos/cohere.png +0 -0
  36. package/src/provider-logos/gemini.png +0 -0
  37. package/src/provider-logos/groqcloud.png +0 -0
  38. package/src/provider-logos/huggingface.png +0 -0
  39. package/src/provider-logos/jina.png +0 -0
  40. package/src/provider-logos/localai.png +0 -0
  41. package/src/provider-logos/mistral-ai.png +0 -0
  42. package/src/provider-logos/moonshot-ai.png +0 -0
  43. package/src/provider-logos/ollama.png +0 -0
  44. package/src/provider-logos/openai.png +0 -0
  45. package/src/provider-logos/openllm.png +0 -0
  46. package/src/provider-logos/openrouter.png +0 -0
  47. package/src/provider-logos/replicate.png +0 -0
  48. package/src/provider-logos/together-ai.png +0 -0
  49. package/src/provider-logos/tongyi.png +0 -0
  50. package/src/provider-logos/xorbits-inference.png +0 -0
  51. package/src/provider-logos/zhipu-ai.png +0 -0
  52. package/src/tools/index.ts +13 -13
  53. package/src/tools/open-connector-mastra.ts +270 -270
  54. package/src/tools/open-connector-mcp.ts +170 -170
  55. package/src/tools/qwksearch-api-tools.ts +326 -326
  56. package/src/tools/search/doc-utils.ts +139 -139
  57. package/src/tools/search/document.ts +47 -47
  58. package/src/tools/search/index.ts +14 -14
  59. package/src/tools/search/link-summarizer.ts +112 -112
  60. package/src/tools/search/meta-search-types.ts +62 -62
  61. package/src/tools/search/metaSearchAgent.ts +465 -465
  62. package/src/tools/search/search-handlers.ts +97 -97
  63. package/src/tools/search/suggestionGeneratorAgent.ts +56 -56
  64. package/src/types.d.ts +137 -137
  65. package/src/utils/index.ts +2 -2
  66. package/src/utils/markdown-to-html.ts +53 -53
  67. package/src/utils/outputParser.ts +73 -73
@@ -1,327 +1,327 @@
1
- /**
2
- * @fileoverview Specialized tools for AI agents to interact with the QwkSearch API.
3
- * Provides web search, content extraction, and AI response generation.
4
- */
5
-
6
- // @ts-nocheck
7
- import { z } from "zod";
8
- import * as QwkSearch from 'qwksearch-api-client'; // Update this path to match your QwkSearch module location
9
-
10
- // Configuration for QwkSearch API
11
- const QWKSEARCH_CONFIG = {
12
- baseURL: typeof process !== "undefined" && process?.env.QWKSEARCH_URL || 'https://app.qwksearch.com/api',
13
- apiKey: typeof process !== "undefined" && process?.env.QWKSEARCH_API_KEY || null,
14
- };
15
-
16
- /**
17
- * List of tools available for agent usage, including their schemas and implementation.
18
- */
19
- export const AGENT_TOOLS = [
20
-
21
- {
22
- name: "web_search",
23
- description:
24
- "Search the web for information on any topic using QwkSearch API. Input: search query string and optional category. Returns relevant search results with titles, descriptions, and URLs from 100+ sources via SearXNG metasearch engine.",
25
- schema: z.object({
26
- query: z.string(),
27
- category: z.enum(["general", "news", "videos", "images", "science", "files", "it"]).optional().default("general"),
28
- recency: z.enum(["none", "day", "week", "month", "year"]).optional().default("none"),
29
- page: z.number().optional().default(1),
30
- language: z.string().optional().default("en-US"),
31
- public: z.boolean().optional().default(false),
32
- timeout: z.number().optional().default(10),
33
- baseURL: z.string().optional(),
34
- apiKey: z.string().optional()
35
- }),
36
- func: async ({ query, category = "general", recency = "none", page = 1, language = "en-US", public: isPublic = false, timeout = 10, baseURL, apiKey }) => {
37
- try {
38
- // Use provided config or fall back to environment/defaults
39
- const baseUrl = baseURL || QWKSEARCH_CONFIG.baseURL;
40
- const headers = apiKey || QWKSEARCH_CONFIG.apiKey ? { 'x-api-key': apiKey || QWKSEARCH_CONFIG.apiKey } : undefined;
41
-
42
- const result = await QwkSearch.searchWeb({
43
- query: {
44
- q: query,
45
- cat: category,
46
- recency: recency,
47
- page: page,
48
- lang: language,
49
- public: isPublic,
50
- timeout: timeout
51
- },
52
- baseUrl: baseUrl,
53
- ...(headers && { headers })
54
- });
55
-
56
- if (!result.data || !result.data.results || result.data.results.length === 0) {
57
- return `No search results found for "${query}". Please try a different search term.`;
58
- }
59
-
60
- let resultText = `Web search results for "${query}" (${category} category):\n\n`;
61
-
62
- result.data.results.forEach((searchResult, index) => {
63
- resultText += `${index + 1}. ${searchResult.title}\n`;
64
- resultText += ` URL: ${searchResult.url}\n`;
65
- if (searchResult.domain) {
66
- resultText += ` Domain: ${searchResult.domain}\n`;
67
- }
68
- if (searchResult.snippet) {
69
- resultText += ` Description: ${searchResult.snippet}\n`;
70
- }
71
- if (searchResult.engines && searchResult.engines.length > 0) {
72
- resultText += ` Sources: ${searchResult.engines.join(", ")}\n`;
73
- }
74
- resultText += `\n`;
75
- });
76
-
77
- resultText += `Found ${result.data.results.length} results from multiple search engines. This is the complete search information.`;
78
-
79
- return resultText;
80
- } catch (error) {
81
- return `Unable to perform web search for "${query}". Error: ${error.message}`;
82
- }
83
- },
84
- },
85
- {
86
- name: "extract_page",
87
- description:
88
- "Extract and summarize content from a web page using QwkSearch API. Supports articles, PDFs, and YouTube videos. Uses Mozilla Readability and Postlight Mercury algorithms with 100+ custom adapters for major sites. Input: URL of the page to extract. Returns structured content with citation information.",
89
- schema: z.object({
90
- url: z.string().url(),
91
- images: z.boolean().optional().default(true),
92
- links: z.boolean().optional().default(true),
93
- formatting: z.boolean().optional().default(true),
94
- absoluteURLs: z.boolean().optional().default(true),
95
- timeout: z.number().min(1).max(30).optional().default(10),
96
- baseURL: z.string().optional(),
97
- apiKey: z.string().optional()
98
- }),
99
- func: async ({ url, images = true, links = true, formatting = true, absoluteURLs = true, timeout = 10, baseURL, apiKey }) => {
100
- try {
101
- // Use provided config or fall back to environment/defaults
102
- const baseUrl = baseURL || QWKSEARCH_CONFIG.baseURL;
103
- const headers = apiKey || QWKSEARCH_CONFIG.apiKey ? { 'x-api-key': apiKey || QWKSEARCH_CONFIG.apiKey } : undefined;
104
-
105
- const result = await QwkSearch.extractContent({
106
- query: {
107
- url: url,
108
- images: images,
109
- links: links,
110
- formatting: formatting,
111
- absoluteURLs: absoluteURLs,
112
- timeout: timeout
113
- },
114
- baseUrl: baseUrl,
115
- ...(headers && { headers })
116
- });
117
-
118
- if (!result.data) {
119
- return `No content could be extracted from "${url}". Please check the URL and try again.`;
120
- }
121
-
122
- const data = result.data;
123
-
124
- let resultText = `Content extracted from: ${data.url || url}\n\n`;
125
-
126
- if (data.title) {
127
- resultText += `Title: ${data.title}\n\n`;
128
- }
129
-
130
- if (data.author) {
131
- resultText += `Author: ${data.author}\n`;
132
- if (data.author_cite) {
133
- resultText += `Author (Citation Format): ${data.author_cite}\n`;
134
- }
135
- if (data.author_type) {
136
- resultText += `Author Type: ${data.author_type}\n`;
137
- }
138
- }
139
-
140
- if (data.date) {
141
- resultText += `Publication Date: ${data.date}\n`;
142
- }
143
-
144
- if (data.source) {
145
- resultText += `Source: ${data.source}\n`;
146
- }
147
-
148
- if (data.word_count) {
149
- resultText += `Word Count: ${data.word_count}\n`;
150
- }
151
-
152
- if (data.cite) {
153
- resultText += `\nCitation (APA Format): ${data.cite}\n`;
154
- }
155
-
156
- if (data.html) {
157
- resultText += `\nContent:\n${data.html}\n\n`;
158
- }
159
-
160
- resultText += `This is the complete page extraction information.`;
161
-
162
- return resultText;
163
- } catch (error) {
164
- return `Unable to extract content from "${url}". Error: ${error.message}`;
165
- }
166
- },
167
- },
168
- {
169
- name: "generate_ai_response",
170
- description:
171
- "Generate AI language model responses using QwkSearch API with various agent templates. Supports multiple providers (Groq, OpenAI, Anthropic, etc.) and agent types for different tasks like summarization, question answering, and content generation.",
172
- schema: z.object({
173
- provider: z.enum(["groq", "openai", "anthropic", "together", "xai", "google", "perplexity", "cloudflare"]),
174
- key: z.string().optional(),
175
- agent: z.enum([
176
- "question",
177
- "summarize-bullets",
178
- "summarize",
179
- "suggest-followups",
180
- "answer-cite-sources",
181
- "query-resolution",
182
- "knowledge-graph-nodes",
183
- "summary-longtext"
184
- ]).optional().default("question"),
185
- model: z.string().optional().default("meta-llama/llama-4-maverick-17b-128e-instruct"),
186
- temperature: z.number().min(0).max(2).optional().default(0.7),
187
- html: z.boolean().optional().default(true),
188
- query: z.string().optional(),
189
- chat_history: z.string().optional(),
190
- article: z.string().optional(),
191
- baseURL: z.string().optional(),
192
- apiKey: z.string().optional()
193
- }),
194
- func: async ({ provider, key, agent = "question", model = "meta-llama/llama-4-maverick-17b-128e-instruct", temperature = 0.7, html = true, query, chat_history, article, baseURL, apiKey }) => {
195
- try {
196
- // Use provided config or fall back to environment/defaults
197
- const baseUrl = baseURL || QWKSEARCH_CONFIG.baseURL;
198
- const headers = apiKey || QWKSEARCH_CONFIG.apiKey ? { 'x-api-key': apiKey || QWKSEARCH_CONFIG.apiKey } : undefined;
199
-
200
- const requestBody = {
201
- agent,
202
- provider,
203
- model,
204
- html,
205
- temperature
206
- };
207
-
208
- if (key) {
209
- requestBody.key = key;
210
- }
211
-
212
- if (query) {
213
- requestBody.query = query;
214
- }
215
-
216
- if (chat_history) {
217
- requestBody.chat_history = chat_history;
218
- }
219
-
220
- if (article) {
221
- requestBody.article = article;
222
- }
223
-
224
- const result = await QwkSearch.writeLanguage({
225
- body: requestBody,
226
- baseUrl: baseUrl,
227
- ...(headers && { headers })
228
- });
229
-
230
- if (!result.data) {
231
- return `No response generated. Please check your input parameters and try again.`;
232
- }
233
-
234
- let resultText = `AI Response (${agent} agent, ${provider} provider):\n\n`;
235
-
236
- if (result.data.content) {
237
- resultText += result.data.content;
238
- }
239
-
240
- if (result.data.extract) {
241
- resultText += `\n\nStructured Extract:\n${JSON.stringify(result.data.extract, null, 2)}`;
242
- }
243
-
244
- resultText += `\n\nThis is the complete AI-generated response.`;
245
-
246
- return resultText;
247
- } catch (error) {
248
- return `Unable to generate AI response. Error: ${error.message}`;
249
- }
250
- },
251
- },
252
- {
253
- name: "render_page_with_javascript",
254
- description:
255
- "Render a web page with JavaScript execution using Cloudflare Browser Rendering. Use this for JavaScript-heavy sites (SPAs, React apps), pages behind bot protection (Cloudflare, reCAPTCHA), or when extract_page fails. Returns fully rendered HTML with JavaScript executed. Slower but more complete than extract_page.",
256
- schema: z.object({
257
- url: z.string().url(),
258
- blockImages: z.boolean().optional().default(true),
259
- wait: z.number().min(0).max(10000).optional().default(0),
260
- timeout: z.number().min(1000).max(60000).optional().default(30000),
261
- waitUntil: z.enum(["domcontentloaded", "load", "networkidle0", "networkidle2"]).optional().default("networkidle2"),
262
- bypassCaptcha: z.boolean().optional().default(true),
263
- sessionId: z.string().optional().default("default"),
264
- format: z.enum(["html", "json"]).optional().default("html"),
265
- scraperUrl: z.string().optional(),
266
- scraperApiKey: z.string().optional()
267
- }),
268
- func: async ({ url, blockImages = true, wait = 0, timeout = 30000, waitUntil = "networkidle2", bypassCaptcha = true, sessionId = "default", format = "html", scraperUrl, scraperApiKey }) => {
269
- try {
270
- const scraperEndpoint = scraperUrl || (typeof process !== "undefined" && process?.env?.SCRAPER_URL) || 'https://scraper.qwksearch.workers.dev';
271
- const apiKey = scraperApiKey || (typeof process !== "undefined" && process?.env?.SCRAPER_API_KEY);
272
-
273
- const requestUrl = new URL('/api/render', scraperEndpoint);
274
-
275
- const body = {
276
- url,
277
- blockImages,
278
- wait,
279
- timeout,
280
- waitUntil,
281
- bypassCaptcha,
282
- sessionId,
283
- format
284
- };
285
-
286
- const headers: Record<string, string> = {
287
- 'Content-Type': 'application/json',
288
- };
289
-
290
- if (apiKey) {
291
- headers['Authorization'] = `Bearer ${apiKey}`;
292
- }
293
-
294
- const response = await fetch(requestUrl.toString(), {
295
- method: 'POST',
296
- headers,
297
- body: JSON.stringify(body),
298
- });
299
-
300
- if (!response.ok) {
301
- const errorText = await response.text();
302
- return `Failed to render page: ${errorText}`;
303
- }
304
-
305
- if (format === 'json') {
306
- const data = await response.json();
307
- let resultText = `Page rendered successfully: ${data.url}\n\n`;
308
- if (data.title) {
309
- resultText += `Title: ${data.title}\n`;
310
- }
311
- resultText += `Load Time: ${data.loadTime}ms\n`;
312
- if (data.challengeBypassed) {
313
- resultText += `Challenge Bypassed: Yes (${data.retryCount} retries)\n`;
314
- }
315
- resultText += `\nRendered HTML Content:\n${data.html}\n\n`;
316
- resultText += `This is the complete rendered page content.`;
317
- return resultText;
318
- }
319
-
320
- const html = await response.text();
321
- return `Page rendered successfully.\n\nHTML Content:\n${html}`;
322
- } catch (error) {
323
- return `Unable to render page "${url}". Error: ${error.message}`;
324
- }
325
- },
326
- },
1
+ /**
2
+ * @fileoverview Specialized tools for AI agents to interact with the QwkSearch API.
3
+ * Provides web search, content extraction, and AI response generation.
4
+ */
5
+
6
+ // @ts-nocheck
7
+ import { z } from "zod";
8
+ import * as QwkSearch from 'qwksearch-api-client'; // Update this path to match your QwkSearch module location
9
+
10
+ // Configuration for QwkSearch API
11
+ const QWKSEARCH_CONFIG = {
12
+ baseURL: typeof process !== "undefined" && process?.env.QWKSEARCH_URL || 'https://app.qwksearch.com/api',
13
+ apiKey: typeof process !== "undefined" && process?.env.QWKSEARCH_API_KEY || null,
14
+ };
15
+
16
+ /**
17
+ * List of tools available for agent usage, including their schemas and implementation.
18
+ */
19
+ export const AGENT_TOOLS = [
20
+
21
+ {
22
+ name: "web_search",
23
+ description:
24
+ "Search the web for information on any topic using QwkSearch API. Input: search query string and optional category. Returns relevant search results with titles, descriptions, and URLs from 100+ sources via SearXNG metasearch engine.",
25
+ schema: z.object({
26
+ query: z.string(),
27
+ category: z.enum(["general", "news", "videos", "images", "science", "files", "it"]).optional().default("general"),
28
+ recency: z.enum(["none", "day", "week", "month", "year"]).optional().default("none"),
29
+ page: z.number().optional().default(1),
30
+ language: z.string().optional().default("en-US"),
31
+ public: z.boolean().optional().default(false),
32
+ timeout: z.number().optional().default(10),
33
+ baseURL: z.string().optional(),
34
+ apiKey: z.string().optional()
35
+ }),
36
+ func: async ({ query, category = "general", recency = "none", page = 1, language = "en-US", public: isPublic = false, timeout = 10, baseURL, apiKey }) => {
37
+ try {
38
+ // Use provided config or fall back to environment/defaults
39
+ const baseUrl = baseURL || QWKSEARCH_CONFIG.baseURL;
40
+ const headers = apiKey || QWKSEARCH_CONFIG.apiKey ? { 'x-api-key': apiKey || QWKSEARCH_CONFIG.apiKey } : undefined;
41
+
42
+ const result = await QwkSearch.searchWeb({
43
+ query: {
44
+ q: query,
45
+ cat: category,
46
+ recency: recency,
47
+ page: page,
48
+ lang: language,
49
+ public: isPublic,
50
+ timeout: timeout
51
+ },
52
+ baseUrl: baseUrl,
53
+ ...(headers && { headers })
54
+ });
55
+
56
+ if (!result.data || !result.data.results || result.data.results.length === 0) {
57
+ return `No search results found for "${query}". Please try a different search term.`;
58
+ }
59
+
60
+ let resultText = `Web search results for "${query}" (${category} category):\n\n`;
61
+
62
+ result.data.results.forEach((searchResult, index) => {
63
+ resultText += `${index + 1}. ${searchResult.title}\n`;
64
+ resultText += ` URL: ${searchResult.url}\n`;
65
+ if (searchResult.domain) {
66
+ resultText += ` Domain: ${searchResult.domain}\n`;
67
+ }
68
+ if (searchResult.snippet) {
69
+ resultText += ` Description: ${searchResult.snippet}\n`;
70
+ }
71
+ if (searchResult.engines && searchResult.engines.length > 0) {
72
+ resultText += ` Sources: ${searchResult.engines.join(", ")}\n`;
73
+ }
74
+ resultText += `\n`;
75
+ });
76
+
77
+ resultText += `Found ${result.data.results.length} results from multiple search engines. This is the complete search information.`;
78
+
79
+ return resultText;
80
+ } catch (error) {
81
+ return `Unable to perform web search for "${query}". Error: ${error.message}`;
82
+ }
83
+ },
84
+ },
85
+ {
86
+ name: "extract_page",
87
+ description:
88
+ "Extract and summarize content from a web page using QwkSearch API. Supports articles, PDFs, and YouTube videos. Uses Mozilla Readability and Postlight Mercury algorithms with 100+ custom adapters for major sites. Input: URL of the page to extract. Returns structured content with citation information.",
89
+ schema: z.object({
90
+ url: z.string().url(),
91
+ images: z.boolean().optional().default(true),
92
+ links: z.boolean().optional().default(true),
93
+ formatting: z.boolean().optional().default(true),
94
+ absoluteURLs: z.boolean().optional().default(true),
95
+ timeout: z.number().min(1).max(30).optional().default(10),
96
+ baseURL: z.string().optional(),
97
+ apiKey: z.string().optional()
98
+ }),
99
+ func: async ({ url, images = true, links = true, formatting = true, absoluteURLs = true, timeout = 10, baseURL, apiKey }) => {
100
+ try {
101
+ // Use provided config or fall back to environment/defaults
102
+ const baseUrl = baseURL || QWKSEARCH_CONFIG.baseURL;
103
+ const headers = apiKey || QWKSEARCH_CONFIG.apiKey ? { 'x-api-key': apiKey || QWKSEARCH_CONFIG.apiKey } : undefined;
104
+
105
+ const result = await QwkSearch.extractContent({
106
+ query: {
107
+ url: url,
108
+ images: images,
109
+ links: links,
110
+ formatting: formatting,
111
+ absoluteURLs: absoluteURLs,
112
+ timeout: timeout
113
+ },
114
+ baseUrl: baseUrl,
115
+ ...(headers && { headers })
116
+ });
117
+
118
+ if (!result.data) {
119
+ return `No content could be extracted from "${url}". Please check the URL and try again.`;
120
+ }
121
+
122
+ const data = result.data;
123
+
124
+ let resultText = `Content extracted from: ${data.url || url}\n\n`;
125
+
126
+ if (data.title) {
127
+ resultText += `Title: ${data.title}\n\n`;
128
+ }
129
+
130
+ if (data.author) {
131
+ resultText += `Author: ${data.author}\n`;
132
+ if (data.author_cite) {
133
+ resultText += `Author (Citation Format): ${data.author_cite}\n`;
134
+ }
135
+ if (data.author_type) {
136
+ resultText += `Author Type: ${data.author_type}\n`;
137
+ }
138
+ }
139
+
140
+ if (data.date) {
141
+ resultText += `Publication Date: ${data.date}\n`;
142
+ }
143
+
144
+ if (data.source) {
145
+ resultText += `Source: ${data.source}\n`;
146
+ }
147
+
148
+ if (data.word_count) {
149
+ resultText += `Word Count: ${data.word_count}\n`;
150
+ }
151
+
152
+ if (data.cite) {
153
+ resultText += `\nCitation (APA Format): ${data.cite}\n`;
154
+ }
155
+
156
+ if (data.html) {
157
+ resultText += `\nContent:\n${data.html}\n\n`;
158
+ }
159
+
160
+ resultText += `This is the complete page extraction information.`;
161
+
162
+ return resultText;
163
+ } catch (error) {
164
+ return `Unable to extract content from "${url}". Error: ${error.message}`;
165
+ }
166
+ },
167
+ },
168
+ {
169
+ name: "generate_ai_response",
170
+ description:
171
+ "Generate AI language model responses using QwkSearch API with various agent templates. Supports multiple providers (Groq, OpenAI, Anthropic, etc.) and agent types for different tasks like summarization, question answering, and content generation.",
172
+ schema: z.object({
173
+ provider: z.enum(["groq", "openai", "anthropic", "together", "xai", "google", "perplexity", "cloudflare"]),
174
+ key: z.string().optional(),
175
+ agent: z.enum([
176
+ "question",
177
+ "summarize-bullets",
178
+ "summarize",
179
+ "suggest-followups",
180
+ "answer-cite-sources",
181
+ "query-resolution",
182
+ "knowledge-graph-nodes",
183
+ "summary-longtext"
184
+ ]).optional().default("question"),
185
+ model: z.string().optional().default("meta-llama/llama-4-maverick-17b-128e-instruct"),
186
+ temperature: z.number().min(0).max(2).optional().default(0.7),
187
+ html: z.boolean().optional().default(true),
188
+ query: z.string().optional(),
189
+ chat_history: z.string().optional(),
190
+ article: z.string().optional(),
191
+ baseURL: z.string().optional(),
192
+ apiKey: z.string().optional()
193
+ }),
194
+ func: async ({ provider, key, agent = "question", model = "meta-llama/llama-4-maverick-17b-128e-instruct", temperature = 0.7, html = true, query, chat_history, article, baseURL, apiKey }) => {
195
+ try {
196
+ // Use provided config or fall back to environment/defaults
197
+ const baseUrl = baseURL || QWKSEARCH_CONFIG.baseURL;
198
+ const headers = apiKey || QWKSEARCH_CONFIG.apiKey ? { 'x-api-key': apiKey || QWKSEARCH_CONFIG.apiKey } : undefined;
199
+
200
+ const requestBody = {
201
+ agent,
202
+ provider,
203
+ model,
204
+ html,
205
+ temperature
206
+ };
207
+
208
+ if (key) {
209
+ requestBody.key = key;
210
+ }
211
+
212
+ if (query) {
213
+ requestBody.query = query;
214
+ }
215
+
216
+ if (chat_history) {
217
+ requestBody.chat_history = chat_history;
218
+ }
219
+
220
+ if (article) {
221
+ requestBody.article = article;
222
+ }
223
+
224
+ const result = await QwkSearch.writeLanguage({
225
+ body: requestBody,
226
+ baseUrl: baseUrl,
227
+ ...(headers && { headers })
228
+ });
229
+
230
+ if (!result.data) {
231
+ return `No response generated. Please check your input parameters and try again.`;
232
+ }
233
+
234
+ let resultText = `AI Response (${agent} agent, ${provider} provider):\n\n`;
235
+
236
+ if (result.data.content) {
237
+ resultText += result.data.content;
238
+ }
239
+
240
+ if (result.data.extract) {
241
+ resultText += `\n\nStructured Extract:\n${JSON.stringify(result.data.extract, null, 2)}`;
242
+ }
243
+
244
+ resultText += `\n\nThis is the complete AI-generated response.`;
245
+
246
+ return resultText;
247
+ } catch (error) {
248
+ return `Unable to generate AI response. Error: ${error.message}`;
249
+ }
250
+ },
251
+ },
252
+ {
253
+ name: "render_page_with_javascript",
254
+ description:
255
+ "Render a web page with JavaScript execution using Cloudflare Browser Rendering. Use this for JavaScript-heavy sites (SPAs, React apps), pages behind bot protection (Cloudflare, reCAPTCHA), or when extract_page fails. Returns fully rendered HTML with JavaScript executed. Slower but more complete than extract_page.",
256
+ schema: z.object({
257
+ url: z.string().url(),
258
+ blockImages: z.boolean().optional().default(true),
259
+ wait: z.number().min(0).max(10000).optional().default(0),
260
+ timeout: z.number().min(1000).max(60000).optional().default(30000),
261
+ waitUntil: z.enum(["domcontentloaded", "load", "networkidle0", "networkidle2"]).optional().default("networkidle2"),
262
+ bypassCaptcha: z.boolean().optional().default(true),
263
+ sessionId: z.string().optional().default("default"),
264
+ format: z.enum(["html", "json"]).optional().default("html"),
265
+ scraperUrl: z.string().optional(),
266
+ scraperApiKey: z.string().optional()
267
+ }),
268
+ func: async ({ url, blockImages = true, wait = 0, timeout = 30000, waitUntil = "networkidle2", bypassCaptcha = true, sessionId = "default", format = "html", scraperUrl, scraperApiKey }) => {
269
+ try {
270
+ const scraperEndpoint = scraperUrl || (typeof process !== "undefined" && process?.env?.SCRAPER_URL) || 'https://scraper.qwksearch.workers.dev';
271
+ const apiKey = scraperApiKey || (typeof process !== "undefined" && process?.env?.SCRAPER_API_KEY);
272
+
273
+ const requestUrl = new URL('/api/render', scraperEndpoint);
274
+
275
+ const body = {
276
+ url,
277
+ blockImages,
278
+ wait,
279
+ timeout,
280
+ waitUntil,
281
+ bypassCaptcha,
282
+ sessionId,
283
+ format
284
+ };
285
+
286
+ const headers: Record<string, string> = {
287
+ 'Content-Type': 'application/json',
288
+ };
289
+
290
+ if (apiKey) {
291
+ headers['Authorization'] = `Bearer ${apiKey}`;
292
+ }
293
+
294
+ const response = await fetch(requestUrl.toString(), {
295
+ method: 'POST',
296
+ headers,
297
+ body: JSON.stringify(body),
298
+ });
299
+
300
+ if (!response.ok) {
301
+ const errorText = await response.text();
302
+ return `Failed to render page: ${errorText}`;
303
+ }
304
+
305
+ if (format === 'json') {
306
+ const data = await response.json();
307
+ let resultText = `Page rendered successfully: ${data.url}\n\n`;
308
+ if (data.title) {
309
+ resultText += `Title: ${data.title}\n`;
310
+ }
311
+ resultText += `Load Time: ${data.loadTime}ms\n`;
312
+ if (data.challengeBypassed) {
313
+ resultText += `Challenge Bypassed: Yes (${data.retryCount} retries)\n`;
314
+ }
315
+ resultText += `\nRendered HTML Content:\n${data.html}\n\n`;
316
+ resultText += `This is the complete rendered page content.`;
317
+ return resultText;
318
+ }
319
+
320
+ const html = await response.text();
321
+ return `Page rendered successfully.\n\nHTML Content:\n${html}`;
322
+ } catch (error) {
323
+ return `Unable to render page "${url}". Error: ${error.message}`;
324
+ }
325
+ },
326
+ },
327
327
  ];