@stackfactor/agent-utils 1.4.3 → 1.8.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.
Files changed (46) hide show
  1. package/README.md +81 -6
  2. package/dist/cjs/errorHandling.d.ts.map +1 -1
  3. package/dist/cjs/errorHandling.js +10 -5
  4. package/dist/cjs/index.d.ts +8 -2
  5. package/dist/cjs/index.d.ts.map +1 -1
  6. package/dist/cjs/index.js +9 -1
  7. package/dist/cjs/langChain.d.ts +25 -5
  8. package/dist/cjs/langChain.d.ts.map +1 -1
  9. package/dist/cjs/langChain.js +244 -179
  10. package/dist/cjs/runtimeContext.d.ts +3 -0
  11. package/dist/cjs/runtimeContext.d.ts.map +1 -1
  12. package/dist/cjs/serve.d.ts +88 -0
  13. package/dist/cjs/serve.d.ts.map +1 -1
  14. package/dist/cjs/serve.js +230 -3
  15. package/dist/cjs/stackfactorKnowledge.d.ts +182 -0
  16. package/dist/cjs/stackfactorKnowledge.d.ts.map +1 -0
  17. package/dist/cjs/stackfactorKnowledge.js +274 -0
  18. package/dist/cjs/tavily.d.ts +4 -0
  19. package/dist/cjs/tavily.d.ts.map +1 -1
  20. package/dist/cjs/tavily.js +17 -18
  21. package/dist/cjs/usage.d.ts +74 -0
  22. package/dist/cjs/usage.d.ts.map +1 -0
  23. package/dist/cjs/usage.js +160 -0
  24. package/dist/esm/errorHandling.d.ts.map +1 -1
  25. package/dist/esm/errorHandling.js +10 -5
  26. package/dist/esm/index.d.ts +8 -2
  27. package/dist/esm/index.d.ts.map +1 -1
  28. package/dist/esm/index.js +6 -2
  29. package/dist/esm/langChain.d.ts +25 -5
  30. package/dist/esm/langChain.d.ts.map +1 -1
  31. package/dist/esm/langChain.js +244 -179
  32. package/dist/esm/runtimeContext.d.ts +3 -0
  33. package/dist/esm/runtimeContext.d.ts.map +1 -1
  34. package/dist/esm/serve.d.ts +88 -0
  35. package/dist/esm/serve.d.ts.map +1 -1
  36. package/dist/esm/serve.js +226 -3
  37. package/dist/esm/stackfactorKnowledge.d.ts +182 -0
  38. package/dist/esm/stackfactorKnowledge.d.ts.map +1 -0
  39. package/dist/esm/stackfactorKnowledge.js +266 -0
  40. package/dist/esm/tavily.d.ts +4 -0
  41. package/dist/esm/tavily.d.ts.map +1 -1
  42. package/dist/esm/tavily.js +17 -18
  43. package/dist/esm/usage.d.ts +74 -0
  44. package/dist/esm/usage.d.ts.map +1 -0
  45. package/dist/esm/usage.js +150 -0
  46. package/package.json +14 -8
@@ -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
+ };
@@ -86,6 +86,10 @@ declare const _default: {
86
86
  assertTavilyUsable: (config: any, options: TavilyConfig) => void;
87
87
  buildTavilyTools: (config: any, options: TavilyConfig, usageTracker?: UsageTracker | null) => any[];
88
88
  getTavilyOptions: (config: any) => TavilyConfig | null;
89
+ recordTavilyUsage: (tracker: UsageTracker | null | undefined, _config: any, credits: number, sources: {
90
+ url: string;
91
+ title?: string;
92
+ }[]) => void;
89
93
  };
90
94
  export default _default;
