chat-agent-toolkit 1.2.379 → 1.2.380
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/dist/chat-agent-toolkit/src/tools/search/doc-utils.d.ts +33 -2
- package/dist/chat-agent-toolkit/src/tools/search/index.d.ts +2 -2
- package/dist/chat-agent-toolkit/src/tools/search/meta-search-types.d.ts +14 -1
- package/dist/chat-agent-toolkit/src/tools/search/metaSearchAgent.d.ts +1 -1
- package/dist/research-agent.cjs.js +1 -1
- package/dist/research-agent.cjs.js.map +1 -1
- package/dist/research-agent.es.js +1 -1
- package/dist/research-agent.es.js.map +1 -1
- package/package.json +2 -2
- package/src/tools/search/doc-utils.d.ts +16 -2
- package/src/tools/search/doc-utils.ts +246 -204
- package/src/tools/search/index.d.ts +2 -2
- package/src/tools/search/index.ts +14 -14
- package/src/tools/search/meta-search-types.d.ts +7 -1
- package/src/tools/search/meta-search-types.ts +78 -64
- package/src/tools/search/metaSearchAgent.d.ts +3 -4
- package/src/tools/search/metaSearchAgent.ts +518 -493
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chat-agent-toolkit",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.380",
|
|
4
4
|
"description": "Multi-provider AI agent toolkit: generate language responses, search the web, extract content, and manage memory across 10+ LLM providers.",
|
|
5
5
|
"author": "vtempest <grokthiscontact@gmail.com>",
|
|
6
6
|
"license": "rights.institute/PROSPER",
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"html-entities": "^2.6.0",
|
|
99
99
|
"manage-storage": "^0.0.47",
|
|
100
100
|
"marked": "^18.0.12",
|
|
101
|
-
"qwksearch-api-client": "^1.0.
|
|
101
|
+
"qwksearch-api-client": "^1.0.52",
|
|
102
102
|
"write-language": "^0.1.153",
|
|
103
103
|
"zod": "^4.5.4"
|
|
104
104
|
},
|
|
@@ -39,11 +39,25 @@ export type UploadFileLoader = (fileId: string) => Promise<LoadedUpload | null>;
|
|
|
39
39
|
* the S3-credentials fallback.
|
|
40
40
|
*/
|
|
41
41
|
export declare function registerUploadFileLoader(loader: UploadFileLoader): void;
|
|
42
|
+
/**
|
|
43
|
+
* Resolves every uploaded fileId once, in order — sequentially, so one raw
|
|
44
|
+
* payload is live at a time, and once per request, with the result shared by
|
|
45
|
+
* the reranker and the image loader.
|
|
46
|
+
*/
|
|
47
|
+
export declare function loadUploads(fileIds: string[], r2Credentials?: R2CredentialsInput): Promise<LoadedUpload[]>;
|
|
48
|
+
/**
|
|
49
|
+
* Picks the image attachments out of already-resolved uploads, ready to be
|
|
50
|
+
* passed to the LLM as image content parts.
|
|
51
|
+
*/
|
|
52
|
+
export declare function selectUploadImages(uploads: LoadedUpload[]): UploadImageAttachment[];
|
|
42
53
|
/**
|
|
43
54
|
* Resolves image attachments for the given uploaded fileIds so they can be
|
|
44
55
|
* passed to the LLM as image content parts. Non-image uploads (documents) and
|
|
45
56
|
* images stored without inline data are skipped.
|
|
46
57
|
*/
|
|
47
|
-
export declare function loadUploadImages(fileIds: string[], r2Credentials?: R2CredentialsInput): Promise<UploadImageAttachment[]>;
|
|
48
|
-
|
|
58
|
+
export declare function loadUploadImages(fileIds: string[], r2Credentials?: R2CredentialsInput, preloaded?: LoadedUpload[]): Promise<UploadImageAttachment[]>;
|
|
59
|
+
/**
|
|
60
|
+
* Builds the answer context: uploaded file content first, then web results.
|
|
61
|
+
*/
|
|
62
|
+
export declare function rerankDocs(query: string, docs: Document[], fileIds: string[], optimizationMode: "speed" | "balanced" | "quality", r2Credentials?: R2CredentialsInput, preloaded?: LoadedUpload[]): Promise<Document[]>;
|
|
49
63
|
export declare function processDocs(docs: Document[]): string;
|
|
@@ -1,204 +1,246 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module research/search/doc-utils
|
|
3
|
-
* @description Document utilities: fallback docs, reranking, and formatting.
|
|
4
|
-
*/
|
|
5
|
-
import type { Document } from "./document";
|
|
6
|
-
|
|
7
|
-
export interface R2CredentialsInput {
|
|
8
|
-
accountId: string;
|
|
9
|
-
accessKeyId: string;
|
|
10
|
-
secretAccessKey: string;
|
|
11
|
-
bucket: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function buildFallbackDocs(query: string): Document[] {
|
|
15
|
-
const trimmedQuery = (query || "").trim();
|
|
16
|
-
const searchQuery = trimmedQuery.length > 0 ? trimmedQuery : "web search";
|
|
17
|
-
const fallbackUrl = `https://www.google.com/search?q=${encodeURIComponent(searchQuery)}`;
|
|
18
|
-
|
|
19
|
-
return [
|
|
20
|
-
{
|
|
21
|
-
pageContent:
|
|
22
|
-
"No indexed sources were returned by the configured search providers for this query.",
|
|
23
|
-
metadata: {
|
|
24
|
-
title: `Search results for: ${searchQuery}`,
|
|
25
|
-
url: fallbackUrl,
|
|
26
|
-
source: "Google Search",
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function normalizeSourcesOutput(output: unknown, query: string): Document[] {
|
|
33
|
-
if (Array.isArray(output) && output.length > 0) {
|
|
34
|
-
return output as Document[];
|
|
35
|
-
}
|
|
36
|
-
return buildFallbackDocs(query);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** The payload resolved for an uploaded fileId. */
|
|
40
|
-
export interface LoadedUpload {
|
|
41
|
-
title: string;
|
|
42
|
-
content: string;
|
|
43
|
-
/** MIME type for image uploads (e.g. `"image/png"`). */
|
|
44
|
-
mediaType?: string;
|
|
45
|
-
/** `data:`/`http(s)` URL of an image upload, passed to the LLM as image content. */
|
|
46
|
-
image?: string;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** An image attachment resolved from an uploaded fileId. */
|
|
50
|
-
export interface UploadImageAttachment {
|
|
51
|
-
mediaType: string;
|
|
52
|
-
/** `data:`/`http(s)` URL of the image, ready for an AI SDK image content part. */
|
|
53
|
-
image: string;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Resolves an uploaded fileId to its extracted payload. Hosts register a loader
|
|
58
|
-
* (e.g. reading the native R2 binding) so the search pipeline does not depend
|
|
59
|
-
* on filesystem or credential-based access. Image uploads additionally carry
|
|
60
|
-
* `mediaType` and `image` so they can be passed to the LLM directly.
|
|
61
|
-
*/
|
|
62
|
-
export type UploadFileLoader = (fileId: string) => Promise<LoadedUpload | null>;
|
|
63
|
-
|
|
64
|
-
let uploadFileLoader: UploadFileLoader | null = null;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Registers the loader used by {@link rerankDocs} to resolve uploaded
|
|
68
|
-
* fileIds to extracted content. The registered loader takes precedence over
|
|
69
|
-
* the S3-credentials fallback.
|
|
70
|
-
*/
|
|
71
|
-
export function registerUploadFileLoader(loader: UploadFileLoader): void {
|
|
72
|
-
uploadFileLoader = loader;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async function downloadExtractedContent(fileId: string, r2Credentials: R2CredentialsInput): Promise<LoadedUpload | null> {
|
|
76
|
-
try {
|
|
77
|
-
const { manageStorage } = await import("manage-storage");
|
|
78
|
-
const config = {
|
|
79
|
-
provider: "cloudflare" as const,
|
|
80
|
-
BUCKET_NAME: r2Credentials.bucket,
|
|
81
|
-
ACCESS_KEY_ID: r2Credentials.accessKeyId,
|
|
82
|
-
SECRET_ACCESS_KEY: r2Credentials.secretAccessKey,
|
|
83
|
-
BUCKET_URL: `https://${r2Credentials.accountId}.r2.cloudflarestorage.com`,
|
|
84
|
-
};
|
|
85
|
-
const extractedKey = `${fileId}-extracted.json`;
|
|
86
|
-
const data = await manageStorage("download", { ...config, key: extractedKey });
|
|
87
|
-
const parsed = JSON.parse(data);
|
|
88
|
-
return {
|
|
89
|
-
title: parsed.title || "Uploaded Document",
|
|
90
|
-
content: parsed.content || "",
|
|
91
|
-
...(parsed.mediaType ? { mediaType: parsed.mediaType } : {}),
|
|
92
|
-
...(parsed.image ? { image: parsed.image } : {}),
|
|
93
|
-
};
|
|
94
|
-
} catch (error) {
|
|
95
|
-
console.error(`[rerankDocs] Failed to download extracted content for fileId ${fileId}:`, error);
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Resolves a single uploaded fileId via the registered loader (preferred) or
|
|
102
|
-
* the R2 credentials fallback. Returns `null` when neither can resolve it.
|
|
103
|
-
*/
|
|
104
|
-
async function resolveUpload(
|
|
105
|
-
fileId: string,
|
|
106
|
-
r2Credentials?: R2CredentialsInput,
|
|
107
|
-
): Promise<LoadedUpload | null> {
|
|
108
|
-
if (uploadFileLoader) {
|
|
109
|
-
try {
|
|
110
|
-
const loaded = await uploadFileLoader(fileId);
|
|
111
|
-
if (loaded) return loaded;
|
|
112
|
-
} catch (error) {
|
|
113
|
-
console.error(
|
|
114
|
-
`[resolveUpload] Registered upload loader failed for fileId ${fileId}:`,
|
|
115
|
-
error,
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
if (r2Credentials) {
|
|
120
|
-
return downloadExtractedContent(fileId, r2Credentials);
|
|
121
|
-
}
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Resolves
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
export function
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @module research/search/doc-utils
|
|
3
|
+
* @description Document utilities: fallback docs, reranking, and formatting.
|
|
4
|
+
*/
|
|
5
|
+
import type { Document } from "./document";
|
|
6
|
+
|
|
7
|
+
export interface R2CredentialsInput {
|
|
8
|
+
accountId: string;
|
|
9
|
+
accessKeyId: string;
|
|
10
|
+
secretAccessKey: string;
|
|
11
|
+
bucket: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function buildFallbackDocs(query: string): Document[] {
|
|
15
|
+
const trimmedQuery = (query || "").trim();
|
|
16
|
+
const searchQuery = trimmedQuery.length > 0 ? trimmedQuery : "web search";
|
|
17
|
+
const fallbackUrl = `https://www.google.com/search?q=${encodeURIComponent(searchQuery)}`;
|
|
18
|
+
|
|
19
|
+
return [
|
|
20
|
+
{
|
|
21
|
+
pageContent:
|
|
22
|
+
"No indexed sources were returned by the configured search providers for this query.",
|
|
23
|
+
metadata: {
|
|
24
|
+
title: `Search results for: ${searchQuery}`,
|
|
25
|
+
url: fallbackUrl,
|
|
26
|
+
source: "Google Search",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function normalizeSourcesOutput(output: unknown, query: string): Document[] {
|
|
33
|
+
if (Array.isArray(output) && output.length > 0) {
|
|
34
|
+
return output as Document[];
|
|
35
|
+
}
|
|
36
|
+
return buildFallbackDocs(query);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The payload resolved for an uploaded fileId. */
|
|
40
|
+
export interface LoadedUpload {
|
|
41
|
+
title: string;
|
|
42
|
+
content: string;
|
|
43
|
+
/** MIME type for image uploads (e.g. `"image/png"`). */
|
|
44
|
+
mediaType?: string;
|
|
45
|
+
/** `data:`/`http(s)` URL of an image upload, passed to the LLM as image content. */
|
|
46
|
+
image?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** An image attachment resolved from an uploaded fileId. */
|
|
50
|
+
export interface UploadImageAttachment {
|
|
51
|
+
mediaType: string;
|
|
52
|
+
/** `data:`/`http(s)` URL of the image, ready for an AI SDK image content part. */
|
|
53
|
+
image: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Resolves an uploaded fileId to its extracted payload. Hosts register a loader
|
|
58
|
+
* (e.g. reading the native R2 binding) so the search pipeline does not depend
|
|
59
|
+
* on filesystem or credential-based access. Image uploads additionally carry
|
|
60
|
+
* `mediaType` and `image` so they can be passed to the LLM directly.
|
|
61
|
+
*/
|
|
62
|
+
export type UploadFileLoader = (fileId: string) => Promise<LoadedUpload | null>;
|
|
63
|
+
|
|
64
|
+
let uploadFileLoader: UploadFileLoader | null = null;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Registers the loader used by {@link rerankDocs} to resolve uploaded
|
|
68
|
+
* fileIds to extracted content. The registered loader takes precedence over
|
|
69
|
+
* the S3-credentials fallback.
|
|
70
|
+
*/
|
|
71
|
+
export function registerUploadFileLoader(loader: UploadFileLoader): void {
|
|
72
|
+
uploadFileLoader = loader;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function downloadExtractedContent(fileId: string, r2Credentials: R2CredentialsInput): Promise<LoadedUpload | null> {
|
|
76
|
+
try {
|
|
77
|
+
const { manageStorage } = await import("manage-storage");
|
|
78
|
+
const config = {
|
|
79
|
+
provider: "cloudflare" as const,
|
|
80
|
+
BUCKET_NAME: r2Credentials.bucket,
|
|
81
|
+
ACCESS_KEY_ID: r2Credentials.accessKeyId,
|
|
82
|
+
SECRET_ACCESS_KEY: r2Credentials.secretAccessKey,
|
|
83
|
+
BUCKET_URL: `https://${r2Credentials.accountId}.r2.cloudflarestorage.com`,
|
|
84
|
+
};
|
|
85
|
+
const extractedKey = `${fileId}-extracted.json`;
|
|
86
|
+
const data = await manageStorage("download", { ...config, key: extractedKey });
|
|
87
|
+
const parsed = JSON.parse(data);
|
|
88
|
+
return {
|
|
89
|
+
title: parsed.title || "Uploaded Document",
|
|
90
|
+
content: parsed.content || "",
|
|
91
|
+
...(parsed.mediaType ? { mediaType: parsed.mediaType } : {}),
|
|
92
|
+
...(parsed.image ? { image: parsed.image } : {}),
|
|
93
|
+
};
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error(`[rerankDocs] Failed to download extracted content for fileId ${fileId}:`, error);
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Resolves a single uploaded fileId via the registered loader (preferred) or
|
|
102
|
+
* the R2 credentials fallback. Returns `null` when neither can resolve it.
|
|
103
|
+
*/
|
|
104
|
+
async function resolveUpload(
|
|
105
|
+
fileId: string,
|
|
106
|
+
r2Credentials?: R2CredentialsInput,
|
|
107
|
+
): Promise<LoadedUpload | null> {
|
|
108
|
+
if (uploadFileLoader) {
|
|
109
|
+
try {
|
|
110
|
+
const loaded = await uploadFileLoader(fileId);
|
|
111
|
+
if (loaded) return loaded;
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error(
|
|
114
|
+
`[resolveUpload] Registered upload loader failed for fileId ${fileId}:`,
|
|
115
|
+
error,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (r2Credentials) {
|
|
120
|
+
return downloadExtractedContent(fileId, r2Credentials);
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Resolves every uploaded fileId once, in order.
|
|
127
|
+
*
|
|
128
|
+
* Two things here are deliberate, and both are about the 128MB an edge isolate
|
|
129
|
+
* gets. An attachment arrives as a JSON object whose `image` field is a data
|
|
130
|
+
* URL, so an 8MB picture is ~11MB of base64 that exists twice while it is
|
|
131
|
+
* parsed — and a request may carry ten of them. So: resolved **sequentially**,
|
|
132
|
+
* to keep one raw payload live at a time rather than ten; and resolved **once**
|
|
133
|
+
* per request, with the result shared by the reranker and the image loader,
|
|
134
|
+
* which between them used to fetch and parse every attachment twice over.
|
|
135
|
+
*
|
|
136
|
+
* Duplicate ids in the list collapse to one fetch. A file that cannot be
|
|
137
|
+
* resolved is dropped, exactly as before.
|
|
138
|
+
*/
|
|
139
|
+
export async function loadUploads(
|
|
140
|
+
fileIds: string[],
|
|
141
|
+
r2Credentials?: R2CredentialsInput,
|
|
142
|
+
): Promise<LoadedUpload[]> {
|
|
143
|
+
if (!fileIds || fileIds.length === 0) return [];
|
|
144
|
+
|
|
145
|
+
const seen = new Set<string>();
|
|
146
|
+
const loaded: LoadedUpload[] = [];
|
|
147
|
+
|
|
148
|
+
for (const fileId of fileIds) {
|
|
149
|
+
if (seen.has(fileId)) continue;
|
|
150
|
+
seen.add(fileId);
|
|
151
|
+
const upload = await resolveUpload(fileId, r2Credentials);
|
|
152
|
+
if (upload) loaded.push(upload);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return loaded;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Picks the image attachments out of already-resolved uploads, ready to be
|
|
160
|
+
* passed to the LLM as image content parts. Documents and images stored
|
|
161
|
+
* without inline data carry no `image`, so they drop out here.
|
|
162
|
+
*/
|
|
163
|
+
export function selectUploadImages(uploads: LoadedUpload[]): UploadImageAttachment[] {
|
|
164
|
+
return uploads
|
|
165
|
+
.filter((u) => typeof u.image === "string" && u.image.length > 0)
|
|
166
|
+
.map((u) => ({
|
|
167
|
+
mediaType: u.mediaType || "image/png",
|
|
168
|
+
image: u.image as string,
|
|
169
|
+
}));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Resolves image attachments for the given uploaded fileIds so they can be
|
|
174
|
+
* passed to the LLM as image content parts. Non-image uploads (documents) and
|
|
175
|
+
* images stored without inline data are skipped.
|
|
176
|
+
*
|
|
177
|
+
* Pass `preloaded` when the uploads have already been resolved for this
|
|
178
|
+
* request — {@link loadUploads} explains why fetching them twice is worth
|
|
179
|
+
* avoiding.
|
|
180
|
+
*/
|
|
181
|
+
export async function loadUploadImages(
|
|
182
|
+
fileIds: string[],
|
|
183
|
+
r2Credentials?: R2CredentialsInput,
|
|
184
|
+
preloaded?: LoadedUpload[],
|
|
185
|
+
): Promise<UploadImageAttachment[]> {
|
|
186
|
+
if (preloaded) return selectUploadImages(preloaded);
|
|
187
|
+
if (!fileIds || fileIds.length === 0) return [];
|
|
188
|
+
return selectUploadImages(await loadUploads(fileIds, r2Credentials));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Builds the answer context: uploaded file content first, then web results.
|
|
193
|
+
*
|
|
194
|
+
* Pass `preloaded` when the uploads have already been resolved for this
|
|
195
|
+
* request — see {@link loadUploads}.
|
|
196
|
+
*/
|
|
197
|
+
export async function rerankDocs(
|
|
198
|
+
query: string,
|
|
199
|
+
docs: Document[],
|
|
200
|
+
fileIds: string[],
|
|
201
|
+
optimizationMode: "speed" | "balanced" | "quality",
|
|
202
|
+
r2Credentials?: R2CredentialsInput,
|
|
203
|
+
preloaded?: LoadedUpload[],
|
|
204
|
+
): Promise<Document[]> {
|
|
205
|
+
if (docs.length === 0 && fileIds.length === 0) {
|
|
206
|
+
return docs;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const filesData: LoadedUpload[] =
|
|
210
|
+
preloaded ??
|
|
211
|
+
(fileIds.length > 0 ? await loadUploads(fileIds, r2Credentials) : []);
|
|
212
|
+
|
|
213
|
+
// Uploaded documents must always reach the LLM. Build their docs up front so
|
|
214
|
+
// every return path below keeps them in the answer context. Image uploads
|
|
215
|
+
// carry no text content — they are passed separately as image parts (see
|
|
216
|
+
// loadUploadImages) — so they are excluded here to avoid empty docs.
|
|
217
|
+
const fileDocs: Document[] = filesData
|
|
218
|
+
.filter((fileData) => fileData.content && fileData.content.length > 0)
|
|
219
|
+
.map((fileData) => ({
|
|
220
|
+
pageContent: fileData.content,
|
|
221
|
+
metadata: { title: fileData.title, url: "File" },
|
|
222
|
+
}));
|
|
223
|
+
|
|
224
|
+
if (query.toLocaleLowerCase() === "summarize") {
|
|
225
|
+
// Skip web-result reranking for an explicit "summarize" request, but keep
|
|
226
|
+
// the uploaded file content — otherwise attachments are silently dropped
|
|
227
|
+
// and never analysed.
|
|
228
|
+
return [...fileDocs, ...docs].slice(0, 15);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const docsWithContent = docs.filter(
|
|
232
|
+
(doc) => doc.pageContent && doc.pageContent.length > 0,
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
// Combine file docs with web results, cap at 15
|
|
236
|
+
return [...fileDocs, ...docsWithContent].slice(0, 15);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function processDocs(docs: Document[]): string {
|
|
240
|
+
return docs
|
|
241
|
+
.map(
|
|
242
|
+
(_, index) =>
|
|
243
|
+
`${index + 1}. ${docs[index].metadata.title} ${docs[index].pageContent}`,
|
|
244
|
+
)
|
|
245
|
+
.join("\n");
|
|
246
|
+
}
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* @description Meta search agent tools for AI-powered web search and research
|
|
4
4
|
*/
|
|
5
5
|
export { default as MetaSearchAgent } from "./metaSearchAgent";
|
|
6
|
-
export type { MetaSearchAgentType, Config, ChatTurnMessage, SearchingEvent, FewShotExample } from "./meta-search-types";
|
|
6
|
+
export type { MetaSearchAgentType, Config, ChatTurnMessage, DrainableEmitter, SearchingEvent, FewShotExample } from "./meta-search-types";
|
|
7
7
|
export { searchHandlers, createSearchHandlers } from "./search-handlers";
|
|
8
8
|
export { default as generateSuggestions } from "./suggestionGeneratorAgent";
|
|
9
9
|
export { groupAndSummarizeDocs } from "./link-summarizer";
|
|
10
10
|
export type { Document } from "./document";
|
|
11
11
|
export { splitTextIntoChunks } from "./document";
|
|
12
|
-
export { buildFallbackDocs, rerankDocs, processDocs, normalizeSourcesOutput, registerUploadFileLoader, loadUploadImages } from "./doc-utils";
|
|
12
|
+
export { buildFallbackDocs, rerankDocs, processDocs, normalizeSourcesOutput, registerUploadFileLoader, loadUploads, loadUploadImages, selectUploadImages } from "./doc-utils";
|
|
13
13
|
export type { UploadFileLoader, LoadedUpload, UploadImageAttachment } from "./doc-utils";
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module agent-toolkit/tools/search
|
|
3
|
-
* @description Meta search agent tools for AI-powered web search and research
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export { default as MetaSearchAgent } from "./metaSearchAgent";
|
|
7
|
-
export type { MetaSearchAgentType, Config, ChatTurnMessage, SearchingEvent, FewShotExample } from "./meta-search-types";
|
|
8
|
-
export { searchHandlers, createSearchHandlers } from "./search-handlers";
|
|
9
|
-
export { default as generateSuggestions } from "./suggestionGeneratorAgent";
|
|
10
|
-
export { groupAndSummarizeDocs } from "./link-summarizer";
|
|
11
|
-
export type { Document } from "./document";
|
|
12
|
-
export { splitTextIntoChunks } from "./document";
|
|
13
|
-
export { buildFallbackDocs, rerankDocs, processDocs, normalizeSourcesOutput, registerUploadFileLoader, loadUploadImages } from "./doc-utils";
|
|
14
|
-
export type { UploadFileLoader, LoadedUpload, UploadImageAttachment } from "./doc-utils";
|
|
1
|
+
/**
|
|
2
|
+
* @module agent-toolkit/tools/search
|
|
3
|
+
* @description Meta search agent tools for AI-powered web search and research
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { default as MetaSearchAgent } from "./metaSearchAgent";
|
|
7
|
+
export type { MetaSearchAgentType, Config, ChatTurnMessage, DrainableEmitter, SearchingEvent, FewShotExample } from "./meta-search-types";
|
|
8
|
+
export { searchHandlers, createSearchHandlers } from "./search-handlers";
|
|
9
|
+
export { default as generateSuggestions } from "./suggestionGeneratorAgent";
|
|
10
|
+
export { groupAndSummarizeDocs } from "./link-summarizer";
|
|
11
|
+
export type { Document } from "./document";
|
|
12
|
+
export { splitTextIntoChunks } from "./document";
|
|
13
|
+
export { buildFallbackDocs, rerankDocs, processDocs, normalizeSourcesOutput, registerUploadFileLoader, loadUploads, loadUploadImages, selectUploadImages } from "./doc-utils";
|
|
14
|
+
export type { UploadFileLoader, LoadedUpload, UploadImageAttachment } from "./doc-utils";
|
|
@@ -11,10 +11,16 @@ export interface ChatTurnMessage {
|
|
|
11
11
|
}
|
|
12
12
|
/** A `[role, content]` tuple used for few-shot prompt examples. */
|
|
13
13
|
export type FewShotExample = [role: "user" | "assistant", content: string];
|
|
14
|
+
/**
|
|
15
|
+
* The pipeline's event emitter, plus the hook a consumer uses to push back.
|
|
16
|
+
*/
|
|
17
|
+
export interface DrainableEmitter extends EventEmitter {
|
|
18
|
+
waitForDrain?: () => Promise<void>;
|
|
19
|
+
}
|
|
14
20
|
export interface MetaSearchAgentType {
|
|
15
21
|
searchAndAnswer: (message: string, history: ChatTurnMessage[], llm: LanguageModel, optimizationMode: "speed" | "balanced" | "quality", fileIds: string[], systemInstructions: string, category?: string, sourceExtractionEnabled?: boolean, thinkingTimeLimit?: number,
|
|
16
22
|
/** User-authored replacement for the focus mode's query-expansion prompt. */
|
|
17
|
-
queryExpansionPrompt?: string) => Promise<
|
|
23
|
+
queryExpansionPrompt?: string) => Promise<DrainableEmitter>;
|
|
18
24
|
}
|
|
19
25
|
/** Emitted on the EventEmitter data channel to report live search progress. */
|
|
20
26
|
export interface SearchingEvent {
|