chat-agent-toolkit 1.2.115 → 1.2.116
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/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/dist/tools/search/doc-utils.d.ts +26 -7
- package/dist/tools/search/index.d.ts +2 -2
- package/package.json +1 -1
- package/src/tools/search/doc-utils.ts +93 -33
- package/src/tools/search/index.ts +2 -2
- package/src/tools/search/metaSearchAgent.ts +17 -1
|
@@ -7,20 +7,39 @@ export interface R2CredentialsInput {
|
|
|
7
7
|
}
|
|
8
8
|
export declare function buildFallbackDocs(query: string): Document[];
|
|
9
9
|
export declare function normalizeSourcesOutput(output: unknown, query: string): Document[];
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
* Hosts register a loader (e.g. reading the native R2 binding) so the search
|
|
13
|
-
* pipeline does not depend on filesystem or credential-based access.
|
|
14
|
-
*/
|
|
15
|
-
export type UploadFileLoader = (fileId: string) => Promise<{
|
|
10
|
+
/** The payload resolved for an uploaded fileId. */
|
|
11
|
+
export interface LoadedUpload {
|
|
16
12
|
title: string;
|
|
17
13
|
content: string;
|
|
18
|
-
|
|
14
|
+
/** MIME type for image uploads (e.g. `"image/png"`). */
|
|
15
|
+
mediaType?: string;
|
|
16
|
+
/** `data:`/`http(s)` URL of an image upload, passed to the LLM as image content. */
|
|
17
|
+
image?: string;
|
|
18
|
+
}
|
|
19
|
+
/** An image attachment resolved from an uploaded fileId. */
|
|
20
|
+
export interface UploadImageAttachment {
|
|
21
|
+
mediaType: string;
|
|
22
|
+
/** `data:`/`http(s)` URL of the image, ready for an AI SDK image content part. */
|
|
23
|
+
image: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolves an uploaded fileId to its extracted payload. Hosts register a loader
|
|
27
|
+
* (e.g. reading the native R2 binding) so the search pipeline does not depend
|
|
28
|
+
* on filesystem or credential-based access. Image uploads additionally carry
|
|
29
|
+
* `mediaType` and `image` so they can be passed to the LLM directly.
|
|
30
|
+
*/
|
|
31
|
+
export type UploadFileLoader = (fileId: string) => Promise<LoadedUpload | null>;
|
|
19
32
|
/**
|
|
20
33
|
* Registers the loader used by {@link rerankDocs} to resolve uploaded
|
|
21
34
|
* fileIds to extracted content. The registered loader takes precedence over
|
|
22
35
|
* the S3-credentials fallback.
|
|
23
36
|
*/
|
|
24
37
|
export declare function registerUploadFileLoader(loader: UploadFileLoader): void;
|
|
38
|
+
/**
|
|
39
|
+
* Resolves image attachments for the given uploaded fileIds so they can be
|
|
40
|
+
* passed to the LLM as image content parts. Non-image uploads (documents) and
|
|
41
|
+
* images stored without inline data are skipped.
|
|
42
|
+
*/
|
|
43
|
+
export declare function loadUploadImages(fileIds: string[], r2Credentials?: R2CredentialsInput): Promise<UploadImageAttachment[]>;
|
|
25
44
|
export declare function rerankDocs(query: string, docs: Document[], fileIds: string[], optimizationMode: "speed" | "balanced" | "quality", r2Credentials?: R2CredentialsInput): Promise<Document[]>;
|
|
26
45
|
export declare function processDocs(docs: Document[]): string;
|
|
@@ -9,5 +9,5 @@ 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 } from './doc-utils';
|
|
13
|
-
export type { UploadFileLoader } from './doc-utils';
|
|
12
|
+
export { buildFallbackDocs, rerankDocs, processDocs, normalizeSourcesOutput, registerUploadFileLoader, loadUploadImages } from './doc-utils';
|
|
13
|
+
export type { UploadFileLoader, LoadedUpload, UploadImageAttachment } from './doc-utils';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chat-agent-toolkit",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.116",
|
|
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",
|
|
@@ -36,14 +36,30 @@ export function normalizeSourcesOutput(output: unknown, query: string): Document
|
|
|
36
36
|
return buildFallbackDocs(query);
|
|
37
37
|
}
|
|
38
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
|
+
|
|
39
56
|
/**
|
|
40
|
-
* Resolves an uploaded fileId to its extracted
|
|
41
|
-
*
|
|
42
|
-
*
|
|
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.
|
|
43
61
|
*/
|
|
44
|
-
export type UploadFileLoader = (
|
|
45
|
-
fileId: string,
|
|
46
|
-
) => Promise<{ title: string; content: string } | null>;
|
|
62
|
+
export type UploadFileLoader = (fileId: string) => Promise<LoadedUpload | null>;
|
|
47
63
|
|
|
48
64
|
let uploadFileLoader: UploadFileLoader | null = null;
|
|
49
65
|
|
|
@@ -56,7 +72,7 @@ export function registerUploadFileLoader(loader: UploadFileLoader): void {
|
|
|
56
72
|
uploadFileLoader = loader;
|
|
57
73
|
}
|
|
58
74
|
|
|
59
|
-
async function downloadExtractedContent(fileId: string, r2Credentials: R2CredentialsInput): Promise<
|
|
75
|
+
async function downloadExtractedContent(fileId: string, r2Credentials: R2CredentialsInput): Promise<LoadedUpload | null> {
|
|
60
76
|
try {
|
|
61
77
|
const { manageStorage } = await import("manage-storage");
|
|
62
78
|
const config = {
|
|
@@ -69,13 +85,69 @@ async function downloadExtractedContent(fileId: string, r2Credentials: R2Credent
|
|
|
69
85
|
const extractedKey = `${fileId}-extracted.json`;
|
|
70
86
|
const data = await manageStorage("download", { ...config, key: extractedKey });
|
|
71
87
|
const parsed = JSON.parse(data);
|
|
72
|
-
return {
|
|
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
|
+
};
|
|
73
94
|
} catch (error) {
|
|
74
95
|
console.error(`[rerankDocs] Failed to download extracted content for fileId ${fileId}:`, error);
|
|
75
96
|
return null;
|
|
76
97
|
}
|
|
77
98
|
}
|
|
78
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 image attachments for the given uploaded fileIds so they can be
|
|
127
|
+
* passed to the LLM as image content parts. Non-image uploads (documents) and
|
|
128
|
+
* images stored without inline data are skipped.
|
|
129
|
+
*/
|
|
130
|
+
export async function loadUploadImages(
|
|
131
|
+
fileIds: string[],
|
|
132
|
+
r2Credentials?: R2CredentialsInput,
|
|
133
|
+
): Promise<UploadImageAttachment[]> {
|
|
134
|
+
if (!fileIds || fileIds.length === 0) return [];
|
|
135
|
+
|
|
136
|
+
const resolved = await Promise.all(
|
|
137
|
+
fileIds.map((fileId) => resolveUpload(fileId, r2Credentials)),
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
return resolved
|
|
141
|
+
.filter(
|
|
142
|
+
(r): r is LoadedUpload =>
|
|
143
|
+
r !== null && typeof r.image === "string" && r.image.length > 0,
|
|
144
|
+
)
|
|
145
|
+
.map((r) => ({
|
|
146
|
+
mediaType: r.mediaType || "image/png",
|
|
147
|
+
image: r.image as string,
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
|
|
79
151
|
export async function rerankDocs(
|
|
80
152
|
query: string,
|
|
81
153
|
docs: Document[],
|
|
@@ -87,37 +159,25 @@ export async function rerankDocs(
|
|
|
87
159
|
return docs;
|
|
88
160
|
}
|
|
89
161
|
|
|
90
|
-
let filesData:
|
|
162
|
+
let filesData: LoadedUpload[] = [];
|
|
91
163
|
|
|
92
164
|
if (fileIds.length > 0) {
|
|
93
165
|
const results = await Promise.all(
|
|
94
|
-
fileIds.map(
|
|
95
|
-
if (uploadFileLoader) {
|
|
96
|
-
try {
|
|
97
|
-
const loaded = await uploadFileLoader(fileId);
|
|
98
|
-
if (loaded) return loaded;
|
|
99
|
-
} catch (error) {
|
|
100
|
-
console.error(
|
|
101
|
-
`[rerankDocs] Registered upload loader failed for fileId ${fileId}:`,
|
|
102
|
-
error,
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
if (r2Credentials) {
|
|
107
|
-
return downloadExtractedContent(fileId, r2Credentials);
|
|
108
|
-
}
|
|
109
|
-
return null;
|
|
110
|
-
}),
|
|
166
|
+
fileIds.map((fileId) => resolveUpload(fileId, r2Credentials)),
|
|
111
167
|
);
|
|
112
|
-
filesData = results.filter((r) => r !== null);
|
|
168
|
+
filesData = results.filter((r): r is LoadedUpload => r !== null);
|
|
113
169
|
}
|
|
114
170
|
|
|
115
|
-
// Uploaded
|
|
116
|
-
// every return path below keeps them in the answer context.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
171
|
+
// Uploaded documents must always reach the LLM. Build their docs up front so
|
|
172
|
+
// every return path below keeps them in the answer context. Image uploads
|
|
173
|
+
// carry no text content — they are passed separately as image parts (see
|
|
174
|
+
// loadUploadImages) — so they are excluded here to avoid empty docs.
|
|
175
|
+
const fileDocs: Document[] = filesData
|
|
176
|
+
.filter((fileData) => fileData.content && fileData.content.length > 0)
|
|
177
|
+
.map((fileData) => ({
|
|
178
|
+
pageContent: fileData.content,
|
|
179
|
+
metadata: { title: fileData.title, url: "File" },
|
|
180
|
+
}));
|
|
121
181
|
|
|
122
182
|
if (query.toLocaleLowerCase() === "summarize") {
|
|
123
183
|
// Skip web-result reranking for an explicit "summarize" request, but keep
|
|
@@ -10,5 +10,5 @@ export { default as generateSuggestions } from "./suggestionGeneratorAgent";
|
|
|
10
10
|
export { groupAndSummarizeDocs } from "./link-summarizer";
|
|
11
11
|
export type { Document } from "./document";
|
|
12
12
|
export { splitTextIntoChunks } from "./document";
|
|
13
|
-
export { buildFallbackDocs, rerankDocs, processDocs, normalizeSourcesOutput, registerUploadFileLoader } from "./doc-utils";
|
|
14
|
-
export type { UploadFileLoader } from "./doc-utils";
|
|
13
|
+
export { buildFallbackDocs, rerankDocs, processDocs, normalizeSourcesOutput, registerUploadFileLoader, loadUploadImages } from "./doc-utils";
|
|
14
|
+
export type { UploadFileLoader, LoadedUpload, UploadImageAttachment } from "./doc-utils";
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
rerankDocs,
|
|
36
36
|
processDocs,
|
|
37
37
|
normalizeSourcesOutput,
|
|
38
|
+
loadUploadImages,
|
|
38
39
|
type R2CredentialsInput,
|
|
39
40
|
} from "./doc-utils";
|
|
40
41
|
import { groupAndSummarizeDocs } from "./link-summarizer";
|
|
@@ -378,11 +379,26 @@ class MetaSearchAgent implements MetaSearchAgentType {
|
|
|
378
379
|
date: new Date().toISOString(),
|
|
379
380
|
});
|
|
380
381
|
|
|
382
|
+
// Resolve any uploaded images so they are passed to the LLM directly as
|
|
383
|
+
// image content parts (alongside the text query). Documents already
|
|
384
|
+
// reach the model as text context via processDocs above.
|
|
385
|
+
const imageAttachments = await loadUploadImages(fileIds, r2Credentials);
|
|
386
|
+
const userContent =
|
|
387
|
+
imageAttachments.length > 0
|
|
388
|
+
? [
|
|
389
|
+
{ type: "text" as const, text: message },
|
|
390
|
+
...imageAttachments.map((img) => ({
|
|
391
|
+
type: "image" as const,
|
|
392
|
+
image: img.image,
|
|
393
|
+
})),
|
|
394
|
+
]
|
|
395
|
+
: message;
|
|
396
|
+
|
|
381
397
|
const result = streamText({
|
|
382
398
|
model: llm,
|
|
383
399
|
temperature: 0.7,
|
|
384
400
|
system: systemPrompt,
|
|
385
|
-
messages: [...history, { role: "user", content:
|
|
401
|
+
messages: [...history, { role: "user", content: userContent }],
|
|
386
402
|
});
|
|
387
403
|
|
|
388
404
|
let responseChunkCount = 0;
|