91
95
  //# sourceMappingURL=tavily.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tavily.d.ts","sourceRoot":"","sources":["../../src/tavily.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AA6BnD,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;AAqLF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,GAAG,EACX,SAAS,YAAY,EACrB,eAAc,YAAY,GAAG,IAAW,KACvC,GAAG,EA2PL,CAAC;;iCA3dyC,GAAG,WAAW,YAAY,KAAG,IAAI;+BA6NlE,GAAG,WACF,YAAY,iBACP,YAAY,GAAG,IAAI,KAChC,GAAG,EAAE;+BA/OiC,GAAG,KAAG,YAAY,GAAG,IAAI;;AA4elE,wBAIE"}
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;;iCA5dyC,GAAG,WAAW,YAAY,KAAG,IAAI;+BA8NlE,GAAG,WACF,YAAY,iBACP,YAAY,GAAG,IAAI,KAChC,GAAG,EAAE;+BAhPiC,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;;AAmWP,wBAKE"}
@@ -10,9 +10,8 @@ const const_js_1 = __importDefault(require("./const.js"));
10
10
  const errorHandling_js_1 = __importDefault(require("./errorHandling.js"));
11
11
  const logger_js_1 = __importDefault(require("./logger.js"));
12
12
  const runtimeContext_js_1 = require("./runtimeContext.js");
13
+ const usage_js_1 = require("./usage.js");
13
14
  const TAVILY_API_BASE = "https://api.tavily.com";
14
- /** Tavily list price per credit, used when the integration defines no rate. */
15
- const DEFAULT_CREDIT_COST_USD = 0.008;
16
15
  /**
17
16
  * Context budgets, in characters, applied to everything a tool hands back to
18
17
  * the model. Tavily returns whatever the page holds, and unlike a provider's
@@ -129,19 +128,25 @@ const callTavily = async (endpoint, body, apiKey, timeoutSeconds) => {
129
128
  throw errorHandling_js_1.default.create(const_js_1.default.HTTP_CODES.INTERNAL_SERVER_ERROR, `Tavily ${endpoint} returned ${response.status}: ${detail.slice(0, 500)}`);
130
129
  }
131
130
  };
131
+ /** The name Tavily's credits are counted and priced under. */
132
+ const TAVILY_MODEL = "tavily";
132
133
  /**
133
- * Adds one Tavily call's credits to the tracker and records the URLs it
134
- * returned. Credits come from the response's own `usage` block, priced with the
135
- * `tavily-credit-costs` constant (USD per credit) and accumulated under
136
- * `tavilyCredits`. Sources land on `tracker.webSearchSources`, deduplicated by
137
- * URL across the whole run, so callers have the citation list Tavily's terms
138
- * require when this output is shown to end users.
139
- * @param tracker - The caller's usage accumulator; no-ops when absent
140
- * @param config - Config object carrying the `tavily-credit-costs` constant
134
+ * Counts one Tavily call's credits and records the URLs it returned. Credits
135
+ * come from the response's own `usage` block; they are counted into the
136
+ * request's usage ledger, which is reported to the platform, and into the
137
+ * caller's tracker under `tavilyCredits`. Sources land on
138
+ * `tracker.webSearchSources`, deduplicated by URL across the whole run, so
139
+ * callers have the citation list Tavily's terms require when this output is
140
+ * shown to end users.
141
+ * @param tracker - The caller's usage accumulator, when it passed one
142
+ * @param _config - No longer read: it carried the rate, which is the platform's to say
141
143
  * @param credits - Credits reported by the Tavily response
142
144
  * @param sources - URLs (and titles where known) the call returned
143
145
  */
