@stackfactor/agent-utils 1.7.0 → 1.9.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 +15 -0
- package/dist/cjs/errorHandling.d.ts.map +1 -1
- package/dist/cjs/errorHandling.js +10 -5
- package/dist/cjs/index.d.ts +5 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +5 -1
- package/dist/cjs/langChain.d.ts +1 -1
- package/dist/cjs/langChain.d.ts.map +1 -1
- package/dist/cjs/langChain.js +27 -8
- package/dist/cjs/stackfactorKnowledge.d.ts +182 -0
- package/dist/cjs/stackfactorKnowledge.d.ts.map +1 -0
- package/dist/cjs/stackfactorKnowledge.js +274 -0
- package/dist/cjs/tavily.d.ts +31 -0
- package/dist/cjs/tavily.d.ts.map +1 -1
- package/dist/cjs/tavily.js +88 -1
- package/dist/esm/errorHandling.d.ts.map +1 -1
- package/dist/esm/errorHandling.js +10 -5
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +4 -0
- package/dist/esm/langChain.d.ts +1 -1
- package/dist/esm/langChain.d.ts.map +1 -1
- package/dist/esm/langChain.js +27 -8
- package/dist/esm/stackfactorKnowledge.d.ts +182 -0
- package/dist/esm/stackfactorKnowledge.d.ts.map +1 -0
- package/dist/esm/stackfactorKnowledge.js +266 -0
- package/dist/esm/tavily.d.ts +31 -0
- package/dist/esm/tavily.d.ts.map +1 -1
- package/dist/esm/tavily.js +86 -0
- package/package.json +5 -4
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
//
|
|
3
|
+
// StackFactor knowledge — grounding generated content in the knowledge bases
|
|
4
|
+
//
|
|
5
|
+
// A generation agent grounded in StackFactor (the Additional Information data
|
|
6
|
+
// source "StackFactor", `dataSource: "RAG"`) writes from knowledge-base
|
|
7
|
+
// passages instead of a web search, and cites every passage it used as a
|
|
8
|
+
// STACKFACTOR reference. A later agent generating for the same skill (its
|
|
9
|
+
// assessments, its learning content) reads those references and searches the
|
|
10
|
+
// documents they cite. Designed with Candin, 2026-10-08:
|
|
11
|
+
//
|
|
12
|
+
// - which knowledge bases: every one the person can open that holds the
|
|
13
|
+
// skill (skill coverage), never a fixed number of them;
|
|
14
|
+
// - which references: one per passage used, with no cap per document or per
|
|
15
|
+
// microskill;
|
|
16
|
+
// - access: the platform's search judges the person the run acts for, so a
|
|
17
|
+
// run reads only what that person may open. A knowledge base the person
|
|
18
|
+
// cannot read is skipped and reported, never an error: the agent
|
|
19
|
+
// generates without it.
|
|
20
|
+
//
|
|
21
|
+
// A reference points at the content and never copies it: the passage is
|
|
22
|
+
// searched again, under the reader's own access, whenever it is used. It is
|
|
23
|
+
// EDIT-only (`visibility: "EDIT"`): the platform strips it from what is
|
|
24
|
+
// published, so a knowledge base's titles never reach a learner; agents read
|
|
25
|
+
// it from the draft.
|
|
26
|
+
//
|
|
27
|
+
// The search itself is the caller's (`search`: client-api
|
|
28
|
+
// `knowledgeBaseAgent.search`, 1.11 or later for `documentIds`), so this
|
|
29
|
+
// module needs no particular client-api.
|
|
30
|
+
//
|
|
31
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
+
exports.referencesFor = exports.passagesForPrompt = exports.retrievePassages = exports.sourcesFromCoverage = exports.sourcesFromReferences = exports.DEFAULT_TOP_K = exports.QUERY_MAX_LENGTH = exports.SEARCH_DOCUMENT_IDS_MAX = exports.STACKFACTOR_REFERENCE = void 0;
|
|
33
|
+
/** What a STACKFACTOR reference to a knowledge base document carries (backend-core REFERENCE). */
|
|
34
|
+
exports.STACKFACTOR_REFERENCE = Object.freeze({
|
|
35
|
+
KIND: "STACKFACTOR",
|
|
36
|
+
TARGET_ELEMENT_TYPE: "KNOWLEDGE_BASE_DOCUMENT",
|
|
37
|
+
ORIGIN: "KB_EXTRACTION",
|
|
38
|
+
VISIBILITY: "EDIT",
|
|
39
|
+
});
|
|
40
|
+
/** How many documents one search may be kept to (the backend's bound); more are split. */
|
|
41
|
+
exports.SEARCH_DOCUMENT_IDS_MAX = 500;
|
|
42
|
+
/** How long a query may be (the backend's bound); longer ones are cut. */
|
|
43
|
+
exports.QUERY_MAX_LENGTH = 4000;
|
|
44
|
+
/** How many passages one search asks for when the caller does not say. */
|
|
45
|
+
exports.DEFAULT_TOP_K = 8;
|
|
46
|
+
/** How many searches run at once. */
|
|
47
|
+
const CONCURRENCY = 4;
|
|
48
|
+
/** Statuses that mean the person may not read the knowledge base, or it is gone. */
|
|
49
|
+
const NO_ACCESS = new Set([401, 403, 404]);
|
|
50
|
+
const text = (value) => (typeof value === "string" ? value.trim() : "");
|
|
51
|
+
const id = (value) => typeof value === "string" ? value.trim() : value != null && typeof value === "object" ? String(value) : "";
|
|
52
|
+
/**
|
|
53
|
+
* The knowledge bases, and the documents in them, that a list of references
|
|
54
|
+
* cites: its STACKFACTOR references to knowledge base documents, by their
|
|
55
|
+
* `knowledgeBase` ids, or by their `target` when only that is there. Any other
|
|
56
|
+
* reference is passed over. Each knowledge base once, its documents once each,
|
|
57
|
+
* in the order they first appear.
|
|
58
|
+
* @param references A skill's (or a microskill's) references
|
|
59
|
+
* @returns The sources to search
|
|
60
|
+
*/
|
|
61
|
+
const sourcesFromReferences = (references) => {
|
|
62
|
+
const byBase = new Map();
|
|
63
|
+
for (const reference of Array.isArray(references) ? references : []) {
|
|
64
|
+
if (reference?.kind !== exports.STACKFACTOR_REFERENCE.KIND)
|
|
65
|
+
continue;
|
|
66
|
+
const target = reference.target ?? {};
|
|
67
|
+
const isDocumentTarget = target.elementType === exports.STACKFACTOR_REFERENCE.TARGET_ELEMENT_TYPE;
|
|
68
|
+
const knowledgeBaseId = id(reference.knowledgeBase?.knowledgeBaseId) || (isDocumentTarget ? id(target.parentId) : "");
|
|
69
|
+
const documentId = id(reference.knowledgeBase?.documentId) || (isDocumentTarget ? id(target.elementId) : "");
|
|
70
|
+
if (!knowledgeBaseId || !documentId)
|
|
71
|
+
continue;
|
|
72
|
+
const documents = byBase.get(knowledgeBaseId) ?? new Set();
|
|
73
|
+
documents.add(documentId);
|
|
74
|
+
byBase.set(knowledgeBaseId, documents);
|
|
75
|
+
}
|
|
76
|
+
return [...byBase.entries()].map(([knowledgeBaseId, documents]) => ({
|
|
77
|
+
knowledgeBaseId,
|
|
78
|
+
documentIds: [...documents],
|
|
79
|
+
}));
|
|
80
|
+
};
|
|
81
|
+
exports.sourcesFromReferences = sourcesFromReferences;
|
|
82
|
+
/**
|
|
83
|
+
* The knowledge bases skill coverage found (client-api
|
|
84
|
+
* `knowledgeBase.getSkillCoverage`): every one the person can open that holds
|
|
85
|
+
* the skill, each searched whole.
|
|
86
|
+
* @param coverage The coverage answer, `{ knowledgeBases: [{ _id }] }`
|
|
87
|
+
* @returns The sources to search
|
|
88
|
+
*/
|
|
89
|
+
const sourcesFromCoverage = (coverage) => {
|
|
90
|
+
const bases = coverage?.knowledgeBases;
|
|
91
|
+
const seen = new Set();
|
|
92
|
+
const sources = [];
|
|
93
|
+
for (const base of Array.isArray(bases) ? bases : []) {
|
|
94
|
+
const knowledgeBaseId = id(base?._id);
|
|
95
|
+
if (!knowledgeBaseId || seen.has(knowledgeBaseId))
|
|
96
|
+
continue;
|
|
97
|
+
seen.add(knowledgeBaseId);
|
|
98
|
+
sources.push({ knowledgeBaseId });
|
|
99
|
+
}
|
|
100
|
+
return sources;
|
|
101
|
+
};
|
|
102
|
+
exports.sourcesFromCoverage = sourcesFromCoverage;
|
|
103
|
+
/** The documents of a source in batches the backend accepts; one batch of none for a whole knowledge base. */
|
|
104
|
+
const batchesOf = (source) => {
|
|
105
|
+
if (!Array.isArray(source.documentIds))
|
|
106
|
+
return [undefined];
|
|
107
|
+
const batches = [];
|
|
108
|
+
for (let at = 0; at < source.documentIds.length; at += exports.SEARCH_DOCUMENT_IDS_MAX) {
|
|
109
|
+
batches.push(source.documentIds.slice(at, at + exports.SEARCH_DOCUMENT_IDS_MAX));
|
|
110
|
+
}
|
|
111
|
+
return batches;
|
|
112
|
+
};
|
|
113
|
+
/** Runs `worker` over `items`, at most `limit` at a time. */
|
|
114
|
+
const inPool = async (items, limit, worker) => {
|
|
115
|
+
let next = 0;
|
|
116
|
+
const run = async () => {
|
|
117
|
+
while (next < items.length) {
|
|
118
|
+
const index = next++;
|
|
119
|
+
await worker(items[index]);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, run));
|
|
123
|
+
};
|
|
124
|
+
/** The status of a failed platform call, from an axios error or one shaped like it. */
|
|
125
|
+
const statusOf = (error) => {
|
|
126
|
+
const status = error?.response?.status ?? error?.status;
|
|
127
|
+
return typeof status === "number" ? status : null;
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Searches every source for every query, under the person's access, and hands
|
|
131
|
+
* back the passages found, best first, each once, tagged "k1", "k2"… for the
|
|
132
|
+
* model to cite. A source with documents is searched within them (in batches of
|
|
133
|
+
* SEARCH_DOCUMENT_IDS_MAX); one without, whole. A source the person may not
|
|
134
|
+
* read (401, 403, 404) is skipped: none of its passages are used and no more of
|
|
135
|
+
* its searches are made. A search that fails otherwise is reported and the
|
|
136
|
+
* passages its other searches found stand. Either way it is in `skipped`, once
|
|
137
|
+
* per knowledge base, and the passages of the others stand.
|
|
138
|
+
* @param options `sources`, `queries`, `search` (client-api
|
|
139
|
+
* `knowledgeBaseAgent.search`), `token` (the run's AGENT token), `topK`
|
|
140
|
+
* per search (default DEFAULT_TOP_K)
|
|
141
|
+
* @returns `{ passages, skipped }`
|
|
142
|
+
*/
|
|
143
|
+
const retrievePassages = async (options) => {
|
|
144
|
+
const queries = [
|
|
145
|
+
...new Set((Array.isArray(options.queries) ? options.queries : [])
|
|
146
|
+
.map((query) => text(query).slice(0, exports.QUERY_MAX_LENGTH))
|
|
147
|
+
.filter(Boolean)),
|
|
148
|
+
];
|
|
149
|
+
const sources = Array.isArray(options.sources) ? options.sources : [];
|
|
150
|
+
const topK = options.topK ?? exports.DEFAULT_TOP_K;
|
|
151
|
+
const searches = [];
|
|
152
|
+
for (const source of sources) {
|
|
153
|
+
for (const documentIds of batchesOf(source)) {
|
|
154
|
+
if (documentIds && documentIds.length === 0)
|
|
155
|
+
continue;
|
|
156
|
+
for (const query of queries) {
|
|
157
|
+
searches.push({ source, query, documentIds, order: searches.length });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const found = new Map();
|
|
162
|
+
const skipped = new Map();
|
|
163
|
+
await inPool(searches, CONCURRENCY, async ({ source, query, documentIds, order }) => {
|
|
164
|
+
if (skipped.get(source.knowledgeBaseId)?.noAccess)
|
|
165
|
+
return;
|
|
166
|
+
try {
|
|
167
|
+
const answer = await options.search(source.knowledgeBaseId, query, documentIds ? { topK, documentIds } : { topK }, options.token);
|
|
168
|
+
(Array.isArray(answer?.chunks) ? answer.chunks : []).forEach((chunk, rank) => {
|
|
169
|
+
const chunkId = id(chunk?._id);
|
|
170
|
+
if (!chunkId || !id(chunk?.documentId))
|
|
171
|
+
return;
|
|
172
|
+
const known = found.get(chunkId);
|
|
173
|
+
const better = !known || (chunk.score ?? 0) > (known.chunk.score ?? 0);
|
|
174
|
+
// Ties keep the first search's rank, so the order is the searches' own.
|
|
175
|
+
found.set(chunkId, {
|
|
176
|
+
chunk: better ? chunk : known.chunk,
|
|
177
|
+
knowledgeBaseId: source.knowledgeBaseId,
|
|
178
|
+
order: known ? Math.min(known.order, order * 1000 + rank) : order * 1000 + rank,
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
const status = statusOf(error);
|
|
184
|
+
const noAccess = status !== null && NO_ACCESS.has(status);
|
|
185
|
+
// A refusal outranks a failure reported before it.
|
|
186
|
+
if (skipped.has(source.knowledgeBaseId) && !noAccess)
|
|
187
|
+
return;
|
|
188
|
+
skipped.set(source.knowledgeBaseId, {
|
|
189
|
+
knowledgeBaseId: source.knowledgeBaseId,
|
|
190
|
+
message: typeof error?.message === "string" ? error.message : String(error),
|
|
191
|
+
noAccess,
|
|
192
|
+
status,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
const passages = [...found.entries()]
|
|
197
|
+
.filter(([, entry]) => !skipped.get(entry.knowledgeBaseId)?.noAccess)
|
|
198
|
+
.sort(([, a], [, b]) => (b.chunk.score ?? 0) - (a.chunk.score ?? 0) || a.order - b.order)
|
|
199
|
+
.map(([chunkId, { chunk, knowledgeBaseId }], index) => ({
|
|
200
|
+
chunkId,
|
|
201
|
+
documentId: id(chunk.documentId),
|
|
202
|
+
documentName: text(chunk.documentName) || null,
|
|
203
|
+
knowledgeBaseId: id(chunk.knowledgeBaseId) || knowledgeBaseId,
|
|
204
|
+
pageNumber: typeof chunk.pageNumber === "number" ? chunk.pageNumber : null,
|
|
205
|
+
score: typeof chunk.score === "number" ? chunk.score : null,
|
|
206
|
+
sectionTitle: text(chunk.sectionTitle) || null,
|
|
207
|
+
skills: (Array.isArray(chunk.skills) ? chunk.skills : []).map((skill) => ({
|
|
208
|
+
microSkills: (Array.isArray(skill?.microSkills) ? skill.microSkills : []).filter((name) => typeof name === "string"),
|
|
209
|
+
name: typeof skill?.name === "string" ? skill.name : null,
|
|
210
|
+
})),
|
|
211
|
+
tag: `k${index + 1}`,
|
|
212
|
+
text: typeof chunk.text === "string" ? chunk.text : "",
|
|
213
|
+
}));
|
|
214
|
+
return { passages, skipped: [...skipped.values()] };
|
|
215
|
+
};
|
|
216
|
+
exports.retrievePassages = retrievePassages;
|
|
217
|
+
/**
|
|
218
|
+
* The passages as a prompt shows them: tag, where each came from, and its text.
|
|
219
|
+
* @param passages From `retrievePassages`
|
|
220
|
+
* @returns `[{ tag, document, section, text }]`, for the model's input
|
|
221
|
+
*/
|
|
222
|
+
const passagesForPrompt = (passages) => (Array.isArray(passages) ? passages : []).map((passage) => ({
|
|
223
|
+
document: passage.documentName,
|
|
224
|
+
section: passage.sectionTitle,
|
|
225
|
+
tag: passage.tag,
|
|
226
|
+
text: passage.text,
|
|
227
|
+
}));
|
|
228
|
+
exports.passagesForPrompt = passagesForPrompt;
|
|
229
|
+
/**
|
|
230
|
+
* STACKFACTOR references for passages: one per passage, in their order, with no
|
|
231
|
+
* cap. With `tags`, only the passages the model cited by tag (any tag it made
|
|
232
|
+
* up is passed over); without, every passage.
|
|
233
|
+
* @param passages From `retrievePassages`
|
|
234
|
+
* @param tags The tags the model cited ("k3", "K3" and " k3 " alike)
|
|
235
|
+
* @returns The references, ready for a skill's, a microskill's or a question's `references`
|
|
236
|
+
*/
|
|
237
|
+
const referencesFor = (passages, tags) => {
|
|
238
|
+
const list = Array.isArray(passages) ? passages : [];
|
|
239
|
+
const cited = Array.isArray(tags)
|
|
240
|
+
? new Set(tags.map((tag) => text(tag).toLowerCase()).filter(Boolean))
|
|
241
|
+
: null;
|
|
242
|
+
return list
|
|
243
|
+
.filter((passage) => !cited || cited.has(passage.tag.toLowerCase()))
|
|
244
|
+
.map((passage) => ({
|
|
245
|
+
description: passage.sectionTitle,
|
|
246
|
+
kind: exports.STACKFACTOR_REFERENCE.KIND,
|
|
247
|
+
knowledgeBase: {
|
|
248
|
+
chunkId: passage.chunkId,
|
|
249
|
+
documentId: passage.documentId,
|
|
250
|
+
knowledgeBaseId: passage.knowledgeBaseId,
|
|
251
|
+
sectionTitle: passage.sectionTitle,
|
|
252
|
+
},
|
|
253
|
+
origin: exports.STACKFACTOR_REFERENCE.ORIGIN,
|
|
254
|
+
target: {
|
|
255
|
+
elementId: passage.documentId,
|
|
256
|
+
elementType: exports.STACKFACTOR_REFERENCE.TARGET_ELEMENT_TYPE,
|
|
257
|
+
parentId: passage.knowledgeBaseId,
|
|
258
|
+
},
|
|
259
|
+
title: passage.documentName || passage.sectionTitle || "Knowledge base document",
|
|
260
|
+
visibility: exports.STACKFACTOR_REFERENCE.VISIBILITY,
|
|
261
|
+
}));
|
|
262
|
+
};
|
|
263
|
+
exports.referencesFor = referencesFor;
|
|
264
|
+
exports.default = {
|
|
265
|
+
DEFAULT_TOP_K: exports.DEFAULT_TOP_K,
|
|
266
|
+
QUERY_MAX_LENGTH: exports.QUERY_MAX_LENGTH,
|
|
267
|
+
SEARCH_DOCUMENT_IDS_MAX: exports.SEARCH_DOCUMENT_IDS_MAX,
|
|
268
|
+
STACKFACTOR_REFERENCE: exports.STACKFACTOR_REFERENCE,
|
|
269
|
+
passagesForPrompt: exports.passagesForPrompt,
|
|
270
|
+
referencesFor: exports.referencesFor,
|
|
271
|
+
retrievePassages: exports.retrievePassages,
|
|
272
|
+
sourcesFromCoverage: exports.sourcesFromCoverage,
|
|
273
|
+
sourcesFromReferences: exports.sourcesFromReferences,
|
|
274
|
+
};
|
package/dist/cjs/tavily.d.ts
CHANGED
|
@@ -82,9 +82,40 @@ export declare const assertTavilyUsable: (config: any, options: TavilyConfig) =>
|
|
|
82
82
|
* @returns LangChain tool instances to append to the agent's tool list
|
|
83
83
|
*/
|
|
84
84
|
export declare const buildTavilyTools: (config: any, options: TavilyConfig, usageTracker?: UsageTracker | null) => any[];
|
|
85
|
+
/**
|
|
86
|
+
* Which of these URLs resolve, judged by Tavily fetching them rather than by
|
|
87
|
+
* this process connecting to them.
|
|
88
|
+
*
|
|
89
|
+
* An agent that confirms a cited link by requesting it itself sends that
|
|
90
|
+
* request from inside StackFactor's network, through its fixed egress
|
|
91
|
+
* addresses, to wherever the link, a redirect or its DNS points — and cited
|
|
92
|
+
* links come from web pages anyone can publish, so that is a request to an
|
|
93
|
+
* address an outsider chose (server-side request forgery). Tavily fetches from
|
|
94
|
+
* its own infrastructure, so nothing here ever connects to such a host.
|
|
95
|
+
*
|
|
96
|
+
* Only `http:` and `https:` URLs are sent; any other is unreachable by
|
|
97
|
+
* definition. They go in batches of 20, Tavily's limit, at basic depth: 1
|
|
98
|
+
* credit per 5 URLs Tavily fetched, failed ones free. The credits are billed
|
|
99
|
+
* like every other Tavily call, but the URLs are not added to the tracker's
|
|
100
|
+
* sources: a link worth checking was found there in the first place.
|
|
101
|
+
*
|
|
102
|
+
* Tavily accounts for every URL it is given, listing the ones it could not
|
|
103
|
+
* fetch under `failed_results`. So a URL listed there is unreachable, and every
|
|
104
|
+
* other URL of a batch Tavily answered is reachable (the results may name a
|
|
105
|
+
* URL by its address after a redirect, which is why they are not matched). A
|
|
106
|
+
* batch whose call fails outright, or a config without `tavilyAPIKey`,
|
|
107
|
+
* confirms none of its URLs: a link that could not be checked is not trusted.
|
|
108
|
+
*
|
|
109
|
+
* @param urls - The URLs to check; a URL given twice is checked once
|
|
110
|
+
* @param config - The integration config; supplies `tavilyAPIKey`
|
|
111
|
+
* @param usageTracker - Optional accumulator the credits are counted into
|
|
112
|
+
* @returns The given URLs, exactly as passed, that resolve
|
|
113
|
+
*/
|
|
114
|
+
export declare const checkUrlsReachable: (urls: readonly string[], config: any, usageTracker?: UsageTracker | null) => Promise<Set<string>>;
|
|
85
115
|
declare const _default: {
|
|
86
116
|
assertTavilyUsable: (config: any, options: TavilyConfig) => void;
|
|
87
117
|
buildTavilyTools: (config: any, options: TavilyConfig, usageTracker?: UsageTracker | null) => any[];
|
|
118
|
+
checkUrlsReachable: (urls: readonly string[], config: any, usageTracker?: UsageTracker | null) => Promise<Set<string>>;
|
|
88
119
|
getTavilyOptions: (config: any) => TavilyConfig | null;
|
|
89
120
|
recordTavilyUsage: (tracker: UsageTracker | null | undefined, _config: any, credits: number, sources: {
|
|
90
121
|
url: string;
|
package/dist/cjs/tavily.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tavily.d.ts","sourceRoot":"","sources":["../../src/tavily.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AA0BnD,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,KAAK,CAAC;AAEpE;;;;;;;;;GASG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,iEAAiE;IACjE,WAAW,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,YAAY,CAAC;IAC3D,kFAAkF;IAClF,YAAY,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IACpC,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,6CAA6C;IAC7C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;CAC9B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,QAAQ,GAAG,KAAG,YAAY,GAAG,IAI7D,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,GAAG,EAAE,SAAS,YAAY,KAAG,IAsBvE,CAAC;AAsLF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,GAAG,EACX,SAAS,YAAY,EACrB,eAAc,YAAY,GAAG,IAAW,KACvC,GAAG,EA2PL,CAAC;;
|
|
1
|
+
{"version":3,"file":"tavily.d.ts","sourceRoot":"","sources":["../../src/tavily.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AA0BnD,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,KAAK,CAAC;AAEpE;;;;;;;;;GASG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,iEAAiE;IACjE,WAAW,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,YAAY,CAAC;IAC3D,kFAAkF;IAClF,YAAY,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IACpC,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,6CAA6C;IAC7C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;CAC9B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,QAAQ,GAAG,KAAG,YAAY,GAAG,IAI7D,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,GAAG,EAAE,SAAS,YAAY,KAAG,IAsBvE,CAAC;AAsLF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,GAAG,EACX,SAAS,YAAY,EACrB,eAAc,YAAY,GAAG,IAAW,KACvC,GAAG,EA2PL,CAAC;AAqBF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,kBAAkB,GAC7B,MAAM,SAAS,MAAM,EAAE,EACvB,QAAQ,GAAG,EACX,eAAc,YAAY,GAAG,IAAW,KACvC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CA6DrB,CAAC;;iCA/kByC,GAAG,WAAW,YAAY,KAAG,IAAI;+BA8NlE,GAAG,WACF,YAAY,iBACP,YAAY,GAAG,IAAI,KAChC,GAAG,EAAE;+BA8SA,SAAS,MAAM,EAAE,UACf,GAAG,iBACG,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;+BAjiBkB,GAAG,KAAG,YAAY,GAAG,IAAI;iCAsIvD,YAAY,GAAG,IAAI,GAAG,SAAS,WAC/B,GAAG,WACH,MAAM,WACN;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,KACzC,IAAI;;AAsdP,wBAME"}
|
package/dist/cjs/tavily.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.buildTavilyTools = exports.assertTavilyUsable = exports.getTavilyOptions = void 0;
|
|
6
|
+
exports.checkUrlsReachable = exports.buildTavilyTools = exports.assertTavilyUsable = exports.getTavilyOptions = void 0;
|
|
7
7
|
const langchain_1 = require("langchain");
|
|
8
8
|
const zod_1 = require("zod");
|
|
9
9
|
const const_js_1 = __importDefault(require("./const.js"));
|
|
@@ -395,9 +395,96 @@ const buildTavilyTools = (config, options, usageTracker = null) => {
|
|
|
395
395
|
return tools;
|
|
396
396
|
};
|
|
397
397
|
exports.buildTavilyTools = buildTavilyTools;
|
|
398
|
+
/** Tavily's own limit on URLs per `/extract` request. */
|
|
399
|
+
const EXTRACT_MAX_URLS = 20;
|
|
400
|
+
/** Seconds. A reachability check needs the page fetched, not rendered at length. */
|
|
401
|
+
const REACHABILITY_TIMEOUT_SECONDS = 20;
|
|
402
|
+
/** How a URL is matched against Tavily's answer: trimmed, lowercased, no trailing slash. */
|
|
403
|
+
const urlMatchKey = (url) => url.trim().replace(/\/+$/, "").toLowerCase();
|
|
404
|
+
/** True for an `http:` or `https:` URL; false for anything else, unparsable included. */
|
|
405
|
+
const isWebUrl = (url) => {
|
|
406
|
+
try {
|
|
407
|
+
const { protocol } = new URL(url);
|
|
408
|
+
return protocol === "http:" || protocol === "https:";
|
|
409
|
+
}
|
|
410
|
+
catch {
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
/**
|
|
415
|
+
* Which of these URLs resolve, judged by Tavily fetching them rather than by
|
|
416
|
+
* this process connecting to them.
|
|
417
|
+
*
|
|
418
|
+
* An agent that confirms a cited link by requesting it itself sends that
|
|
419
|
+
* request from inside StackFactor's network, through its fixed egress
|
|
420
|
+
* addresses, to wherever the link, a redirect or its DNS points — and cited
|
|
421
|
+
* links come from web pages anyone can publish, so that is a request to an
|
|
422
|
+
* address an outsider chose (server-side request forgery). Tavily fetches from
|
|
423
|
+
* its own infrastructure, so nothing here ever connects to such a host.
|
|
424
|
+
*
|
|
425
|
+
* Only `http:` and `https:` URLs are sent; any other is unreachable by
|
|
426
|
+
* definition. They go in batches of 20, Tavily's limit, at basic depth: 1
|
|
427
|
+
* credit per 5 URLs Tavily fetched, failed ones free. The credits are billed
|
|
428
|
+
* like every other Tavily call, but the URLs are not added to the tracker's
|
|
429
|
+
* sources: a link worth checking was found there in the first place.
|
|
430
|
+
*
|
|
431
|
+
* Tavily accounts for every URL it is given, listing the ones it could not
|
|
432
|
+
* fetch under `failed_results`. So a URL listed there is unreachable, and every
|
|
433
|
+
* other URL of a batch Tavily answered is reachable (the results may name a
|
|
434
|
+
* URL by its address after a redirect, which is why they are not matched). A
|
|
435
|
+
* batch whose call fails outright, or a config without `tavilyAPIKey`,
|
|
436
|
+
* confirms none of its URLs: a link that could not be checked is not trusted.
|
|
437
|
+
*
|
|
438
|
+
* @param urls - The URLs to check; a URL given twice is checked once
|
|
439
|
+
* @param config - The integration config; supplies `tavilyAPIKey`
|
|
440
|
+
* @param usageTracker - Optional accumulator the credits are counted into
|
|
441
|
+
* @returns The given URLs, exactly as passed, that resolve
|
|
442
|
+
*/
|
|
443
|
+
const checkUrlsReachable = async (urls, config, usageTracker = null) => {
|
|
444
|
+
const reachable = new Set();
|
|
445
|
+
const candidates = [
|
|
446
|
+
...new Set(urls.filter((url) => typeof url === "string" && isWebUrl(url.trim()))),
|
|
447
|
+
];
|
|
448
|
+
if (candidates.length === 0)
|
|
449
|
+
return reachable;
|
|
450
|
+
const apiKey = config?.tavilyAPIKey;
|
|
451
|
+
if (!apiKey) {
|
|
452
|
+
logger_js_1.default.log(null, logger_js_1.default.levels.warn, `Tavily reachability: no tavilyAPIKey, so none of ${candidates.length} URL(s) is confirmed`);
|
|
453
|
+
return reachable;
|
|
454
|
+
}
|
|
455
|
+
const batches = [];
|
|
456
|
+
for (let start = 0; start < candidates.length; start += EXTRACT_MAX_URLS) {
|
|
457
|
+
batches.push(candidates.slice(start, start + EXTRACT_MAX_URLS));
|
|
458
|
+
}
|
|
459
|
+
await Promise.all(batches.map(async (batch) => {
|
|
460
|
+
let data;
|
|
461
|
+
try {
|
|
462
|
+
data = await callTavily("extract", {
|
|
463
|
+
urls: batch.map((url) => url.trim()),
|
|
464
|
+
extract_depth: "basic",
|
|
465
|
+
format: "text",
|
|
466
|
+
}, apiKey, REACHABILITY_TIMEOUT_SECONDS);
|
|
467
|
+
}
|
|
468
|
+
catch (err) {
|
|
469
|
+
logger_js_1.default.log(null, logger_js_1.default.levels.warn, `Tavily reachability: the check of ${batch.length} URL(s) failed (${err?.message || String(err)}), so none of them is confirmed`);
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
recordTavilyUsage(usageTracker, config, data?.usage?.credits || 0, []);
|
|
473
|
+
const failed = new Set((data?.failed_results || [])
|
|
474
|
+
.map((entry) => typeof entry?.url === "string" ? urlMatchKey(entry.url) : "")
|
|
475
|
+
.filter(Boolean));
|
|
476
|
+
for (const url of batch) {
|
|
477
|
+
if (!failed.has(urlMatchKey(url)))
|
|
478
|
+
reachable.add(url);
|
|
479
|
+
}
|
|
480
|
+
}));
|
|
481
|
+
return reachable;
|
|
482
|
+
};
|
|
483
|
+
exports.checkUrlsReachable = checkUrlsReachable;
|
|
398
484
|
exports.default = {
|
|
399
485
|
assertTavilyUsable: exports.assertTavilyUsable,
|
|
400
486
|
buildTavilyTools: exports.buildTavilyTools,
|
|
487
|
+
checkUrlsReachable: exports.checkUrlsReachable,
|
|
401
488
|
getTavilyOptions: exports.getTavilyOptions,
|
|
402
489
|
recordTavilyUsage,
|
|
403
490
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorHandling.d.ts","sourceRoot":"","sources":["../../src/errorHandling.ts"],"names":[],"mappings":"AAGA,gEAAgE;AAChE,KAAK,QAAQ,GACT,OAAO,GACP,MAAM,GACN,MAAM,GACN,MAAM,GACN,SAAS,GACT,OAAO,GACP,OAAO,CAAC;AAEZ;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;
|
|
1
|
+
{"version":3,"file":"errorHandling.d.ts","sourceRoot":"","sources":["../../src/errorHandling.ts"],"names":[],"mappings":"AAGA,gEAAgE;AAChE,KAAK,QAAQ,GACT,OAAO,GACP,MAAM,GACN,MAAM,GACN,MAAM,GACN,SAAS,GACT,OAAO,GACP,OAAO,CAAC;AAEZ;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAmHD;;;;;;;;;;;;GAYG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACjC,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC;IACvB,qEAAqE;IACrE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;gBAElB,MAAM,GAAE,WAAgB;IAUpC;;;OAGG;IACH,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ;IAKrC;;;OAGG;IACH,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW;IAIzC;;;;OAIG;IACH,OAAO,IAAI,WAAW,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAS1D,MAAM,IAAI,WAAW;IAIrB;;;;OAIG;IACH,WAAW,IAAI,KAAK;IAIpB;;;;;;;;OAQG;IACH,GAAG,CAAC,OAAO,GAAE,GAAU,EAAE,KAAK,GAAE,QAAkB,EAAE,OAAO,GAAE,GAAQ,GAAG,IAAI;CAU7E;;IAkBC;;;;;;;;;OASG;wBACiB,MAAM,gBAAgB,MAAM,YAAW,GAAG;;;;;;IAU9D,6EAA6E;mBAzOpD,GAAG,KAAG,WAAW;IA2O1C,0EAA0E;;IAE1E,gFAAgF;oBA9BzE,OAAO,YACL,GAAG,YACH,GAAG,KACX,QAAQ;IA6BT,wDAAwD;uBA1PhC,GAAG,KAAG,IAAI,IAAI,MAAM;IA4P5C,kCAAkC;;;AA7BpC,wBA+BE"}
|
|
@@ -81,11 +81,16 @@ const parseError = (error) => {
|
|
|
81
81
|
// A JSON-encoded error string, or just a plain message.
|
|
82
82
|
try {
|
|
83
83
|
const parsed = JSON.parse(error);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
84
|
+
// Only an object is an encoded error; "404" or "true" also parse as
|
|
85
|
+
// JSON but are just the message text.
|
|
86
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
87
|
+
return {
|
|
88
|
+
code: isHttpCode(parsed.code) ? parsed.code : undefined,
|
|
89
|
+
message: parsed.message,
|
|
90
|
+
integration: parsed.integration,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return { message: error };
|
|
89
94
|
}
|
|
90
95
|
catch {
|
|
91
96
|
return { message: error };
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -6,11 +6,14 @@ import { serve, runOnce, isRunOnceExecution, EXECUTION_ENV } from "./serve.js";
|
|
|
6
6
|
import { callAgent, checkAgent } from "./client.js";
|
|
7
7
|
import * as runtimeContext from "./runtimeContext.js";
|
|
8
8
|
import { recordUsage, USAGE_UNITS } from "./usage.js";
|
|
9
|
+
import stackfactorKnowledge from "./stackfactorKnowledge.js";
|
|
10
|
+
import { checkUrlsReachable } from "./tavily.js";
|
|
9
11
|
export { constants };
|
|
10
12
|
export { errorHandling, AppError };
|
|
11
13
|
export type { ParsedError } from "./errorHandling.js";
|
|
12
14
|
export { langChain };
|
|
13
15
|
export type { UsageTracker } from "./langChain.js";
|
|
16
|
+
export { checkUrlsReachable };
|
|
14
17
|
export type { TavilyConfig, TavilyToolName } from "./tavily.js";
|
|
15
18
|
export { logger };
|
|
16
19
|
export { serve, runOnce, isRunOnceExecution, EXECUTION_ENV };
|
|
@@ -20,4 +23,6 @@ export type { AgentExecuteRequest, CallAgentOptions, CheckAgentOptions, AgentHea
|
|
|
20
23
|
export { runtimeContext };
|
|
21
24
|
export { recordUsage, USAGE_UNITS };
|
|
22
25
|
export type { UsageEntry, UsageUnit } from "./usage.js";
|
|
26
|
+
export { stackfactorKnowledge };
|
|
27
|
+
export type { GroundingPassage, KnowledgeChunk, KnowledgeSearch, KnowledgeSource, SkippedSource, StackFactorReference, } from "./stackfactorKnowledge.js";
|
|
23
28
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,aAAa,EAAE,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,aAAa,EAAE,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,oBAAoB,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC;AAEnC,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAEhE,OAAO,EAAE,MAAM,EAAE,CAAC;AAElB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,CAAC;AAE7D,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,OAAO,EAAE,UAAU,EAAE,CAAC;AAEtB,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,GACZ,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;AAEpC,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAEhC,YAAY,EACV,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,eAAe,EACf,aAAa,EACb,oBAAoB,GACrB,MAAM,2BAA2B,CAAC"}
|
package/dist/esm/index.js
CHANGED
|
@@ -6,12 +6,16 @@ import { serve, runOnce, isRunOnceExecution, EXECUTION_ENV } from "./serve.js";
|
|
|
6
6
|
import { callAgent, checkAgent } from "./client.js";
|
|
7
7
|
import * as runtimeContext from "./runtimeContext.js";
|
|
8
8
|
import { recordUsage, USAGE_UNITS } from "./usage.js";
|
|
9
|
+
import stackfactorKnowledge from "./stackfactorKnowledge.js";
|
|
10
|
+
import { checkUrlsReachable } from "./tavily.js";
|
|
9
11
|
export { constants };
|
|
10
12
|
export { errorHandling, AppError };
|
|
11
13
|
export { langChain };
|
|
14
|
+
export { checkUrlsReachable };
|
|
12
15
|
export { logger };
|
|
13
16
|
export { serve, runOnce, isRunOnceExecution, EXECUTION_ENV };
|
|
14
17
|
export { callAgent };
|
|
15
18
|
export { checkAgent };
|
|
16
19
|
export { runtimeContext };
|
|
17
20
|
export { recordUsage, USAGE_UNITS };
|
|
21
|
+
export { stackfactorKnowledge };
|
package/dist/esm/langChain.d.ts
CHANGED
|
@@ -62,7 +62,7 @@ declare const _default: {
|
|
|
62
62
|
supportsNativeSchema: (modelName: string) => boolean;
|
|
63
63
|
checkIfAIProviderConfigured: (config: any) => void;
|
|
64
64
|
createAgent: (name: string, modelName: string, systemPrompt: string, tools: any[], responseFormat: any, config: any, usageTracker?: UsageTracker | null) => any;
|
|
65
|
-
runAgent: (agent: any, prompt: string, config: any, onProgress?: Function | null, usageTracker?: UsageTracker | null) => Promise<any>;
|
|
65
|
+
runAgent: (agent: any, prompt: string, config: any, onProgress?: Function | null, usageTracker?: UsageTracker | null, progress?: number) => Promise<any>;
|
|
66
66
|
runPromptWithModel: (modelName: string, config: any, prompt: any, onProgressReport: any, minPercent?: number, maxPercent?: number, expectsJsonResponse?: boolean, schema?: any, agentName?: string, tools?: any[], usageTracker?: UsageTracker | null) => Promise<any>;
|
|
67
67
|
runPromptWithModelForImageGeneration: (modelName: string, config: any, prompt: string, options?: any, usageTracker?: UsageTracker | null) => Promise<any>;
|
|
68
68
|
runPromptWithModelForVideoGeneration: (modelName: string, config: any, prompt: string, options?: any, usageTracker?: UsageTracker | null) => Promise<any>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":"AAoGA,KAAK,UAAU,GAAG;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IAKtB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACvC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACtD,CAAC;
|
|
1
|
+
{"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":"AAoGA,KAAK,UAAU,GAAG;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IAKtB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACvC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACtD,CAAC;AA0iBF;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,eAAO,MAAM,WAAW,GACtB,WAAW,MAAM,EACjB,QAAQ,GAAG,EACX,SAAQ,GAAU,KACjB,GAwHF,CAAC;;8CAkb+C,OAAO,UAAU,GAAG,KAAG,OAAO;oCAWxC,GAAG,KAAG,OAAO;0CAgBtC,MAAM,cACN,GAAG,SACR,GAAG,KACT,MAAM;2CA0CqC,GAAG,UAAU,GAAG,KAAG,MAAM;sCApxC9B,MAAM,KAAG,OAAO;0CA+FZ,GAAG,KAAG,IAAI;wBA8sB/C,MAAM,aACD,MAAM,gBACH,MAAM,SACb,GAAG,EAAE,kBACI,GAAG,UACX,GAAG,iBACG,YAAY,GAAG,IAAI,KAChC,GAAG;sBAsCG,GAAG,UACF,MAAM,UACN,GAAG,eACC,QAAQ,GAAG,IAAI,iBACb,YAAY,GAAG,IAAI,aACvB,MAAM,KACf,OAAO,CAAC,GAAG,CAAC;oCAghBF,MAAM,UACT,GAAG,UACH,GAAG,oBACO,GAAG,eACT,MAAM,eACN,MAAM,wBACG,OAAO,WACpB,GAAG,cACA,MAAM,UACV,GAAG,EAAE,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDA2sBF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDAmTF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;0CAx6C8B,GAAG,KAAG,MAAM;uCAxyB9C,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,YACP,GAAG,WACJ,GAAG,YACF,QAAQ,GAAG,QAAQ,KAC5B,IAAI;kCA5CI,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,SACV,UAAU,GAAG,IAAI,YACd,GAAG,KACZ,IAAI;+CA0FI,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,kBACD,MAAM,YACZ,GAAG,KACZ,IAAI;uCAoBI,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,WACR,MAAM,YACL,GAAG,KACZ,IAAI;mCAyuBU,MAAM,mBACJ,MAAM,EAAE,KACxB,MAAM;;AA28CT,wBAkBE"}
|
package/dist/esm/langChain.js
CHANGED
|
@@ -327,8 +327,14 @@ const formatZodErrors = (zodError) => {
|
|
|
327
327
|
const path = err.path.length > 0 ? err.path.join(".") : "root";
|
|
328
328
|
// Provide more context based on error type
|
|
329
329
|
switch (err.code) {
|
|
330
|
-
case "invalid_type":
|
|
331
|
-
|
|
330
|
+
case "invalid_type": {
|
|
331
|
+
// zod v3 sets `received`; zod v4 names it only in the message
|
|
332
|
+
// ("Invalid input: expected string, received number").
|
|
333
|
+
const received = err.received ??
|
|
334
|
+
/received (\w+)/.exec(String(err.message ?? ""))?.[1] ??
|
|
335
|
+
"a different type";
|
|
336
|
+
return `Field "${path}": Expected ${err.expected}, but received ${received}`;
|
|
337
|
+
}
|
|
332
338
|
case "invalid_literal":
|
|
333
339
|
return `Field "${path}": Expected literal value ${JSON.stringify(err.expected)}, but received ${JSON.stringify(err.received)}`;
|
|
334
340
|
case "unrecognized_keys":
|
|
@@ -764,19 +770,30 @@ const createAgent = (name, modelName, systemPrompt, tools = [], responseFormat,
|
|
|
764
770
|
* @param onProgress - Optional callback invoked with progress event objects throughout
|
|
765
771
|
* agent execution
|
|
766
772
|
* @param usageTracker - Optional usage accumulator updated on successful completion
|
|
773
|
+
* @param progress - The percentage every progress event reports (the stage's
|
|
774
|
+
* current one; the agent loop has no percentage of its own). Defaults to 0.
|
|
767
775
|
* @returns The raw response object returned by the agent's `invoke` method
|
|
768
776
|
*/
|
|
769
|
-
const runAgent = async (agent, prompt, config, onProgress = null, usageTracker = null) => {
|
|
777
|
+
const runAgent = async (agent, prompt, config, onProgress = null, usageTracker = null, progress = 0) => {
|
|
770
778
|
const startTime = Date.now();
|
|
779
|
+
// Every event carries `progress`: callers forward these to the platform's
|
|
780
|
+
// `{ progress, message }` progress bar, where a missing value would read as
|
|
781
|
+
// 0% and make the bar jump back during a tool call. The agent loop reports
|
|
782
|
+
// no percentage of its own, so the stage's current one is repeated.
|
|
771
783
|
const callbacks = onProgress
|
|
772
784
|
? [
|
|
773
785
|
{
|
|
774
|
-
handleToolStart
|
|
775
|
-
|
|
786
|
+
// LangChain calls handleToolStart(serializedTool, input, runId,
|
|
787
|
+
// parentRunId, tags, metadata, runName): the serialized tool
|
|
788
|
+
// ({ lc, type, id }) has no name; the tool's name is runName.
|
|
789
|
+
handleToolStart: (toolInfo, _input, _runId, _parentRunId, _tags, _metadata, runName) => {
|
|
790
|
+
const name = runName || toolInfo?.name || toolInfo?.kwargs?.name;
|
|
791
|
+
if (name)
|
|
776
792
|
onProgress({
|
|
777
793
|
type: "tool_start",
|
|
778
|
-
message: `Using "${
|
|
779
|
-
tool:
|
|
794
|
+
message: `Using "${name}" tool`,
|
|
795
|
+
tool: name,
|
|
796
|
+
progress,
|
|
780
797
|
});
|
|
781
798
|
},
|
|
782
799
|
handleAgentAction: (action) => {
|
|
@@ -785,12 +802,14 @@ const runAgent = async (agent, prompt, config, onProgress = null, usageTracker =
|
|
|
785
802
|
message: action.log || `Calling "${action.tool}" action`,
|
|
786
803
|
tool: action.tool,
|
|
787
804
|
input: action.toolInput,
|
|
805
|
+
progress,
|
|
788
806
|
});
|
|
789
807
|
},
|
|
790
808
|
handleChainError: (err) => {
|
|
791
809
|
onProgress({
|
|
792
810
|
type: "error",
|
|
793
811
|
message: `Error: ${err?.message || String(err)}`,
|
|
812
|
+
progress,
|
|
794
813
|
});
|
|
795
814
|
},
|
|
796
815
|
},
|
|
@@ -1254,7 +1273,7 @@ const runPromptWithModel = async (modelName, config, prompt, onProgressReport, m
|
|
|
1254
1273
|
let attempt = 0;
|
|
1255
1274
|
for (;;) {
|
|
1256
1275
|
// Run the agent with progress callback
|
|
1257
|
-
const response = await runAgent(agent, activeUserPrompt, config, onProgressReport || null, usageTracker);
|
|
1276
|
+
const response = await runAgent(agent, activeUserPrompt, config, onProgressReport || null, usageTracker, minPercent);
|
|
1258
1277
|
// Extract content from agent response
|
|
1259
1278
|
const messages = response?.messages || [];
|
|
1260
1279
|
if (messages.length === 0) {
|