@quelvio/langchain 0.1.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/CHANGELOG.md +38 -0
- package/LICENSE +21 -0
- package/README.md +299 -0
- package/dist/index.cjs +594 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +789 -0
- package/dist/index.d.ts +789 -0
- package/dist/index.js +564 -0
- package/dist/index.js.map +1 -0
- package/package.json +68 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { Document } from '@langchain/core/documents';
|
|
3
|
+
import { BaseRetriever } from '@langchain/core/retrievers';
|
|
4
|
+
import { StructuredTool } from '@langchain/core/tools';
|
|
5
|
+
|
|
6
|
+
// src/exceptions.ts
|
|
7
|
+
var QuelvioError = class extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "QuelvioError";
|
|
11
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var QuelvioAuthError = class extends QuelvioError {
|
|
15
|
+
constructor(message) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "QuelvioAuthError";
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var QuelvioBadRequestError = class extends QuelvioError {
|
|
21
|
+
constructor(message) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = "QuelvioBadRequestError";
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var QuelvioNotFoundError = class extends QuelvioError {
|
|
27
|
+
constructor(message) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = "QuelvioNotFoundError";
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var QuelvioRateLimitError = class extends QuelvioError {
|
|
33
|
+
/** Value of the `Retry-After` header if present; otherwise `null`. */
|
|
34
|
+
retryAfterSeconds;
|
|
35
|
+
constructor(message, retryAfterSeconds = null) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = "QuelvioRateLimitError";
|
|
38
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var QuelvioServerError = class extends QuelvioError {
|
|
42
|
+
statusCode;
|
|
43
|
+
constructor(message, statusCode) {
|
|
44
|
+
super(message);
|
|
45
|
+
this.name = "QuelvioServerError";
|
|
46
|
+
this.statusCode = statusCode;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var QuelvioTimeoutError = class extends QuelvioError {
|
|
50
|
+
constructor(message) {
|
|
51
|
+
super(message);
|
|
52
|
+
this.name = "QuelvioTimeoutError";
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var QuelvioNetworkError = class extends QuelvioError {
|
|
56
|
+
constructor(message) {
|
|
57
|
+
super(message);
|
|
58
|
+
this.name = "QuelvioNetworkError";
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var QueryModeSchema = z.enum(["fast", "standard", "deep"]);
|
|
62
|
+
var QueryRequestSchema = z.object({
|
|
63
|
+
query: z.string(),
|
|
64
|
+
limit: z.number().int().min(1).max(50).default(5),
|
|
65
|
+
mode: QueryModeSchema.default("standard"),
|
|
66
|
+
domain_filter: z.string().nullable().optional()
|
|
67
|
+
});
|
|
68
|
+
var ChunkResultSchema = z.object({
|
|
69
|
+
chunk_id: z.string(),
|
|
70
|
+
content_piece_id: z.string(),
|
|
71
|
+
title: z.string(),
|
|
72
|
+
excerpt: z.string(),
|
|
73
|
+
score: z.number(),
|
|
74
|
+
rank: z.number().int(),
|
|
75
|
+
authority_score: z.number().nullable().optional(),
|
|
76
|
+
taxonomy_domain: z.string().nullable().optional(),
|
|
77
|
+
source_url: z.string().nullable().optional(),
|
|
78
|
+
creator_id: z.string().nullable().optional(),
|
|
79
|
+
author_name: z.string().nullable().optional(),
|
|
80
|
+
author_email: z.string().nullable().optional(),
|
|
81
|
+
department: z.string().nullable().optional()
|
|
82
|
+
}).passthrough();
|
|
83
|
+
var QueryResponseSchema = z.object({
|
|
84
|
+
query: z.string(),
|
|
85
|
+
query_id: z.string(),
|
|
86
|
+
results: z.array(ChunkResultSchema).default([]),
|
|
87
|
+
result_count: z.number().int().default(0),
|
|
88
|
+
coverage: z.string().nullable().optional(),
|
|
89
|
+
risk_flag: z.record(z.boolean()).default({}),
|
|
90
|
+
retrieval_mode: z.string().nullable().optional(),
|
|
91
|
+
synthesis: z.string().nullable().optional(),
|
|
92
|
+
synthesis_model: z.string().nullable().optional(),
|
|
93
|
+
latency_ms: z.number().int().nullable().optional(),
|
|
94
|
+
tokens_consumed: z.number().int().nullable().optional()
|
|
95
|
+
}).passthrough();
|
|
96
|
+
var DomainCoverageSchema = z.object({
|
|
97
|
+
taxonomy_domain: z.string(),
|
|
98
|
+
document_count: z.number().int().default(0),
|
|
99
|
+
chunk_count: z.number().int().default(0),
|
|
100
|
+
expert_count: z.number().int().default(0),
|
|
101
|
+
coverage_level: z.string().nullable().optional()
|
|
102
|
+
}).passthrough();
|
|
103
|
+
var DomainsListResponseSchema = z.object({
|
|
104
|
+
domains: z.array(DomainCoverageSchema).default([]),
|
|
105
|
+
total: z.number().int().default(0)
|
|
106
|
+
}).passthrough();
|
|
107
|
+
var SourceChunkSchema = z.object({
|
|
108
|
+
chunk_id: z.string(),
|
|
109
|
+
content_piece_id: z.string(),
|
|
110
|
+
title: z.string(),
|
|
111
|
+
excerpt: z.string(),
|
|
112
|
+
source_url: z.string().nullable().optional(),
|
|
113
|
+
source_type: z.string().nullable().optional(),
|
|
114
|
+
lifecycle_state: z.string().nullable().optional(),
|
|
115
|
+
embedded_at: z.string().nullable().optional(),
|
|
116
|
+
last_source_updated_at: z.string().nullable().optional(),
|
|
117
|
+
authority_score: z.number().nullable().optional(),
|
|
118
|
+
taxonomy_domain: z.string().nullable().optional(),
|
|
119
|
+
author_name: z.string().nullable().optional(),
|
|
120
|
+
author_email: z.string().nullable().optional()
|
|
121
|
+
}).passthrough();
|
|
122
|
+
var SourceDetailResponseSchema = z.object({
|
|
123
|
+
query_id: z.string(),
|
|
124
|
+
tenant_id: z.string().nullable().optional(),
|
|
125
|
+
chunks: z.array(SourceChunkSchema).default([]),
|
|
126
|
+
chunk_count: z.number().int().default(0)
|
|
127
|
+
}).passthrough();
|
|
128
|
+
|
|
129
|
+
// src/version.ts
|
|
130
|
+
var VERSION = "0.1.0";
|
|
131
|
+
|
|
132
|
+
// src/client.ts
|
|
133
|
+
var DEFAULT_BASE_URL = "https://api.quelvio.com";
|
|
134
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
135
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
136
|
+
var BASE_BACKOFF_MS = 1e3;
|
|
137
|
+
var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([502, 503, 504]);
|
|
138
|
+
var VALID_MODES = /* @__PURE__ */ new Set(["fast", "standard", "deep"]);
|
|
139
|
+
function resolveApiKey(explicit) {
|
|
140
|
+
if (explicit) return explicit;
|
|
141
|
+
const fromEnv = process.env.QUELVIO_API_KEY;
|
|
142
|
+
if (fromEnv) return fromEnv;
|
|
143
|
+
throw new QuelvioAuthError(
|
|
144
|
+
"No Quelvio API key was provided. Pass `apiKey` to the constructor or set the QUELVIO_API_KEY environment variable. Generate a Personal Access Token at https://enterprise.quelvio.com/account."
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
function resolveBaseUrl(explicit) {
|
|
148
|
+
const candidate = explicit ?? process.env.QUELVIO_API_BASE ?? DEFAULT_BASE_URL;
|
|
149
|
+
return candidate.replace(/\/+$/, "");
|
|
150
|
+
}
|
|
151
|
+
function buildUserAgent() {
|
|
152
|
+
return `@quelvio/langchain/${VERSION} node/${process.version} ${process.platform}-${process.arch}`;
|
|
153
|
+
}
|
|
154
|
+
function normalizeMode(mode) {
|
|
155
|
+
if (mode === void 0 || mode === null) return "standard";
|
|
156
|
+
const lowered = String(mode).trim().toLowerCase();
|
|
157
|
+
if (!VALID_MODES.has(lowered)) {
|
|
158
|
+
throw new QuelvioBadRequestError(
|
|
159
|
+
`Invalid mode ${JSON.stringify(mode)}. Expected one of: fast, standard, deep.`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
return lowered;
|
|
163
|
+
}
|
|
164
|
+
function boundLimit(value) {
|
|
165
|
+
if (value === void 0 || value === null) return 5;
|
|
166
|
+
if (value < 1) return 1;
|
|
167
|
+
if (value > 50) return 50;
|
|
168
|
+
return Math.floor(value);
|
|
169
|
+
}
|
|
170
|
+
function buildQueryBody(opts) {
|
|
171
|
+
const body = {
|
|
172
|
+
query: opts.query,
|
|
173
|
+
limit: boundLimit(opts.limit),
|
|
174
|
+
mode: normalizeMode(opts.mode)
|
|
175
|
+
};
|
|
176
|
+
if (opts.domainFilter !== void 0 && opts.domainFilter !== null) {
|
|
177
|
+
body.domain_filter = opts.domainFilter;
|
|
178
|
+
}
|
|
179
|
+
return body;
|
|
180
|
+
}
|
|
181
|
+
function backoffMs(attempt, rand) {
|
|
182
|
+
const base = BASE_BACKOFF_MS * 2 ** attempt;
|
|
183
|
+
const jitter = 1 + (rand * 0.4 - 0.2);
|
|
184
|
+
return Math.round(base * jitter);
|
|
185
|
+
}
|
|
186
|
+
function sleep(ms) {
|
|
187
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
188
|
+
}
|
|
189
|
+
function parseRetryAfter(value) {
|
|
190
|
+
if (!value) return null;
|
|
191
|
+
const n = Number.parseInt(value, 10);
|
|
192
|
+
return Number.isFinite(n) ? n : null;
|
|
193
|
+
}
|
|
194
|
+
async function extractDetail(response) {
|
|
195
|
+
try {
|
|
196
|
+
const clone = response.clone();
|
|
197
|
+
const payload = await clone.json();
|
|
198
|
+
if (payload && typeof payload === "object") {
|
|
199
|
+
const obj = payload;
|
|
200
|
+
const detail = obj.detail ?? obj.message;
|
|
201
|
+
if (typeof detail === "string") return detail;
|
|
202
|
+
}
|
|
203
|
+
} catch {
|
|
204
|
+
try {
|
|
205
|
+
const text = (await response.clone().text()).trim();
|
|
206
|
+
return text ? text.slice(0, 500) : null;
|
|
207
|
+
} catch {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
async function mapError(response) {
|
|
214
|
+
const detail = await extractDetail(response);
|
|
215
|
+
const status = response.status;
|
|
216
|
+
const reason = response.statusText || "";
|
|
217
|
+
if (status === 400) {
|
|
218
|
+
return new QuelvioBadRequestError(detail ?? `Bad request: ${reason}`);
|
|
219
|
+
}
|
|
220
|
+
if (status === 401 || status === 403) {
|
|
221
|
+
return new QuelvioAuthError(
|
|
222
|
+
"Quelvio authentication failed. Your token may be invalid, expired, or revoked. Generate a new Personal Access Token at https://enterprise.quelvio.com/account."
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
if (status === 404) {
|
|
226
|
+
return new QuelvioNotFoundError(detail ?? `Not found: ${reason}`);
|
|
227
|
+
}
|
|
228
|
+
if (status === 429) {
|
|
229
|
+
const retryAfter = parseRetryAfter(response.headers.get("retry-after"));
|
|
230
|
+
const suffix = retryAfter !== null ? ` (retry after ${retryAfter}s)` : "";
|
|
231
|
+
return new QuelvioRateLimitError(`Quelvio rate limit exceeded.${suffix}`, retryAfter);
|
|
232
|
+
}
|
|
233
|
+
if (status >= 500 && status < 600) {
|
|
234
|
+
return new QuelvioServerError(`Quelvio server error: ${status} ${reason}`, status);
|
|
235
|
+
}
|
|
236
|
+
return new QuelvioError(`Unexpected response: ${status} ${reason}`);
|
|
237
|
+
}
|
|
238
|
+
var QuelvioClient = class {
|
|
239
|
+
baseUrl;
|
|
240
|
+
timeoutMs;
|
|
241
|
+
maxRetries;
|
|
242
|
+
// Held in a closure so it is invisible to `toString`, `JSON.stringify`,
|
|
243
|
+
// and any introspection. There is no field on the instance.
|
|
244
|
+
#buildHeaders;
|
|
245
|
+
#fetch;
|
|
246
|
+
#source;
|
|
247
|
+
#apiKey;
|
|
248
|
+
constructor(options = {}) {
|
|
249
|
+
this.#apiKey = resolveApiKey(options.apiKey);
|
|
250
|
+
this.baseUrl = resolveBaseUrl(options.baseUrl);
|
|
251
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
252
|
+
this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
253
|
+
this.#source = options.source ?? "langchain-js-client";
|
|
254
|
+
this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
255
|
+
const apiKey = this.#apiKey;
|
|
256
|
+
const source = this.#source;
|
|
257
|
+
this.#buildHeaders = () => {
|
|
258
|
+
const h = new Headers();
|
|
259
|
+
h.set("Authorization", `Bearer ${apiKey}`);
|
|
260
|
+
h.set("Accept", "application/json");
|
|
261
|
+
h.set("Content-Type", "application/json");
|
|
262
|
+
h.set("User-Agent", buildUserAgent());
|
|
263
|
+
h.set("X-Quelvio-Source", "langchain-js");
|
|
264
|
+
h.set("X-Quelvio-Command", source);
|
|
265
|
+
return h;
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Don't surface the bearer token from string coercion or JSON
|
|
270
|
+
* serialization. We deliberately list non-sensitive fields only.
|
|
271
|
+
*/
|
|
272
|
+
toString() {
|
|
273
|
+
return `QuelvioClient(baseUrl=${this.baseUrl}, timeoutMs=${this.timeoutMs}, maxRetries=${this.maxRetries})`;
|
|
274
|
+
}
|
|
275
|
+
toJSON() {
|
|
276
|
+
return {
|
|
277
|
+
baseUrl: this.baseUrl,
|
|
278
|
+
timeoutMs: this.timeoutMs,
|
|
279
|
+
maxRetries: this.maxRetries
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
/** Internal accessor used by sibling classes that share the same client. */
|
|
283
|
+
_exposeApiKeyForSibling() {
|
|
284
|
+
return this.#apiKey;
|
|
285
|
+
}
|
|
286
|
+
/** Internal accessor — returns the configured `fetch` for sibling reuse. */
|
|
287
|
+
_exposeFetchForSibling() {
|
|
288
|
+
return this.#fetch;
|
|
289
|
+
}
|
|
290
|
+
/** Send a one-shot query and return the parsed response. */
|
|
291
|
+
async query(opts) {
|
|
292
|
+
if (!opts.query || !opts.query.trim()) {
|
|
293
|
+
throw new TypeError("query must be a non-empty string");
|
|
294
|
+
}
|
|
295
|
+
const body = buildQueryBody(opts);
|
|
296
|
+
const raw = await this.#request({ method: "POST", path: "/v1/enterprise/query", body });
|
|
297
|
+
return QueryResponseSchema.parse(raw);
|
|
298
|
+
}
|
|
299
|
+
async listDomains(opts = {}) {
|
|
300
|
+
const params = opts.coverage ? { coverage: opts.coverage } : void 0;
|
|
301
|
+
const raw = await this.#request({ method: "GET", path: "/v1/enterprise/domains", params });
|
|
302
|
+
return DomainsListResponseSchema.parse(raw);
|
|
303
|
+
}
|
|
304
|
+
async getSourceDetail(queryId) {
|
|
305
|
+
if (!queryId) {
|
|
306
|
+
throw new TypeError("queryId must be a non-empty string");
|
|
307
|
+
}
|
|
308
|
+
const raw = await this.#request({
|
|
309
|
+
method: "GET",
|
|
310
|
+
path: `/v1/enterprise/sources/${encodeURIComponent(queryId)}`
|
|
311
|
+
});
|
|
312
|
+
return SourceDetailResponseSchema.parse(raw);
|
|
313
|
+
}
|
|
314
|
+
async #request(opts) {
|
|
315
|
+
const url = this.#buildUrl(opts.path, opts.params);
|
|
316
|
+
let lastError = null;
|
|
317
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
318
|
+
const controller = new AbortController();
|
|
319
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
320
|
+
let response;
|
|
321
|
+
try {
|
|
322
|
+
response = await this.#fetch(url, {
|
|
323
|
+
method: opts.method,
|
|
324
|
+
headers: this.#buildHeaders(),
|
|
325
|
+
body: opts.body === void 0 ? void 0 : JSON.stringify(opts.body),
|
|
326
|
+
signal: controller.signal
|
|
327
|
+
});
|
|
328
|
+
} catch (err) {
|
|
329
|
+
lastError = err;
|
|
330
|
+
clearTimeout(timer);
|
|
331
|
+
const isAbort = err instanceof Error && err.name === "AbortError" || typeof err === "object" && err !== null && err.name === "AbortError";
|
|
332
|
+
if (isAbort) {
|
|
333
|
+
if (attempt < this.maxRetries) {
|
|
334
|
+
await sleep(backoffMs(attempt, Math.random()));
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
throw new QuelvioTimeoutError(
|
|
338
|
+
`Request to ${opts.path} timed out after ${this.timeoutMs}ms`
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
if (attempt < this.maxRetries) {
|
|
342
|
+
await sleep(backoffMs(attempt, Math.random()));
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
const name = err instanceof Error ? err.constructor.name : typeof err;
|
|
346
|
+
throw new QuelvioNetworkError(`Network error contacting Quelvio: ${name}`);
|
|
347
|
+
} finally {
|
|
348
|
+
clearTimeout(timer);
|
|
349
|
+
}
|
|
350
|
+
if (response.ok) {
|
|
351
|
+
if (response.status === 204) return null;
|
|
352
|
+
const text = await response.text();
|
|
353
|
+
if (!text) return null;
|
|
354
|
+
return JSON.parse(text);
|
|
355
|
+
}
|
|
356
|
+
if (RETRYABLE_STATUSES.has(response.status) && attempt < this.maxRetries) {
|
|
357
|
+
await sleep(backoffMs(attempt, Math.random()));
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
throw await mapError(response);
|
|
361
|
+
}
|
|
362
|
+
throw new QuelvioError(
|
|
363
|
+
`Request failed after ${this.maxRetries + 1} attempts${lastError instanceof Error ? `: ${lastError.message}` : ""}`
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
#buildUrl(path, params) {
|
|
367
|
+
const normalized = path.startsWith("/") ? path : `/${path}`;
|
|
368
|
+
if (!params) return `${this.baseUrl}${normalized}`;
|
|
369
|
+
const search = new URLSearchParams();
|
|
370
|
+
for (const [k, v] of Object.entries(params)) {
|
|
371
|
+
if (v !== void 0) search.set(k, v);
|
|
372
|
+
}
|
|
373
|
+
const qs = search.toString();
|
|
374
|
+
return qs ? `${this.baseUrl}${normalized}?${qs}` : `${this.baseUrl}${normalized}`;
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
function chunkToDocument(chunk) {
|
|
378
|
+
const metadata = {
|
|
379
|
+
chunk_id: chunk.chunk_id,
|
|
380
|
+
content_piece_id: chunk.content_piece_id,
|
|
381
|
+
title: chunk.title,
|
|
382
|
+
score: chunk.score,
|
|
383
|
+
rank: chunk.rank
|
|
384
|
+
};
|
|
385
|
+
if (chunk.authority_score !== void 0 && chunk.authority_score !== null) {
|
|
386
|
+
metadata.authority_score = chunk.authority_score;
|
|
387
|
+
}
|
|
388
|
+
if (chunk.taxonomy_domain) metadata.taxonomy_domain = chunk.taxonomy_domain;
|
|
389
|
+
if (chunk.source_url) {
|
|
390
|
+
metadata.source_url = chunk.source_url;
|
|
391
|
+
metadata.source = chunk.source_url;
|
|
392
|
+
}
|
|
393
|
+
if (chunk.author_name) metadata.author_name = chunk.author_name;
|
|
394
|
+
if (chunk.author_email) metadata.author_email = chunk.author_email;
|
|
395
|
+
if (chunk.department) metadata.department = chunk.department;
|
|
396
|
+
return new Document({ pageContent: chunk.excerpt, metadata });
|
|
397
|
+
}
|
|
398
|
+
function responseToDocuments(response) {
|
|
399
|
+
const docs = response.results.map(chunkToDocument);
|
|
400
|
+
if (docs.length > 0) {
|
|
401
|
+
docs[0].metadata.query_id = response.query_id;
|
|
402
|
+
}
|
|
403
|
+
return docs;
|
|
404
|
+
}
|
|
405
|
+
var QuelvioRetriever = class extends BaseRetriever {
|
|
406
|
+
static lc_name() {
|
|
407
|
+
return "QuelvioRetriever";
|
|
408
|
+
}
|
|
409
|
+
lc_namespace = ["quelvio", "retrievers"];
|
|
410
|
+
limit;
|
|
411
|
+
mode;
|
|
412
|
+
domainFilter;
|
|
413
|
+
#client;
|
|
414
|
+
constructor(options = {}) {
|
|
415
|
+
super(options);
|
|
416
|
+
this.limit = options.limit ?? 5;
|
|
417
|
+
this.mode = options.mode ?? "standard";
|
|
418
|
+
this.domainFilter = options.domainFilter ?? null;
|
|
419
|
+
if (options.client) {
|
|
420
|
+
this.#client = options.client;
|
|
421
|
+
} else {
|
|
422
|
+
const clientOpts = { source: "langchain-js-retriever" };
|
|
423
|
+
if (options.apiKey !== void 0) clientOpts.apiKey = options.apiKey;
|
|
424
|
+
if (options.baseUrl !== void 0) clientOpts.baseUrl = options.baseUrl;
|
|
425
|
+
if (options.timeoutMs !== void 0) clientOpts.timeoutMs = options.timeoutMs;
|
|
426
|
+
if (options.maxRetries !== void 0) clientOpts.maxRetries = options.maxRetries;
|
|
427
|
+
if (options.fetch !== void 0) clientOpts.fetch = options.fetch;
|
|
428
|
+
this.#client = new QuelvioClient(clientOpts);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
toString() {
|
|
432
|
+
return `QuelvioRetriever(limit=${this.limit}, mode=${this.mode}, domainFilter=${this.domainFilter ?? "null"})`;
|
|
433
|
+
}
|
|
434
|
+
async _getRelevantDocuments(query, _runManager) {
|
|
435
|
+
if (!query || !query.trim()) {
|
|
436
|
+
throw new TypeError("query must be a non-empty string");
|
|
437
|
+
}
|
|
438
|
+
const response = await this.#client.query({
|
|
439
|
+
query,
|
|
440
|
+
limit: this.limit,
|
|
441
|
+
mode: this.mode,
|
|
442
|
+
domainFilter: this.domainFilter
|
|
443
|
+
});
|
|
444
|
+
return responseToDocuments(response);
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
// src/synthesis.ts
|
|
449
|
+
function fromResponse(response) {
|
|
450
|
+
return {
|
|
451
|
+
answer: response.synthesis ?? null,
|
|
452
|
+
sources: [...response.results],
|
|
453
|
+
queryId: response.query_id
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
async function synthesizeAnswer(question, options = {}) {
|
|
457
|
+
if (!question || !question.trim()) {
|
|
458
|
+
throw new TypeError("question must be a non-empty string");
|
|
459
|
+
}
|
|
460
|
+
let client = options.client;
|
|
461
|
+
if (!client) {
|
|
462
|
+
const clientOpts = { source: "langchain-js-synthesis" };
|
|
463
|
+
if (options.apiKey !== void 0) clientOpts.apiKey = options.apiKey;
|
|
464
|
+
if (options.baseUrl !== void 0) clientOpts.baseUrl = options.baseUrl;
|
|
465
|
+
if (options.timeoutMs !== void 0) clientOpts.timeoutMs = options.timeoutMs;
|
|
466
|
+
if (options.maxRetries !== void 0) clientOpts.maxRetries = options.maxRetries;
|
|
467
|
+
if (options.fetch !== void 0) clientOpts.fetch = options.fetch;
|
|
468
|
+
client = new QuelvioClient(clientOpts);
|
|
469
|
+
}
|
|
470
|
+
const response = await client.query({
|
|
471
|
+
query: question,
|
|
472
|
+
limit: options.maxSources ?? 5,
|
|
473
|
+
mode: options.mode ?? "standard",
|
|
474
|
+
domainFilter: options.domainFilter ?? null
|
|
475
|
+
});
|
|
476
|
+
return fromResponse(response);
|
|
477
|
+
}
|
|
478
|
+
var asynthesizeAnswer = synthesizeAnswer;
|
|
479
|
+
var DEFAULT_DESCRIPTION = "Search the organization's connected knowledge brain (Google Drive, SharePoint, Confluence, Slack, Notion, and other internal sources) for an authoritative, cited answer. Use this whenever the user asks about internal company information \u2014 policies, processes, decisions, people, products, projects, or anything else that lives in the company's systems rather than on the public internet. The answer is scoped to the running user's individual access permissions, so results never include documents they cannot already see. Returns a synthesized answer plus a list of cited sources (titles + URLs).";
|
|
480
|
+
var QuelvioToolInputSchema = z.object({
|
|
481
|
+
question: z.string().min(1).describe(
|
|
482
|
+
"The natural-language question to ask the company's knowledge brain. Phrase it as the user would ask it \u2014 do not pre-process or keyword-extract."
|
|
483
|
+
),
|
|
484
|
+
mode: z.enum(["fast", "standard", "deep"]).optional().describe(
|
|
485
|
+
"Synthesis depth: 'fast' for low-latency retrieval-only, 'standard' (default) for retrieval + synthesis, 'deep' for multi-pass reasoning over a wider window."
|
|
486
|
+
),
|
|
487
|
+
max_sources: z.number().int().min(1).max(50).optional().describe("Maximum number of source chunks to retrieve (1 to 50, default 5)."),
|
|
488
|
+
domain: z.string().optional().describe(
|
|
489
|
+
"Optional taxonomy domain to restrict retrieval to (e.g. 'engineering', 'legal', 'people-ops')."
|
|
490
|
+
)
|
|
491
|
+
});
|
|
492
|
+
function formatResponse(response) {
|
|
493
|
+
const lines = [];
|
|
494
|
+
if (response.synthesis) {
|
|
495
|
+
lines.push(response.synthesis.trim());
|
|
496
|
+
lines.push("");
|
|
497
|
+
}
|
|
498
|
+
if (response.results.length > 0) {
|
|
499
|
+
lines.push("Sources:");
|
|
500
|
+
response.results.forEach((chunk, idx) => {
|
|
501
|
+
const label = chunk.title || chunk.chunk_id;
|
|
502
|
+
if (chunk.source_url) {
|
|
503
|
+
lines.push(` [${idx + 1}] ${label} \u2014 ${chunk.source_url}`);
|
|
504
|
+
} else {
|
|
505
|
+
lines.push(` [${idx + 1}] ${label}`);
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
if (lines.length === 0) {
|
|
510
|
+
return "No matching content was found in the company's knowledge brain.";
|
|
511
|
+
}
|
|
512
|
+
return lines.join("\n").trimEnd();
|
|
513
|
+
}
|
|
514
|
+
var QuelvioTool = class extends StructuredTool {
|
|
515
|
+
static lc_name() {
|
|
516
|
+
return "QuelvioTool";
|
|
517
|
+
}
|
|
518
|
+
get lc_namespace() {
|
|
519
|
+
return ["quelvio", "tools"];
|
|
520
|
+
}
|
|
521
|
+
name = "quelvio_query";
|
|
522
|
+
description = DEFAULT_DESCRIPTION;
|
|
523
|
+
schema = QuelvioToolInputSchema;
|
|
524
|
+
defaultMode;
|
|
525
|
+
defaultMaxSources;
|
|
526
|
+
#client;
|
|
527
|
+
constructor(options = {}) {
|
|
528
|
+
super();
|
|
529
|
+
if (options.name) this.name = options.name;
|
|
530
|
+
if (options.description) this.description = options.description;
|
|
531
|
+
this.defaultMode = options.defaultMode ?? "standard";
|
|
532
|
+
this.defaultMaxSources = options.defaultMaxSources ?? 5;
|
|
533
|
+
if (options.client) {
|
|
534
|
+
this.#client = options.client;
|
|
535
|
+
} else {
|
|
536
|
+
const clientOpts = { source: "langchain-js-tool" };
|
|
537
|
+
if (options.apiKey !== void 0) clientOpts.apiKey = options.apiKey;
|
|
538
|
+
if (options.baseUrl !== void 0) clientOpts.baseUrl = options.baseUrl;
|
|
539
|
+
if (options.timeoutMs !== void 0) clientOpts.timeoutMs = options.timeoutMs;
|
|
540
|
+
if (options.maxRetries !== void 0) clientOpts.maxRetries = options.maxRetries;
|
|
541
|
+
if (options.fetch !== void 0) clientOpts.fetch = options.fetch;
|
|
542
|
+
this.#client = new QuelvioClient(clientOpts);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
toString() {
|
|
546
|
+
return `QuelvioTool(name=${this.name}, defaultMode=${this.defaultMode}, defaultMaxSources=${this.defaultMaxSources})`;
|
|
547
|
+
}
|
|
548
|
+
async _call(input, _runManager) {
|
|
549
|
+
if (!input.question || !input.question.trim()) {
|
|
550
|
+
throw new TypeError("question must be a non-empty string");
|
|
551
|
+
}
|
|
552
|
+
const response = await this.#client.query({
|
|
553
|
+
query: input.question,
|
|
554
|
+
limit: input.max_sources ?? this.defaultMaxSources,
|
|
555
|
+
mode: input.mode ?? this.defaultMode,
|
|
556
|
+
domainFilter: input.domain ?? null
|
|
557
|
+
});
|
|
558
|
+
return formatResponse(response);
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
export { ChunkResultSchema, DEFAULT_BASE_URL, DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT_MS, DomainCoverageSchema, DomainsListResponseSchema, QuelvioAuthError, QuelvioBadRequestError, QuelvioClient, QuelvioError, QuelvioNetworkError, QuelvioNotFoundError, QuelvioRateLimitError, QuelvioRetriever, QuelvioServerError, QuelvioTimeoutError, QuelvioTool, QuelvioToolInputSchema, QueryModeSchema, QueryRequestSchema, QueryResponseSchema, SourceChunkSchema, SourceDetailResponseSchema, VERSION, asynthesizeAnswer, boundLimit, buildQueryBody, normalizeMode, synthesizeAnswer };
|
|
563
|
+
//# sourceMappingURL=index.js.map
|
|
564
|
+
//# sourceMappingURL=index.js.map
|