144
- const recordTavilyUsage = (tracker, config, credits, sources) => {
146
+ const recordTavilyUsage = (tracker, _config, credits, sources) => {
147
+ if (Number.isFinite(credits) && credits > 0) {
148
+ (0, usage_js_1.recordUsage)(TAVILY_MODEL, "credits", credits);
149
+ }
145
150
  if (!tracker)
146
151
  return;
147
152
  if (typeof tracker.cost !== "number")
@@ -149,13 +154,6 @@ const recordTavilyUsage = (tracker, config, credits, sources) => {
149
154
  if (!tracker.tokens || typeof tracker.tokens !== "object")
150
155
  tracker.tokens = {};
151
156
  if (Number.isFinite(credits) && credits > 0) {
152
- const configured = Number(config?.["tavily-credit-costs"]);
153
- const rate = Number.isFinite(configured)
154
- ? configured
155
- : DEFAULT_CREDIT_COST_USD;
156
- const addedCost = credits * rate;
157
- if (Number.isFinite(addedCost) && addedCost > 0)
158
- tracker.cost += addedCost;
159
157
  tracker.tokens.tavilyCredits =
160
158
  (tracker.tokens.tavilyCredits || 0) + credits;
161
159
  }
@@ -401,4 +399,5 @@ exports.default = {
401
399
  assertTavilyUsable: exports.assertTavilyUsable,
402
400
  buildTavilyTools: exports.buildTavilyTools,
403
401
  getTavilyOptions: exports.getTavilyOptions,
402
+ recordTavilyUsage,
404
403
  };
@@ -0,0 +1,74 @@
1
+ import { type RequestContext } from "./runtimeContext.js";
2
+ /**
3
+ * What an agent used, and how the platform gets to know.
4
+ *
5
+ * An agent says WHAT it used — a model, a unit, an amount — and the platform
6
+ * says what that costs, who is charged and what it was for. Everything that
7
+ * calls a model through this package counts what the call used into the
8
+ * request's {@link UsageLedger}; {@link serve} and {@link runOnce} report the
9
+ * ledger to the platform while the work runs and when it ends, whether it
10
+ * ended well or not. An agent does nothing for this to happen.
11
+ *
12
+ * An agent that calls a provider itself — its own SDK, an HTTP request —
13
+ * counts what that used with {@link recordUsage}.
14
+ */
15
+ /** What usage is counted in; the units the platform has rates for. */
16
+ export declare const USAGE_UNITS: readonly ["inputTokens", "outputTokens", "imageInputTokens", "imageOutputTokens", "characters", "seconds", "credits"];
17
+ export type UsageUnit = (typeof USAGE_UNITS)[number];
18
+ export interface UsageEntry {
19
+ model: string;
20
+ unit: UsageUnit;
21
+ amount: number;
22
+ }
23
+ /** Sends one report to the platform. Rejects when the platform did not take it. */
24
+ export type ReportUsage = (usage: UsageEntry[], reportId: string, token: string) => Promise<unknown>;
25
+ /** How often work that is still running reports what it used so far. */
26
+ export declare const USAGE_REPORT_INTERVAL_MS = 60000;
27
+ /**
28
+ * What one request used and has not reported yet, summed by model and unit.
29
+ */
30
+ export declare class UsageLedger {
31
+ private pending;
32
+ /** Adds to what is pending. Anything that is not a positive amount of a known unit is ignored. */
33
+ add(model: string, unit: UsageUnit, amount: number): void;
34
+ /** What is pending, which then no longer is. */
35
+ take(): UsageEntry[];
36
+ /** True when there is nothing to report. */
37
+ get isEmpty(): boolean;
38
+ }
39
+ /**
40
+ * Counts what the running request used. For an agent that calls a provider
41
+ * itself; what is called through `langChain` is counted already. Outside a
42
+ * request there is nothing to count into, and nothing happens.
43
+ * @param model - The model's id, as its provider names it
44
+ * @param unit - What the amount is counted in
45
+ * @param amount - How much
46
+ */
47
+ export declare const recordUsage: (model: string, unit: UsageUnit, amount: number) => void;
48
+ /**
49
+ * Reports what the request used and has not reported yet. Never throws, and
50
+ * never changes what the work answers: the work happened, and a platform that
51
+ * cannot be told what it used right now is told by the next report, or not at
52
+ * all — which is logged as an error, with what was used.
53
+ *
54
+ * @param context - The request whose ledger is reported
55
+ * @param options - `report` sends a report (default: the platform's route);
56
+ * `last` says no other report follows, so what cannot be sent is given up on
57
+ * rather than kept for the next one
58
+ */
59
+ export declare const reportUsage: (context: Pick<RequestContext, "usage" | "request"> | undefined, options?: {
60
+ report?: ReportUsage;
61
+ last?: boolean;
62
+ retryBaseMs?: number;
63
+ }) => Promise<void>;
64
+ /**
65
+ * Reports what the request used so far every {@link USAGE_REPORT_INTERVAL_MS}
66
+ * while the work runs, so work that dies has reported most of what it used.
67
+ * @returns Stops it
68
+ */
69
+ export declare const reportUsageWhileRunning: (context: Pick<RequestContext, "usage" | "request">, options?: {
70
+ report?: ReportUsage;
71
+ intervalMs?: number;
72
+ retryBaseMs?: number;
73
+ }) => (() => void);
74
+ //# sourceMappingURL=usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/usage.ts"],"names":[],"mappings":"AAGA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAEtE;;;;;;;;;;;;GAYG;AAEH,sEAAsE;AACtE,eAAO,MAAM,WAAW,uHAQd,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,mFAAmF;AACnF,MAAM,MAAM,WAAW,GAAG,CACxB,KAAK,EAAE,UAAU,EAAE,EACnB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,KACV,OAAO,CAAC,OAAO,CAAC,CAAC;AAOtB,wEAAwE;AACxE,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAI/C;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAiC;IAEhD,kGAAkG;IAClG,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAkBzD,gDAAgD;IAChD,IAAI,IAAI,UAAU,EAAE;IAMpB,4CAA4C;IAC5C,IAAI,OAAO,IAAI,OAAO,CAErB;CACF;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,EAAE,MAAM,SAAS,EAAE,QAAQ,MAAM,KAAG,IAE5E,CAAC;AA8BF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,WAAW,GACtB,SAAS,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,SAAS,CAAC,GAAG,SAAS,EAC9D,UAAS;IAAE,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,KAC3E,OAAO,CAAC,IAAI,CAuCd,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,GAClC,SAAS,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,SAAS,CAAC,EAClD,UAAS;IAAE,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,KAChF,CAAC,MAAM,IAAI,CAMb,CAAC"}
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.reportUsageWhileRunning = exports.reportUsage = exports.recordUsage = exports.UsageLedger = exports.USAGE_REPORT_INTERVAL_MS = exports.USAGE_UNITS = void 0;
7
+ const node_crypto_1 = require("node:crypto");
8
+ const client_api_1 = require("@stackfactor/client-api");
9
+ const logger_js_1 = __importDefault(require("./logger.js"));
10
+ const runtimeContext_js_1 = require("./runtimeContext.js");
11
+ /**
12
+ * What an agent used, and how the platform gets to know.
13
+ *
14
+ * An agent says WHAT it used — a model, a unit, an amount — and the platform
15
+ * says what that costs, who is charged and what it was for. Everything that
16
+ * calls a model through this package counts what the call used into the
17
+ * request's {@link UsageLedger}; {@link serve} and {@link runOnce} report the
18
+ * ledger to the platform while the work runs and when it ends, whether it
19
+ * ended well or not. An agent does nothing for this to happen.
20
+ *
21
+ * An agent that calls a provider itself — its own SDK, an HTTP request —
22
+ * counts what that used with {@link recordUsage}.
23
+ */
24
+ /** What usage is counted in; the units the platform has rates for. */
25
+ exports.USAGE_UNITS = [
26
+ "inputTokens",
27
+ "outputTokens",
28
+ "imageInputTokens",
29
+ "imageOutputTokens",
30
+ "characters",
31
+ "seconds",
32
+ "credits",
33
+ ];
34
+ /** Entries one report may carry (the platform's limit). */
35
+ const REPORT_ENTRIES_MAX = 100;
36
+ /** How often a report is tried before it is given up on, and the first wait between tries. */
37
+ const REPORT_ATTEMPTS = 3;
38
+ const REPORT_RETRY_BASE_MS = 500;
39
+ /** How often work that is still running reports what it used so far. */
40
+ exports.USAGE_REPORT_INTERVAL_MS = 60_000;
41
+ const keyOf = (model, unit) => `${model}\n${unit}`;
42
+ /**
43
+ * What one request used and has not reported yet, summed by model and unit.
44
+ */
45
+ class UsageLedger {
46
+ pending = new Map();
47
+ /** Adds to what is pending. Anything that is not a positive amount of a known unit is ignored. */
48
+ add(model, unit, amount) {
49
+ if (typeof model !== "string" ||
50
+ model.trim().length === 0 ||
51
+ !exports.USAGE_UNITS.includes(unit) ||
52
+ typeof amount !== "number" ||
53
+ !Number.isFinite(amount) ||
54
+ amount <= 0) {
55
+ return;
56
+ }
57
+ const name = model.trim();
58
+ const key = keyOf(name, unit);
59
+ const entry = this.pending.get(key);
60
+ if (entry)
61
+ entry.amount += amount;
62
+ else
63
+ this.pending.set(key, { model: name, unit, amount });
64
+ }
65
+ /** What is pending, which then no longer is. */
66
+ take() {
67
+ const entries = [...this.pending.values()];
68
+ this.pending = new Map();
69
+ return entries;
70
+ }
71
+ /** True when there is nothing to report. */
72
+ get isEmpty() {
73
+ return this.pending.size === 0;
74
+ }
75
+ }
76
+ exports.UsageLedger = UsageLedger;
77
+ /**
78
+ * Counts what the running request used. For an agent that calls a provider
79
+ * itself; what is called through `langChain` is counted already. Outside a
80
+ * request there is nothing to count into, and nothing happens.
81
+ * @param model - The model's id, as its provider names it
82
+ * @param unit - What the amount is counted in
83
+ * @param amount - How much
84
+ */
85
+ const recordUsage = (model, unit, amount) => {
86
+ (0, runtimeContext_js_1.getContext)()?.usage?.add(model, unit, amount);
87
+ };
88
+ exports.recordUsage = recordUsage;
89
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
90
+ const defaultReport = (usage, reportId, token) => client_api_1.integration.reportRunUsage(usage, reportId, token);
91
+ /**
92
+ * Sends one report, again when it fails, under the same name every time: the
93
+ * platform charges a report it has seen only once.
94
+ */
95
+ const send = async (entries, token, report, retryBaseMs) => {
96
+ const reportId = (0, node_crypto_1.randomUUID)();
97
+ for (let attempt = 1;; attempt++) {
98
+ try {
99
+ await report(entries, reportId, token);
100
+ return;
101
+ }
102
+ catch (error) {
103
+ if (attempt >= REPORT_ATTEMPTS)
104
+ throw error;
105
+ await delay(retryBaseMs * 2 ** (attempt - 1));
106
+ }
107
+ }
108
+ };
109
+ /**
110
+ * Reports what the request used and has not reported yet. Never throws, and
111
+ * never changes what the work answers: the work happened, and a platform that
112
+ * cannot be told what it used right now is told by the next report, or not at
113
+ * all — which is logged as an error, with what was used.
114
+ *
115
+ * @param context - The request whose ledger is reported
116
+ * @param options - `report` sends a report (default: the platform's route);
117
+ * `last` says no other report follows, so what cannot be sent is given up on
118
+ * rather than kept for the next one
119
+ */
120
+ const reportUsage = async (context, options = {}) => {
121
+ const ledger = context?.usage;
122
+ if (!ledger || ledger.isEmpty)
123
+ return;
124
+ const token = context.request?.authToken ?? context.request?.authorization;
125
+ const entries = ledger.take();
126
+ if (!token) {
127
+ logger_js_1.default.log(null, logger_js_1.default.levels.error, `usage was not reported, the request carries no token: ${JSON.stringify(entries)}`);
128
+ return;
129
+ }
130
+ for (let start = 0; start < entries.length; start += REPORT_ENTRIES_MAX) {
131
+ const chunk = entries.slice(start, start + REPORT_ENTRIES_MAX);
132
+ try {
133
+ await send(chunk, token, options.report ?? defaultReport, options.retryBaseMs ?? REPORT_RETRY_BASE_MS);
134
+ }
135
+ catch (error) {
136
+ if (options.last) {
137
+ logger_js_1.default.log(null, logger_js_1.default.levels.error, `usage could not be reported (${error?.message ?? error}): ${JSON.stringify(chunk)}`);
138
+ }
139
+ else {
140
+ for (const entry of chunk)
141
+ ledger.add(entry.model, entry.unit, entry.amount);
142
+ logger_js_1.default.log(null, logger_js_1.default.levels.warn, `usage could not be reported, it is kept for the next report: ${error?.message ?? error}`);
143
+ }
144
+ }
145
+ }
146
+ };
147
+ exports.reportUsage = reportUsage;
148
+ /**
149
+ * Reports what the request used so far every {@link USAGE_REPORT_INTERVAL_MS}
150
+ * while the work runs, so work that dies has reported most of what it used.
151
+ * @returns Stops it
152
+ */
153
+ const reportUsageWhileRunning = (context, options = {}) => {
154
+ const timer = setInterval(() => {
155
+ void (0, exports.reportUsage)(context, { report: options.report, retryBaseMs: options.retryBaseMs });
156
+ }, options.intervalMs ?? exports.USAGE_REPORT_INTERVAL_MS);
157
+ timer.unref?.();
158
+ return () => clearInterval(timer);
159
+ };
160
+ exports.reportUsageWhileRunning = reportUsageWhileRunning;
@@ -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;AA8GD;;;;;;;;;;;;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;mBApOpD,GAAG,KAAG,WAAW;IAsO1C,0EAA0E;;IAE1E,gFAAgF;oBA9BzE,OAAO,YACL,GAAG,YACH,GAAG,KACX,QAAQ;IA6BT,wDAAwD;uBArPhC,GAAG,KAAG,IAAI,IAAI,MAAM;IAuP5C,kCAAkC;;;AA7BpC,wBA+BE"}
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
- return {
85
- code: isHttpCode(parsed.code) ? parsed.code : undefined,
86
- message: parsed.message,
87
- integration: parsed.integration,
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 };
@@ -2,9 +2,11 @@ import constants from "./const.js";
2
2
  import errorHandling, { AppError } from "./errorHandling.js";
3
3
  import langChain from "./langChain.js";
4
4
  import logger from "./logger.js";
5
- import { serve } from "./serve.js";
5
+ 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
+ import { recordUsage, USAGE_UNITS } from "./usage.js";
9
+ import stackfactorKnowledge from "./stackfactorKnowledge.js";
8
10
  export { constants };
9
11
  export { errorHandling, AppError };
10
12
  export type { ParsedError } from "./errorHandling.js";
@@ -12,9 +14,13 @@ export { langChain };
12
14
  export type { UsageTracker } from "./langChain.js";
13
15
  export type { TavilyConfig, TavilyToolName } from "./tavily.js";
14
16
  export { logger };
15
- export { serve };
17
+ export { serve, runOnce, isRunOnceExecution, EXECUTION_ENV };
16
18
  export { callAgent };
17
19
  export { checkAgent };
18
20
  export type { AgentExecuteRequest, CallAgentOptions, CheckAgentOptions, AgentHealth, } from "./client.js";
19
21
  export { runtimeContext };
22
+ export { recordUsage, USAGE_UNITS };
23
+ export type { UsageEntry, UsageUnit } from "./usage.js";
24
+ export { stackfactorKnowledge };
25
+ export type { GroundingPassage, KnowledgeChunk, KnowledgeSearch, KnowledgeSource, SkippedSource, StackFactorReference, } from "./stackfactorKnowledge.js";
20
26
  //# sourceMappingURL=index.d.ts.map