@retrivora-ai/rag-engine 1.9.2 → 1.9.6
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 +27 -0
- package/dist/{ILLMProvider-Bw2A28nU.d.mts → ILLMProvider-DNhyOYoK.d.mts} +41 -1
- package/dist/{ILLMProvider-Bw2A28nU.d.ts → ILLMProvider-DNhyOYoK.d.ts} +41 -1
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +563 -205
- package/dist/handlers/index.mjs +563 -205
- package/dist/index-C9v7-tWd.d.mts +183 -0
- package/dist/{index-B70ZLkfG.d.mts → index-CHL1jdYm.d.mts} +1 -1
- package/dist/index-CjQdL0cX.d.ts +183 -0
- package/dist/{index-DVu-mkAM.d.ts → index-Hgbwl9X4.d.ts} +1 -1
- package/dist/index.css +40 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +326 -158
- package/dist/index.mjs +312 -151
- package/dist/server.d.mts +15 -94
- package/dist/server.d.ts +15 -94
- package/dist/server.js +623 -205
- package/dist/server.mjs +615 -205
- package/package.json +1 -1
- package/src/app/api/chat/route.ts +2 -2
- package/src/app/api/health/route.ts +3 -2
- package/src/app/api/ingest/route.ts +3 -2
- package/src/app/api/suggestions/route.ts +3 -2
- package/src/app/api/upload/route.ts +3 -2
- package/src/app/constants.tsx +85 -30
- package/src/app/layout.tsx +18 -7
- package/src/components/MessageBubble.tsx +39 -2
- package/src/components/ThinkingBlock.tsx +75 -0
- package/src/components/VisualizationRenderer.tsx +27 -1
- package/src/config/RagConfig.ts +47 -0
- package/src/config/serverConfig.ts +25 -0
- package/src/core/ConfigResolver.ts +56 -4
- package/src/core/ConfigValidator.ts +2 -1
- package/src/core/Pipeline.ts +226 -68
- package/src/core/ProviderRegistry.ts +11 -2
- package/src/core/QueryProcessor.ts +38 -4
- package/src/core/Retrivora.ts +51 -0
- package/src/exceptions/index.ts +59 -0
- package/src/handlers/index.ts +2 -0
- package/src/hooks/useRagChat.ts +26 -4
- package/src/index.ts +25 -1
- package/src/lib/plugin.ts +24 -0
- package/src/llm/LLMFactory.ts +4 -1
- package/src/llm/providers/AnthropicProvider.ts +70 -20
- package/src/llm/providers/GeminiProvider.ts +14 -15
- package/src/llm/providers/OllamaProvider.ts +13 -16
- package/src/llm/providers/OpenAIProvider.ts +9 -14
- package/src/llm/providers/UniversalLLMAdapter.ts +5 -5
- package/src/llm/utils.ts +46 -0
- package/src/providers/vectordb/MongoDBProvider.ts +19 -7
- package/src/providers/vectordb/MultiTablePostgresProvider.ts +45 -13
- package/src/rag/EntityExtractor.ts +2 -2
- package/src/rag/Reranker.ts +9 -16
- package/src/server.ts +27 -1
- package/src/types/chat.ts +7 -0
- package/src/utils/UITransformer.ts +73 -4
- package/dist/DocumentChunker-Dh9TvmGG.d.mts +0 -45
- package/dist/DocumentChunker-Dh9TvmGG.d.ts +0 -45
package/dist/handlers/index.js
CHANGED
|
@@ -661,6 +661,11 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
661
661
|
CREATE INDEX IF NOT EXISTS ${this.uploadTable}_embedding_idx
|
|
662
662
|
ON ${this.uploadTable}
|
|
663
663
|
USING hnsw (embedding vector_cosine_ops)
|
|
664
|
+
`);
|
|
665
|
+
await client.query(`
|
|
666
|
+
CREATE INDEX IF NOT EXISTS "${this.uploadTable}_fts_idx"
|
|
667
|
+
ON "${this.uploadTable}"
|
|
668
|
+
USING gin (to_tsvector('english', content))
|
|
664
669
|
`);
|
|
665
670
|
const res = await client.query(`
|
|
666
671
|
SELECT table_name
|
|
@@ -731,6 +736,11 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
731
736
|
CREATE INDEX IF NOT EXISTS "${tableName}_embedding_idx"
|
|
732
737
|
ON "${tableName}"
|
|
733
738
|
USING hnsw (embedding vector_cosine_ops)
|
|
739
|
+
`);
|
|
740
|
+
await client.query(`
|
|
741
|
+
CREATE INDEX IF NOT EXISTS "${tableName}_fts_idx"
|
|
742
|
+
ON "${tableName}"
|
|
743
|
+
USING gin (to_tsvector('english', content))
|
|
734
744
|
`);
|
|
735
745
|
if (!this.tables.includes(tableName)) {
|
|
736
746
|
this.tables.push(tableName);
|
|
@@ -822,8 +832,11 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
822
832
|
const paramIdx = baseOffset + filterParams.length;
|
|
823
833
|
const synonyms = (_a3 = FIELD_SYNONYMS[key]) != null ? _a3 : [];
|
|
824
834
|
const keysToCheck = [key, ...synonyms];
|
|
825
|
-
const coalesceExprs = keysToCheck.
|
|
826
|
-
|
|
835
|
+
const coalesceExprs = keysToCheck.flatMap((k) => [
|
|
836
|
+
`metadata->>'${k}'`,
|
|
837
|
+
`to_jsonb(t)->>'${k}'`
|
|
838
|
+
]);
|
|
839
|
+
return `COALESCE(${coalesceExprs.map((expr) => `LOWER(${expr})`).join(", ")}) = LOWER($${paramIdx})`;
|
|
827
840
|
});
|
|
828
841
|
whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
829
842
|
}
|
|
@@ -834,16 +847,35 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
834
847
|
CASE WHEN LOWER(val) IN (${entityHints.map((h) => `'${h.replace(/'/g, "''")}'`).join(", ")})
|
|
835
848
|
THEN 1.0 ELSE 0.0 END
|
|
836
849
|
), 0)
|
|
837
|
-
FROM jsonb_each_text(
|
|
850
|
+
FROM jsonb_each_text(COALESCE(v.metadata, k.metadata)) AS kv(key, val)
|
|
838
851
|
WHERE key IN (${this.searchFields.map((f) => `'${f}'`).join(", ")})
|
|
839
852
|
) * 3.0` : "";
|
|
840
853
|
sqlQuery = `
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
854
|
+
WITH vector_search AS (
|
|
855
|
+
SELECT *, (1 - (embedding <=> $1::vector)) AS vector_score
|
|
856
|
+
FROM "${table}" t
|
|
857
|
+
${whereClause}
|
|
858
|
+
ORDER BY embedding <=> $1::vector ASC
|
|
859
|
+
LIMIT ${tableLimit}
|
|
860
|
+
),
|
|
861
|
+
keyword_search AS (
|
|
862
|
+
SELECT *, COALESCE(ts_rank(to_tsvector('english', content), to_tsquery('english', $2)), 0) AS keyword_score
|
|
863
|
+
FROM "${table}" t
|
|
864
|
+
WHERE ${whereClause ? whereClause.replace("WHERE", "") : "TRUE"}
|
|
865
|
+
AND to_tsvector('english', content) @@ to_tsquery('english', $2)
|
|
866
|
+
ORDER BY keyword_score DESC
|
|
867
|
+
LIMIT ${tableLimit}
|
|
868
|
+
)
|
|
869
|
+
SELECT
|
|
870
|
+
COALESCE(v.id, k.id) AS id,
|
|
871
|
+
COALESCE(v.namespace, k.namespace) AS namespace,
|
|
872
|
+
COALESCE(v.content, k.content) AS content,
|
|
873
|
+
COALESCE(v.metadata, k.metadata) AS metadata,
|
|
874
|
+
COALESCE(v.vector_score, 0) AS vector_score,
|
|
875
|
+
COALESCE(k.keyword_score, 0) AS keyword_score,
|
|
876
|
+
(COALESCE(v.vector_score, 0) + COALESCE(k.keyword_score, 0) * 2.0 ${exactNameScoreExpr}) AS hybrid_score
|
|
877
|
+
FROM vector_search v
|
|
878
|
+
FULL OUTER JOIN keyword_search k ON v.id = k.id
|
|
847
879
|
ORDER BY hybrid_score DESC
|
|
848
880
|
LIMIT ${tableLimit}
|
|
849
881
|
`;
|
|
@@ -854,7 +886,7 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
854
886
|
(1 - (embedding <=> $1::vector)) AS hybrid_score
|
|
855
887
|
FROM "${table}" t
|
|
856
888
|
${whereClause}
|
|
857
|
-
ORDER BY
|
|
889
|
+
ORDER BY embedding <=> $1::vector ASC
|
|
858
890
|
LIMIT ${tableLimit}
|
|
859
891
|
`;
|
|
860
892
|
params = [vectorLiteral, ...filterParams];
|
|
@@ -1060,7 +1092,11 @@ var init_MongoDBProvider = __esm({
|
|
|
1060
1092
|
if (key === "namespace") {
|
|
1061
1093
|
vectorSearchFilter.namespace = value;
|
|
1062
1094
|
} else {
|
|
1063
|
-
|
|
1095
|
+
if (typeof value === "string") {
|
|
1096
|
+
matchFilter[key] = { $regex: new RegExp(`^${value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "i") };
|
|
1097
|
+
} else {
|
|
1098
|
+
matchFilter[key] = value;
|
|
1099
|
+
}
|
|
1064
1100
|
}
|
|
1065
1101
|
}
|
|
1066
1102
|
const pipeline = [
|
|
@@ -1090,12 +1126,15 @@ var init_MongoDBProvider = __esm({
|
|
|
1090
1126
|
}
|
|
1091
1127
|
);
|
|
1092
1128
|
const results = await this.collection.aggregate(pipeline).toArray();
|
|
1093
|
-
return results.map((res) =>
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1129
|
+
return results.map((res) => {
|
|
1130
|
+
const normalizedScore = res.score * 2 - 1;
|
|
1131
|
+
return {
|
|
1132
|
+
id: res._id,
|
|
1133
|
+
content: res[this.contentKey],
|
|
1134
|
+
metadata: res[this.metadataKey] || {},
|
|
1135
|
+
score: normalizedScore
|
|
1136
|
+
};
|
|
1137
|
+
});
|
|
1099
1138
|
}
|
|
1100
1139
|
async delete(id, namespace) {
|
|
1101
1140
|
await this.collection.deleteOne(__spreadValues({ _id: id }, namespace ? { namespace } : {}));
|
|
@@ -2146,7 +2185,7 @@ function readEnum(env, name, fallback, allowed) {
|
|
|
2146
2185
|
throw new Error(`[getRagConfig] ${name} must be one of: ${allowed.join(", ")}`);
|
|
2147
2186
|
}
|
|
2148
2187
|
function getEnvConfig(env = process.env, base) {
|
|
2149
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga;
|
|
2188
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga, _Ha, _Ia, _Ja;
|
|
2150
2189
|
const projectId = (_c = (_b = (_a = readString(env, "RAG_PROJECT_ID")) != null ? _a : readString(env, "NEXT_PUBLIC_PROJECT_ID")) != null ? _b : base == null ? void 0 : base.projectId) != null ? _c : "__default__";
|
|
2151
2190
|
const vectorProvider = readEnum(env, "VECTOR_DB_PROVIDER", "pinecone", VECTOR_DB_PROVIDERS);
|
|
2152
2191
|
const llmProvider = readEnum(env, "LLM_PROVIDER", "openai", LLM_PROVIDERS);
|
|
@@ -2216,7 +2255,7 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2216
2255
|
rest: "default",
|
|
2217
2256
|
custom: "default"
|
|
2218
2257
|
};
|
|
2219
|
-
return {
|
|
2258
|
+
return __spreadValues({
|
|
2220
2259
|
projectId,
|
|
2221
2260
|
vectorDb: {
|
|
2222
2261
|
provider: vectorProvider,
|
|
@@ -2232,7 +2271,9 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2232
2271
|
maxTokens: readNumber(env, "LLM_MAX_TOKENS", 4096),
|
|
2233
2272
|
temperature: readNumber(env, "LLM_TEMPERATURE", 0.7),
|
|
2234
2273
|
options: {
|
|
2235
|
-
profile: readString(env, "LLM_UNIVERSAL_PROFILE")
|
|
2274
|
+
profile: readString(env, "LLM_UNIVERSAL_PROFILE"),
|
|
2275
|
+
thinking: readString(env, "LLM_THINKING") === "true" || readString(env, "LLM_THINKING") === "enabled" ? true : readString(env, "LLM_THINKING") === "false" ? false : void 0,
|
|
2276
|
+
thinkingBudget: env.LLM_THINKING_BUDGET ? parseInt(env.LLM_THINKING_BUDGET, 10) : void 0
|
|
2236
2277
|
}
|
|
2237
2278
|
},
|
|
2238
2279
|
embedding: {
|
|
@@ -2264,11 +2305,56 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2264
2305
|
topK: readNumber(env, "RAG_TOP_K", 5),
|
|
2265
2306
|
scoreThreshold: readNumber(env, "RAG_SCORE_THRESHOLD", 0),
|
|
2266
2307
|
chunkSize: readNumber(env, "RAG_CHUNK_SIZE", 1e3),
|
|
2267
|
-
chunkOverlap: readNumber(env, "RAG_CHUNK_OVERLAP", 200)
|
|
2308
|
+
chunkOverlap: readNumber(env, "RAG_CHUNK_OVERLAP", 200),
|
|
2309
|
+
filterableFields: (_Ha = readString(env, "RAG_FILTERABLE_FIELDS")) == null ? void 0 : _Ha.split(",").map((f) => f.trim()),
|
|
2310
|
+
// Query pipeline toggles — read from .env.local
|
|
2311
|
+
useQueryTransformation: readString(env, "RAG_USE_QUERY_TRANSFORMATION") === "true",
|
|
2312
|
+
useReranking: readString(env, "RAG_USE_RERANKING") === "true",
|
|
2313
|
+
useGraphRetrieval: readString(env, "RAG_USE_GRAPH_RETRIEVAL") === "true",
|
|
2314
|
+
architecture: (_Ia = readString(env, "RAG_ARCHITECTURE")) != null ? _Ia : "simple",
|
|
2315
|
+
chunkingStrategy: (_Ja = readString(env, "RAG_CHUNKING_STRATEGY")) != null ? _Ja : "recursive",
|
|
2316
|
+
uiMapping: (() => {
|
|
2317
|
+
const raw = readString(env, "RAG_UI_MAPPING");
|
|
2318
|
+
if (!raw) return void 0;
|
|
2319
|
+
try {
|
|
2320
|
+
return JSON.parse(raw);
|
|
2321
|
+
} catch (e) {
|
|
2322
|
+
return void 0;
|
|
2323
|
+
}
|
|
2324
|
+
})()
|
|
2268
2325
|
}
|
|
2269
|
-
}
|
|
2326
|
+
}, readString(env, "GRAPH_DB_PROVIDER") ? {
|
|
2327
|
+
graphDb: {
|
|
2328
|
+
provider: readString(env, "GRAPH_DB_PROVIDER"),
|
|
2329
|
+
options: {
|
|
2330
|
+
uri: readString(env, "GRAPH_DB_URI"),
|
|
2331
|
+
username: readString(env, "GRAPH_DB_USERNAME"),
|
|
2332
|
+
password: readString(env, "GRAPH_DB_PASSWORD")
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
} : {});
|
|
2270
2336
|
}
|
|
2271
2337
|
|
|
2338
|
+
// src/exceptions/index.ts
|
|
2339
|
+
var RetrivoraError = class extends Error {
|
|
2340
|
+
constructor(message, code, details) {
|
|
2341
|
+
super(message);
|
|
2342
|
+
this.name = new.target.name;
|
|
2343
|
+
this.code = code;
|
|
2344
|
+
this.details = details;
|
|
2345
|
+
}
|
|
2346
|
+
};
|
|
2347
|
+
var ProviderNotFoundException = class extends RetrivoraError {
|
|
2348
|
+
constructor(providerType, provider, details) {
|
|
2349
|
+
super(`Unsupported ${providerType} provider: ${provider}`, "PROVIDER_NOT_FOUND", details);
|
|
2350
|
+
}
|
|
2351
|
+
};
|
|
2352
|
+
var ConfigurationException = class extends RetrivoraError {
|
|
2353
|
+
constructor(message, details) {
|
|
2354
|
+
super(message, "CONFIGURATION_ERROR", details);
|
|
2355
|
+
}
|
|
2356
|
+
};
|
|
2357
|
+
|
|
2272
2358
|
// src/core/ConfigResolver.ts
|
|
2273
2359
|
var ConfigResolver = class {
|
|
2274
2360
|
/**
|
|
@@ -2295,24 +2381,81 @@ var ConfigResolver = class {
|
|
|
2295
2381
|
rag: hostConfig.rag ? __spreadValues(__spreadValues({}, envConfig.rag), hostConfig.rag) : envConfig.rag
|
|
2296
2382
|
});
|
|
2297
2383
|
}
|
|
2384
|
+
/**
|
|
2385
|
+
* Resolves the public SDK config shape used by `new Retrivora({...})`.
|
|
2386
|
+
* Supports aliases from the product prompt while preserving existing env
|
|
2387
|
+
* fallback behavior.
|
|
2388
|
+
*/
|
|
2389
|
+
static resolveUniversal(hostConfig, env = process.env) {
|
|
2390
|
+
var _a;
|
|
2391
|
+
if (!hostConfig) return this.resolve(void 0, env);
|
|
2392
|
+
const normalized = __spreadProps(__spreadValues({}, hostConfig), {
|
|
2393
|
+
vectorDb: (_a = hostConfig.vectorDb) != null ? _a : hostConfig.vectorDatabase,
|
|
2394
|
+
rag: this.mergeRetrievalWorkflow(hostConfig.rag, hostConfig.retrieval, hostConfig.workflow)
|
|
2395
|
+
});
|
|
2396
|
+
return this.resolve(normalized, env);
|
|
2397
|
+
}
|
|
2298
2398
|
/**
|
|
2299
2399
|
* Validates the configuration for required fields.
|
|
2300
2400
|
*/
|
|
2301
2401
|
static validate(config) {
|
|
2302
2402
|
if (!config.projectId) {
|
|
2303
|
-
throw new
|
|
2403
|
+
throw new ConfigurationException("[ConfigResolver] projectId is required");
|
|
2304
2404
|
}
|
|
2305
2405
|
if (!config.vectorDb.provider) {
|
|
2306
|
-
throw new
|
|
2406
|
+
throw new ConfigurationException("[ConfigResolver] vectorDb.provider is required");
|
|
2307
2407
|
}
|
|
2308
2408
|
if (!config.llm.provider) {
|
|
2309
|
-
throw new
|
|
2409
|
+
throw new ConfigurationException("[ConfigResolver] llm.provider is required");
|
|
2310
2410
|
}
|
|
2311
2411
|
}
|
|
2412
|
+
static mergeRetrievalWorkflow(rag, retrieval, workflow) {
|
|
2413
|
+
var _a, _b, _c, _d, _e;
|
|
2414
|
+
if (!rag && !retrieval && !workflow) return void 0;
|
|
2415
|
+
const normalized = __spreadValues({}, rag != null ? rag : {});
|
|
2416
|
+
if (retrieval) {
|
|
2417
|
+
normalized.topK = (_a = retrieval.topK) != null ? _a : normalized.topK;
|
|
2418
|
+
normalized.scoreThreshold = (_b = retrieval.scoreThreshold) != null ? _b : normalized.scoreThreshold;
|
|
2419
|
+
normalized.useReranking = (_c = retrieval.useReranking) != null ? _c : normalized.useReranking;
|
|
2420
|
+
if (retrieval.strategy === "hybrid") normalized.architecture = (_d = normalized.architecture) != null ? _d : "hybrid";
|
|
2421
|
+
if (retrieval.strategy === "agentic") normalized.architecture = "agentic";
|
|
2422
|
+
if (retrieval.strategy === "contextual-compression") normalized.useReranking = true;
|
|
2423
|
+
}
|
|
2424
|
+
if (workflow == null ? void 0 : workflow.type) {
|
|
2425
|
+
const type = workflow.type;
|
|
2426
|
+
if (type === "agentic" || type === "agentic-rag") normalized.architecture = "agentic";
|
|
2427
|
+
else if (type === "hybrid-rag") normalized.architecture = "hybrid";
|
|
2428
|
+
else if (type === "graph-rag") {
|
|
2429
|
+
normalized.architecture = "graph";
|
|
2430
|
+
normalized.useGraphRetrieval = true;
|
|
2431
|
+
} else if (type === "simple-rag" || type === "rag") {
|
|
2432
|
+
normalized.architecture = (_e = normalized.architecture) != null ? _e : "simple";
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
return normalized;
|
|
2436
|
+
}
|
|
2312
2437
|
};
|
|
2313
2438
|
|
|
2314
2439
|
// src/llm/providers/OpenAIProvider.ts
|
|
2315
2440
|
var import_openai = __toESM(require("openai"));
|
|
2441
|
+
|
|
2442
|
+
// src/llm/utils.ts
|
|
2443
|
+
function buildSystemContent(systemPrompt, context) {
|
|
2444
|
+
const noContext = !context || context.trim() === "" || context.trim() === "No relevant context found.";
|
|
2445
|
+
if (systemPrompt.includes("{{context}}")) {
|
|
2446
|
+
return systemPrompt.replace("{{context}}", noContext ? "" : context);
|
|
2447
|
+
}
|
|
2448
|
+
if (noContext) {
|
|
2449
|
+
return systemPrompt;
|
|
2450
|
+
}
|
|
2451
|
+
return `${systemPrompt}
|
|
2452
|
+
|
|
2453
|
+
---
|
|
2454
|
+
Context:
|
|
2455
|
+
${context}`;
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2458
|
+
// src/llm/providers/OpenAIProvider.ts
|
|
2316
2459
|
var OpenAIProvider = class {
|
|
2317
2460
|
constructor(llmConfig, embeddingConfig) {
|
|
2318
2461
|
if (!llmConfig.apiKey) throw new Error("[OpenAIProvider] llmConfig.apiKey is required");
|
|
@@ -2376,16 +2519,10 @@ var OpenAIProvider = class {
|
|
|
2376
2519
|
}
|
|
2377
2520
|
async chat(messages, context, options) {
|
|
2378
2521
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
2379
|
-
const
|
|
2380
|
-
|
|
2381
|
-
Context:
|
|
2382
|
-
${context}`;
|
|
2522
|
+
const basePrompt = (_b = (_a = options == null ? void 0 : options.systemPrompt) != null ? _a : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Answer questions based on the provided context.";
|
|
2383
2523
|
const systemMessage = {
|
|
2384
2524
|
role: "system",
|
|
2385
|
-
content:
|
|
2386
|
-
|
|
2387
|
-
Context:
|
|
2388
|
-
${context}`
|
|
2525
|
+
content: buildSystemContent(basePrompt, context)
|
|
2389
2526
|
};
|
|
2390
2527
|
const formattedMessages = [
|
|
2391
2528
|
systemMessage,
|
|
@@ -2406,16 +2543,10 @@ ${context}`
|
|
|
2406
2543
|
chatStream(messages, context, options) {
|
|
2407
2544
|
return __asyncGenerator(this, null, function* () {
|
|
2408
2545
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2409
|
-
const
|
|
2410
|
-
|
|
2411
|
-
Context:
|
|
2412
|
-
${context}`;
|
|
2546
|
+
const basePrompt = (_b = (_a = options == null ? void 0 : options.systemPrompt) != null ? _a : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Answer questions based on the provided context.";
|
|
2413
2547
|
const systemMessage = {
|
|
2414
2548
|
role: "system",
|
|
2415
|
-
content:
|
|
2416
|
-
|
|
2417
|
-
Context:
|
|
2418
|
-
${context}`
|
|
2549
|
+
content: buildSystemContent(basePrompt, context)
|
|
2419
2550
|
};
|
|
2420
2551
|
const formattedMessages = [
|
|
2421
2552
|
systemMessage,
|
|
@@ -2536,58 +2667,87 @@ var AnthropicProvider = class {
|
|
|
2536
2667
|
};
|
|
2537
2668
|
}
|
|
2538
2669
|
async chat(messages, context, options) {
|
|
2539
|
-
var _a, _b, _c, _d;
|
|
2540
|
-
const
|
|
2541
|
-
|
|
2542
|
-
Context:
|
|
2543
|
-
${context}`;
|
|
2544
|
-
const system = resolvedPrompt.includes("{{context}}") ? resolvedPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedPrompt : `${resolvedPrompt}
|
|
2545
|
-
|
|
2546
|
-
Context:
|
|
2547
|
-
${context}`;
|
|
2670
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2671
|
+
const basePrompt = (_b = (_a = options == null ? void 0 : options.systemPrompt) != null ? _a : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Use the context below to answer the user's question accurately.";
|
|
2672
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2548
2673
|
const anthropicMessages = messages.map((m) => ({
|
|
2549
2674
|
role: m.role === "assistant" ? "assistant" : "user",
|
|
2550
2675
|
content: m.content
|
|
2551
2676
|
}));
|
|
2552
|
-
const
|
|
2677
|
+
const isClaude37 = this.llmConfig.model.includes("claude-3-7-sonnet");
|
|
2678
|
+
const isThinkingEnabled = ((_c = this.llmConfig.options) == null ? void 0 : _c.thinking) === true || ((_d = this.llmConfig.options) == null ? void 0 : _d.thinking) === "enabled" || isClaude37 && ((_e = this.llmConfig.options) == null ? void 0 : _e.thinking) !== false;
|
|
2679
|
+
const extraParams = {};
|
|
2680
|
+
if (isThinkingEnabled && isClaude37) {
|
|
2681
|
+
extraParams.betas = ["interleaved-thinking-2025-05-14"];
|
|
2682
|
+
const maxTokens = (_g = (_f = options == null ? void 0 : options.maxTokens) != null ? _f : this.llmConfig.maxTokens) != null ? _g : 4096;
|
|
2683
|
+
const budget = Math.min(
|
|
2684
|
+
typeof ((_h = this.llmConfig.options) == null ? void 0 : _h.thinkingBudget) === "number" ? (_i = this.llmConfig.options) == null ? void 0 : _i.thinkingBudget : 2048,
|
|
2685
|
+
maxTokens - 1
|
|
2686
|
+
);
|
|
2687
|
+
extraParams.thinking = {
|
|
2688
|
+
type: "enabled",
|
|
2689
|
+
budget_tokens: budget
|
|
2690
|
+
};
|
|
2691
|
+
extraParams.max_tokens = Math.max(maxTokens, budget + 1024);
|
|
2692
|
+
}
|
|
2693
|
+
const response = await this.client.messages.create(__spreadValues({
|
|
2553
2694
|
model: this.llmConfig.model,
|
|
2554
|
-
max_tokens: (
|
|
2695
|
+
max_tokens: (_k = (_j = options == null ? void 0 : options.maxTokens) != null ? _j : this.llmConfig.maxTokens) != null ? _k : 1024,
|
|
2555
2696
|
system,
|
|
2556
2697
|
messages: anthropicMessages
|
|
2557
|
-
});
|
|
2558
|
-
|
|
2559
|
-
|
|
2698
|
+
}, extraParams));
|
|
2699
|
+
let reply = "";
|
|
2700
|
+
for (const block of response.content) {
|
|
2701
|
+
if (block.type === "text") {
|
|
2702
|
+
reply += block.text;
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
return reply;
|
|
2560
2706
|
}
|
|
2561
2707
|
chatStream(messages, context, options) {
|
|
2562
2708
|
return __asyncGenerator(this, null, function* () {
|
|
2563
|
-
var _a, _b, _c, _d;
|
|
2564
|
-
const
|
|
2565
|
-
|
|
2566
|
-
Context:
|
|
2567
|
-
${context}`;
|
|
2568
|
-
const system = resolvedPrompt.includes("{{context}}") ? resolvedPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedPrompt : `${resolvedPrompt}
|
|
2569
|
-
|
|
2570
|
-
Context:
|
|
2571
|
-
${context}`;
|
|
2709
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2710
|
+
const basePrompt = (_b = (_a = options == null ? void 0 : options.systemPrompt) != null ? _a : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Use the context below to answer the user's question accurately.";
|
|
2711
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2572
2712
|
const anthropicMessages = messages.map((m) => ({
|
|
2573
2713
|
role: m.role === "assistant" ? "assistant" : "user",
|
|
2574
2714
|
content: m.content
|
|
2575
2715
|
}));
|
|
2576
|
-
const
|
|
2716
|
+
const isClaude37 = this.llmConfig.model.includes("claude-3-7-sonnet");
|
|
2717
|
+
const isThinkingEnabled = ((_c = this.llmConfig.options) == null ? void 0 : _c.thinking) === true || ((_d = this.llmConfig.options) == null ? void 0 : _d.thinking) === "enabled" || isClaude37 && ((_e = this.llmConfig.options) == null ? void 0 : _e.thinking) !== false;
|
|
2718
|
+
const extraParams = {};
|
|
2719
|
+
if (isThinkingEnabled && isClaude37) {
|
|
2720
|
+
extraParams.betas = ["interleaved-thinking-2025-05-14"];
|
|
2721
|
+
const maxTokens = (_g = (_f = options == null ? void 0 : options.maxTokens) != null ? _f : this.llmConfig.maxTokens) != null ? _g : 4096;
|
|
2722
|
+
const budget = Math.min(
|
|
2723
|
+
typeof ((_h = this.llmConfig.options) == null ? void 0 : _h.thinkingBudget) === "number" ? (_i = this.llmConfig.options) == null ? void 0 : _i.thinkingBudget : 2048,
|
|
2724
|
+
maxTokens - 1
|
|
2725
|
+
);
|
|
2726
|
+
extraParams.thinking = {
|
|
2727
|
+
type: "enabled",
|
|
2728
|
+
budget_tokens: budget
|
|
2729
|
+
};
|
|
2730
|
+
extraParams.max_tokens = Math.max(maxTokens, budget + 1024);
|
|
2731
|
+
}
|
|
2732
|
+
const stream = yield new __await(this.client.messages.create(__spreadValues({
|
|
2577
2733
|
model: this.llmConfig.model,
|
|
2578
|
-
max_tokens: (
|
|
2734
|
+
max_tokens: (_k = (_j = options == null ? void 0 : options.maxTokens) != null ? _j : this.llmConfig.maxTokens) != null ? _k : 1024,
|
|
2579
2735
|
system,
|
|
2580
2736
|
messages: anthropicMessages,
|
|
2581
2737
|
stream: true
|
|
2582
|
-
}));
|
|
2738
|
+
}, extraParams)));
|
|
2583
2739
|
if (!stream) {
|
|
2584
2740
|
throw new Error("[AnthropicProvider] messages.create stream is undefined");
|
|
2585
2741
|
}
|
|
2586
2742
|
try {
|
|
2587
2743
|
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
2588
2744
|
const chunk = temp.value;
|
|
2589
|
-
if (chunk.type === "content_block_delta"
|
|
2590
|
-
|
|
2745
|
+
if (chunk.type === "content_block_delta") {
|
|
2746
|
+
if (chunk.delta.type === "text_delta") {
|
|
2747
|
+
yield chunk.delta.text;
|
|
2748
|
+
} else if (chunk.delta.type === "thinking_delta") {
|
|
2749
|
+
yield `\0THINKING\0${chunk.delta.thinking}`;
|
|
2750
|
+
}
|
|
2591
2751
|
}
|
|
2592
2752
|
}
|
|
2593
2753
|
} catch (temp) {
|
|
@@ -2631,9 +2791,10 @@ ${context}`;
|
|
|
2631
2791
|
var import_axios = __toESM(require("axios"));
|
|
2632
2792
|
var OllamaProvider = class {
|
|
2633
2793
|
constructor(llmConfig, embeddingConfig) {
|
|
2634
|
-
var _a;
|
|
2794
|
+
var _a, _b;
|
|
2635
2795
|
const baseURL = (_a = llmConfig.baseUrl) != null ? _a : "http://localhost:11434";
|
|
2636
|
-
|
|
2796
|
+
const timeout = Number((_b = llmConfig.options) == null ? void 0 : _b.timeout) || 3e5;
|
|
2797
|
+
this.http = import_axios.default.create({ baseURL, timeout });
|
|
2637
2798
|
this.llmConfig = llmConfig;
|
|
2638
2799
|
this.embeddingConfig = embeddingConfig;
|
|
2639
2800
|
}
|
|
@@ -2689,14 +2850,15 @@ var OllamaProvider = class {
|
|
|
2689
2850
|
};
|
|
2690
2851
|
}
|
|
2691
2852
|
async chat(messages, context, options) {
|
|
2692
|
-
var _a, _b, _c, _d;
|
|
2693
|
-
const
|
|
2853
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2854
|
+
const basePrompt = (_b = (_a = options == null ? void 0 : options.systemPrompt) != null ? _a : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Use the provided context to answer the user's question.";
|
|
2855
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2694
2856
|
const { data } = await this.http.post("/api/chat", {
|
|
2695
2857
|
model: this.llmConfig.model,
|
|
2696
2858
|
stream: false,
|
|
2697
2859
|
options: {
|
|
2698
|
-
temperature: (
|
|
2699
|
-
num_predict: (
|
|
2860
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : 0.7,
|
|
2861
|
+
num_predict: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : 1024
|
|
2700
2862
|
},
|
|
2701
2863
|
messages: [
|
|
2702
2864
|
{ role: "system", content: system },
|
|
@@ -2707,14 +2869,15 @@ var OllamaProvider = class {
|
|
|
2707
2869
|
}
|
|
2708
2870
|
chatStream(messages, context, options) {
|
|
2709
2871
|
return __asyncGenerator(this, null, function* () {
|
|
2710
|
-
var _a, _b, _c, _d, _e, _f;
|
|
2711
|
-
const
|
|
2872
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2873
|
+
const basePrompt = (_b = (_a = options == null ? void 0 : options.systemPrompt) != null ? _a : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Use the provided context to answer the user's question.";
|
|
2874
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2712
2875
|
const response = yield new __await(this.http.post("/api/chat", {
|
|
2713
2876
|
model: this.llmConfig.model,
|
|
2714
2877
|
stream: true,
|
|
2715
2878
|
options: {
|
|
2716
|
-
temperature: (
|
|
2717
|
-
num_predict: (
|
|
2879
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : 0.7,
|
|
2880
|
+
num_predict: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : 1024
|
|
2718
2881
|
},
|
|
2719
2882
|
messages: [
|
|
2720
2883
|
{ role: "system", content: system },
|
|
@@ -2736,7 +2899,7 @@ var OllamaProvider = class {
|
|
|
2736
2899
|
if (!line.trim()) continue;
|
|
2737
2900
|
try {
|
|
2738
2901
|
const json = JSON.parse(line);
|
|
2739
|
-
if ((
|
|
2902
|
+
if ((_g = json.message) == null ? void 0 : _g.content) {
|
|
2740
2903
|
yield json.message.content;
|
|
2741
2904
|
}
|
|
2742
2905
|
if (json.done) return;
|
|
@@ -2758,26 +2921,12 @@ var OllamaProvider = class {
|
|
|
2758
2921
|
if (lineBuffer.trim()) {
|
|
2759
2922
|
try {
|
|
2760
2923
|
const json = JSON.parse(lineBuffer);
|
|
2761
|
-
if ((
|
|
2924
|
+
if ((_h = json.message) == null ? void 0 : _h.content) yield json.message.content;
|
|
2762
2925
|
} catch (e) {
|
|
2763
2926
|
}
|
|
2764
2927
|
}
|
|
2765
2928
|
});
|
|
2766
2929
|
}
|
|
2767
|
-
buildSystemPrompt(context, overridePrompt) {
|
|
2768
|
-
var _a;
|
|
2769
|
-
if (overridePrompt) {
|
|
2770
|
-
return overridePrompt;
|
|
2771
|
-
}
|
|
2772
|
-
const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the provided context to answer the user's question.
|
|
2773
|
-
|
|
2774
|
-
Context:
|
|
2775
|
-
${context}`;
|
|
2776
|
-
return systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
|
|
2777
|
-
|
|
2778
|
-
Context:
|
|
2779
|
-
${context}`;
|
|
2780
|
-
}
|
|
2781
2930
|
async embed(text, options) {
|
|
2782
2931
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2783
2932
|
const model = (_c = (_b = options == null ? void 0 : options.model) != null ? _b : (_a = this.embeddingConfig) == null ? void 0 : _a.model) != null ? _c : "nomic-embed-text";
|
|
@@ -2924,21 +3073,6 @@ var GeminiProvider = class {
|
|
|
2924
3073
|
var _a, _b;
|
|
2925
3074
|
return sanitizeModel((_b = optionsModel != null ? optionsModel : (_a = this.embeddingConfig) == null ? void 0 : _a.model) != null ? _b : DEFAULT_EMBEDDING_MODEL);
|
|
2926
3075
|
}
|
|
2927
|
-
/**
|
|
2928
|
-
* Build the system instruction string, inserting the RAG context either via
|
|
2929
|
-
* the `{{context}}` placeholder or by appending it.
|
|
2930
|
-
*/
|
|
2931
|
-
buildSystemInstruction(context) {
|
|
2932
|
-
var _a;
|
|
2933
|
-
const base = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Answer questions based on the provided context.
|
|
2934
|
-
|
|
2935
|
-
Context:
|
|
2936
|
-
${context}`;
|
|
2937
|
-
return base.includes("{{context}}") ? base.replace("{{context}}", context) : `${base}
|
|
2938
|
-
|
|
2939
|
-
Context:
|
|
2940
|
-
${context}`;
|
|
2941
|
-
}
|
|
2942
3076
|
/**
|
|
2943
3077
|
* Convert ChatMessage[] to the Gemini contents format.
|
|
2944
3078
|
*
|
|
@@ -2957,16 +3091,17 @@ ${context}`;
|
|
|
2957
3091
|
// ILLMProvider — chat
|
|
2958
3092
|
// -------------------------------------------------------------------------
|
|
2959
3093
|
async chat(messages, context, options) {
|
|
2960
|
-
var _a, _b, _c, _d;
|
|
3094
|
+
var _a, _b, _c, _d, _e, _f;
|
|
3095
|
+
const basePrompt = (_b = (_a = options == null ? void 0 : options.systemPrompt) != null ? _a : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Answer questions based on the provided context.";
|
|
2961
3096
|
const model = this.client.getGenerativeModel({
|
|
2962
3097
|
model: this.llmConfig.model,
|
|
2963
|
-
systemInstruction:
|
|
3098
|
+
systemInstruction: buildSystemContent(basePrompt, context)
|
|
2964
3099
|
});
|
|
2965
3100
|
const result = await model.generateContent({
|
|
2966
3101
|
contents: this.buildGeminiContents(messages),
|
|
2967
3102
|
generationConfig: {
|
|
2968
|
-
temperature: (
|
|
2969
|
-
maxOutputTokens: (
|
|
3103
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : DEFAULT_TEMPERATURE,
|
|
3104
|
+
maxOutputTokens: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : DEFAULT_MAX_TOKENS,
|
|
2970
3105
|
stopSequences: options == null ? void 0 : options.stop
|
|
2971
3106
|
}
|
|
2972
3107
|
});
|
|
@@ -2974,16 +3109,17 @@ ${context}`;
|
|
|
2974
3109
|
}
|
|
2975
3110
|
chatStream(messages, context, options) {
|
|
2976
3111
|
return __asyncGenerator(this, null, function* () {
|
|
2977
|
-
var _a, _b, _c, _d;
|
|
3112
|
+
var _a, _b, _c, _d, _e, _f;
|
|
3113
|
+
const basePrompt = (_b = (_a = options == null ? void 0 : options.systemPrompt) != null ? _a : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Answer questions based on the provided context.";
|
|
2978
3114
|
const model = this.client.getGenerativeModel({
|
|
2979
3115
|
model: this.llmConfig.model,
|
|
2980
|
-
systemInstruction:
|
|
3116
|
+
systemInstruction: buildSystemContent(basePrompt, context)
|
|
2981
3117
|
});
|
|
2982
3118
|
const result = yield new __await(model.generateContentStream({
|
|
2983
3119
|
contents: this.buildGeminiContents(messages),
|
|
2984
3120
|
generationConfig: {
|
|
2985
|
-
temperature: (
|
|
2986
|
-
maxOutputTokens: (
|
|
3121
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : DEFAULT_TEMPERATURE,
|
|
3122
|
+
maxOutputTokens: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : DEFAULT_MAX_TOKENS,
|
|
2987
3123
|
stopSequences: options == null ? void 0 : options.stop
|
|
2988
3124
|
}
|
|
2989
3125
|
}));
|
|
@@ -3379,7 +3515,9 @@ var LLMFactory = class _LLMFactory {
|
|
|
3379
3515
|
if (llmConfig.baseUrl || ((_b = llmConfig.options) == null ? void 0 : _b.baseUrl)) {
|
|
3380
3516
|
return new UniversalLLMAdapter(llmConfig);
|
|
3381
3517
|
}
|
|
3382
|
-
throw new
|
|
3518
|
+
throw new ProviderNotFoundException(
|
|
3519
|
+
"LLM",
|
|
3520
|
+
String(llmConfig.provider),
|
|
3383
3521
|
`[LLMFactory] Unknown provider "${llmConfig.provider}". Built-in providers: ${_LLMFactory.listProviders().join(", ")}. Register a custom provider with LLMFactory.register().`
|
|
3384
3522
|
);
|
|
3385
3523
|
}
|
|
@@ -3511,7 +3649,7 @@ var ProviderRegistry = class {
|
|
|
3511
3649
|
return UniversalVectorProvider2;
|
|
3512
3650
|
}
|
|
3513
3651
|
default:
|
|
3514
|
-
throw new
|
|
3652
|
+
throw new ProviderNotFoundException("vector", provider);
|
|
3515
3653
|
}
|
|
3516
3654
|
}
|
|
3517
3655
|
static async createVectorProvider(config) {
|
|
@@ -3529,12 +3667,18 @@ var ProviderRegistry = class {
|
|
|
3529
3667
|
return new SimpleGraphProvider2(config);
|
|
3530
3668
|
}
|
|
3531
3669
|
default:
|
|
3532
|
-
throw new
|
|
3670
|
+
throw new ProviderNotFoundException("graph", provider);
|
|
3533
3671
|
}
|
|
3534
3672
|
}
|
|
3535
3673
|
static createLLMProvider(llmConfig, embeddingConfig) {
|
|
3536
3674
|
return LLMFactory.create(llmConfig, embeddingConfig);
|
|
3537
3675
|
}
|
|
3676
|
+
static registerLLMProvider(name, factory) {
|
|
3677
|
+
LLMFactory.register(name, factory);
|
|
3678
|
+
}
|
|
3679
|
+
static createEmbeddingProvider(embeddingConfig) {
|
|
3680
|
+
return LLMFactory.createEmbeddingProvider(embeddingConfig);
|
|
3681
|
+
}
|
|
3538
3682
|
};
|
|
3539
3683
|
ProviderRegistry.vectorProviders = {};
|
|
3540
3684
|
ProviderRegistry.graphProviders = {};
|
|
@@ -3681,8 +3825,8 @@ var ConfigValidator = class {
|
|
|
3681
3825
|
const errorItems = errors.filter((e) => e.severity === "error");
|
|
3682
3826
|
if (errorItems.length > 0) {
|
|
3683
3827
|
const message = errorItems.map((e) => `${e.field}: ${e.message}${e.suggestion ? ` (${e.suggestion})` : ""}`).join("\n ");
|
|
3684
|
-
throw new
|
|
3685
|
-
${message}
|
|
3828
|
+
throw new ConfigurationException(`[ConfigValidator] Configuration validation failed:
|
|
3829
|
+
${message}`, errorItems);
|
|
3686
3830
|
}
|
|
3687
3831
|
}
|
|
3688
3832
|
};
|
|
@@ -3869,20 +4013,17 @@ var Reranker = class {
|
|
|
3869
4013
|
}
|
|
3870
4014
|
try {
|
|
3871
4015
|
const topN = matches.slice(0, 10);
|
|
3872
|
-
const
|
|
3873
|
-
|
|
3874
|
-
Return the result as a comma-separated list of document indices in order of relevance, from most relevant to least relevant.
|
|
3875
|
-
Use the indices provided in brackets like [0], [1], etc.
|
|
3876
|
-
Only return the indices (e.g., "2,0,1"), nothing else. Do not include brackets in the output.
|
|
3877
|
-
|
|
3878
|
-
Query: "${query}"
|
|
4016
|
+
const response = await llm.chat(
|
|
4017
|
+
[{ role: "user", content: `Query: "${query}"
|
|
3879
4018
|
|
|
3880
4019
|
Documents:
|
|
3881
|
-
${topN.map((m, i) => `[${i}] ${m.content.replace(/\n/g, " ")}`).join("\n")}
|
|
3882
|
-
const response = await llm.chat(
|
|
3883
|
-
[{ role: "user", content: prompt }],
|
|
4020
|
+
${topN.map((m, i) => `[${i}] ${m.content.replace(/\n/g, " ")}`).join("\n")}` }],
|
|
3884
4021
|
"",
|
|
3885
|
-
{
|
|
4022
|
+
{
|
|
4023
|
+
systemPrompt: 'You are a relevance ranking expert. Given the user query and a list of retrieved document chunks, rank the chunks by relevance to the query. Return the result as a comma-separated list of document indices in order of relevance, from most relevant to least relevant. Use the indices provided in brackets like [0], [1], etc. Only return the indices (e.g., "2,0,1"), nothing else. Do not include brackets in the output.',
|
|
4024
|
+
temperature: 0,
|
|
4025
|
+
maxTokens: 50
|
|
4026
|
+
}
|
|
3886
4027
|
);
|
|
3887
4028
|
const cleanedResponse = response.trim().replace(/[\[\]\s]/g, "");
|
|
3888
4029
|
const rankedIndices = cleanedResponse.split(",").map(Number).filter((n) => !isNaN(n));
|
|
@@ -4388,6 +4529,18 @@ var QueryProcessor = class {
|
|
|
4388
4529
|
}, normalizedField ? { field: normalizedField } : {}));
|
|
4389
4530
|
}
|
|
4390
4531
|
};
|
|
4532
|
+
const activeValidFields = validFields.length > 0 ? validFields : ["brand", "category", "price", "rating", "stock", "stock_quantity", "status", "name", "title"];
|
|
4533
|
+
const resolveValidField = (fieldStr) => {
|
|
4534
|
+
const cleaned = this.normalizeHintValue(fieldStr).replace(/\b(?:provide|show|get|give|list|all|the|a|an|of|organizations?|companies?|records?|items?|whose|with|having|where|that|which|have|has|is|are|was|were)\b/gi, " ").replace(/\s+/g, " ").trim();
|
|
4535
|
+
const comparable = (v) => v.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
4536
|
+
const cleanedComparable = comparable(cleaned);
|
|
4537
|
+
if (!cleanedComparable) return null;
|
|
4538
|
+
const matchedField = activeValidFields.find((fieldName) => {
|
|
4539
|
+
const candidate = comparable(fieldName);
|
|
4540
|
+
return candidate === cleanedComparable || candidate.includes(cleanedComparable) || cleanedComparable.includes(candidate);
|
|
4541
|
+
});
|
|
4542
|
+
return matchedField != null ? matchedField : null;
|
|
4543
|
+
};
|
|
4391
4544
|
for (const match of question.matchAll(/["']([^"']{2,100})["']/g)) {
|
|
4392
4545
|
addHint(match[1]);
|
|
4393
4546
|
}
|
|
@@ -4412,7 +4565,13 @@ var QueryProcessor = class {
|
|
|
4412
4565
|
const field = match[1];
|
|
4413
4566
|
const value = match[2];
|
|
4414
4567
|
if (field && !this.isLikelyPromptPhrase(field)) {
|
|
4415
|
-
|
|
4568
|
+
const resolvedField = resolveValidField(field);
|
|
4569
|
+
if (resolvedField) {
|
|
4570
|
+
addHint(value, resolvedField);
|
|
4571
|
+
} else {
|
|
4572
|
+
addHint(value);
|
|
4573
|
+
addHint(field);
|
|
4574
|
+
}
|
|
4416
4575
|
}
|
|
4417
4576
|
}
|
|
4418
4577
|
if (validFields.length > 0) {
|
|
@@ -4458,12 +4617,16 @@ var QueryProcessor = class {
|
|
|
4458
4617
|
];
|
|
4459
4618
|
for (const pattern of fieldValuePatterns) {
|
|
4460
4619
|
for (const match of question.matchAll(pattern)) {
|
|
4461
|
-
const field =
|
|
4620
|
+
const field = (_c = match[1]) != null ? _c : "";
|
|
4462
4621
|
const value = (_d = match[2]) != null ? _d : "";
|
|
4463
|
-
|
|
4464
|
-
|
|
4622
|
+
const resolvedField = resolveValidField(field);
|
|
4623
|
+
if (resolvedField) {
|
|
4624
|
+
addHint(value, resolvedField);
|
|
4465
4625
|
} else {
|
|
4466
4626
|
addHint(value);
|
|
4627
|
+
if (field && !this.isLikelyPromptPhrase(field)) {
|
|
4628
|
+
addHint(field);
|
|
4629
|
+
}
|
|
4467
4630
|
}
|
|
4468
4631
|
}
|
|
4469
4632
|
}
|
|
@@ -4690,7 +4853,7 @@ var LLMRouter = class {
|
|
|
4690
4853
|
|
|
4691
4854
|
// src/utils/UITransformer.ts
|
|
4692
4855
|
init_synonyms();
|
|
4693
|
-
var UITransformer = class {
|
|
4856
|
+
var UITransformer = class _UITransformer {
|
|
4694
4857
|
// ─── Public Entry Points ─────────────────────────────────────────────────
|
|
4695
4858
|
/**
|
|
4696
4859
|
* Heuristic-only transform (no LLM required).
|
|
@@ -4752,7 +4915,7 @@ var UITransformer = class {
|
|
|
4752
4915
|
static async analyzeAndDecide(query, sources, llm) {
|
|
4753
4916
|
let intent;
|
|
4754
4917
|
try {
|
|
4755
|
-
intent = await this.detectIntent(query, llm);
|
|
4918
|
+
intent = await this.detectIntent(query, llm, sources);
|
|
4756
4919
|
console.debug("[UITransformer] Detected intent:", intent);
|
|
4757
4920
|
} catch (err) {
|
|
4758
4921
|
console.warn("[UITransformer] Intent detection failed; using heuristic.", err);
|
|
@@ -4770,11 +4933,23 @@ var UITransformer = class {
|
|
|
4770
4933
|
try {
|
|
4771
4934
|
const context = this.buildContextSummary(sources);
|
|
4772
4935
|
const systemPrompt = this.buildVisualizationSystemPrompt();
|
|
4936
|
+
const profile = this.profileData(sources);
|
|
4937
|
+
const schemaContext = `
|
|
4938
|
+
RETRIEVED DATA SCHEMA:
|
|
4939
|
+
- Numeric fields (measures): ${profile.numericFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4940
|
+
- Category fields (dimensions): ${profile.categoricalFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4941
|
+
- Date fields (temporal): ${profile.dateFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4942
|
+
- Boolean fields: ${profile.booleanFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4943
|
+
- Text/Other fields: ${profile.fields.filter((f) => f.kind === "text").map((f) => f.key).join(", ") || "None"}
|
|
4944
|
+
`;
|
|
4773
4945
|
const userPrompt = [
|
|
4774
4946
|
`USER QUESTION: ${query}`,
|
|
4775
4947
|
"",
|
|
4776
4948
|
`DETECTED INTENT (JSON): ${JSON.stringify(intent)}`,
|
|
4777
4949
|
"",
|
|
4950
|
+
`RETRIEVED DATA SCHEMA:`,
|
|
4951
|
+
schemaContext,
|
|
4952
|
+
"",
|
|
4778
4953
|
"RETRIEVED DATA (JSON):",
|
|
4779
4954
|
context
|
|
4780
4955
|
].join("\n");
|
|
@@ -4785,6 +4960,20 @@ var UITransformer = class {
|
|
|
4785
4960
|
);
|
|
4786
4961
|
const parsed = this.parseTransformationResponse(rawResponse);
|
|
4787
4962
|
if (parsed) {
|
|
4963
|
+
let isCompatible = true;
|
|
4964
|
+
if (parsed.type === "line_chart" && (profile.dateFields.length === 0 || profile.numericFields.length === 0)) {
|
|
4965
|
+
isCompatible = false;
|
|
4966
|
+
} else if (["bar_chart", "horizontal_bar", "pie_chart", "donut_chart"].includes(parsed.type) && (profile.categoricalFields.length === 0 && profile.numericFields.length === 0)) {
|
|
4967
|
+
isCompatible = false;
|
|
4968
|
+
} else if (parsed.type === "scatter_plot" && profile.numericFields.length < 2) {
|
|
4969
|
+
isCompatible = false;
|
|
4970
|
+
} else if (parsed.type === "metric_card" && profile.numericFields.length === 0) {
|
|
4971
|
+
isCompatible = false;
|
|
4972
|
+
}
|
|
4973
|
+
if (!isCompatible) {
|
|
4974
|
+
console.warn(`[UITransformer] LLM chose incompatible visualization type "${parsed.type}" for retrieved schema. Falling back to heuristic.`);
|
|
4975
|
+
return this.transform(query, sources, void 0, void 0, intent);
|
|
4976
|
+
}
|
|
4788
4977
|
const intentAllowsTable = intent.wantsExplicitTable || ["tabular", "table", "geographic"].includes(intent.visualizationHint);
|
|
4789
4978
|
const intentWantsPieLikeChart = ["pie_chart", "donut_chart"].includes(intent.recommendedChart) || ["composition", "category_breakdown"].includes(intent.visualizationHint);
|
|
4790
4979
|
if (parsed.type === "table" && !intentAllowsTable) {
|
|
@@ -4814,7 +5003,26 @@ var UITransformer = class {
|
|
|
4814
5003
|
* - The intent object can be reused across both the heuristic and LLM paths.
|
|
4815
5004
|
* - It is easy to unit-test intent detection in isolation.
|
|
4816
5005
|
*/
|
|
4817
|
-
static async detectIntent(query, llm) {
|
|
5006
|
+
static async detectIntent(query, llm, sources) {
|
|
5007
|
+
let schemaProfileText = "";
|
|
5008
|
+
if (sources && sources.length > 0) {
|
|
5009
|
+
const profile = this.profileData(sources);
|
|
5010
|
+
const hasProducts = sources.some((item) => this.isProductData(item));
|
|
5011
|
+
schemaProfileText = `
|
|
5012
|
+
RETRIEVED DATA SCHEMA:
|
|
5013
|
+
- Numeric fields (measures): ${profile.numericFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
5014
|
+
- Category fields (dimensions): ${profile.categoricalFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
5015
|
+
- Date fields (temporal): ${profile.dateFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
5016
|
+
- Boolean fields: ${profile.booleanFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
5017
|
+
- Text fields: ${profile.fields.filter((f) => f.kind === "text").map((f) => f.key).join(", ") || "None"}
|
|
5018
|
+
|
|
5019
|
+
Please choose visualizationHint and recommendedChart dynamically based on the available schema.
|
|
5020
|
+
- Do NOT recommend "trend" or "line_chart" if there are no Date fields.
|
|
5021
|
+
- Do NOT recommend numeric-dependent visualizations (like bar_chart, line_chart, pie_chart, scatter_plot, histogram, metric_card) if there are no Numeric fields. Fall back to tabular/table or text.
|
|
5022
|
+
- If there are multiple metadata fields but no clear categories or numbers, "tabular"/"table" is a highly accurate default.
|
|
5023
|
+
${hasProducts ? `- Note: The retrieved data matches product/catalog structure. If the user query is looking for, exploring, searching, browsing, or listing products/items, you MUST recommend visualizationHint: "product_browse" and recommendedChart: "text".` : ""}
|
|
5024
|
+
`;
|
|
5025
|
+
}
|
|
4818
5026
|
const systemPrompt = `You are an intent classifier for a product-search RAG system.
|
|
4819
5027
|
Given a user query, return ONLY a valid JSON object with this exact shape \u2014 no prose, no markdown:
|
|
4820
5028
|
|
|
@@ -4850,8 +5058,11 @@ RULES:
|
|
|
4850
5058
|
- isTemporal: true if time words appear (trend, historical, over time, last year, monthly, etc.)
|
|
4851
5059
|
- isComparison: true if user compares, ranks, or contrasts entities or categories.
|
|
4852
5060
|
- language: detect from the query text itself; default "en" if uncertain.`;
|
|
5061
|
+
const userPrompt = `QUERY: ${query}${schemaProfileText ? `
|
|
5062
|
+
|
|
5063
|
+
${schemaProfileText}` : ""}`;
|
|
4853
5064
|
const rawResponse = await llm.chat(
|
|
4854
|
-
[{ role: "user", content:
|
|
5065
|
+
[{ role: "user", content: userPrompt }],
|
|
4855
5066
|
"",
|
|
4856
5067
|
{ systemPrompt, temperature: 0 }
|
|
4857
5068
|
);
|
|
@@ -5294,10 +5505,21 @@ RULES:
|
|
|
5294
5505
|
const hasEntityTerms = /\b(organization|organisations?|organizations?|company|companies|employee|employees|staff|headcount|workforce)\b/.test(q);
|
|
5295
5506
|
return asksForRecords && (hasNumericPredicate || hasEntityTerms);
|
|
5296
5507
|
}
|
|
5508
|
+
/** @internal kept private for transform() internal logic */
|
|
5297
5509
|
static isProductQuery(query) {
|
|
5510
|
+
return _UITransformer._productQueryTest(query);
|
|
5511
|
+
}
|
|
5512
|
+
/**
|
|
5513
|
+
* Public entry-point for external callers (e.g. Pipeline)
|
|
5514
|
+
* to check whether a query maps to product_browse without triggering an LLM call.
|
|
5515
|
+
*/
|
|
5516
|
+
static isProductQueryPublic(query) {
|
|
5517
|
+
return _UITransformer._productQueryTest(query);
|
|
5518
|
+
}
|
|
5519
|
+
static _productQueryTest(query) {
|
|
5298
5520
|
const q = query.toLowerCase();
|
|
5299
5521
|
const productTerms = /\b(product|products|item|items|sku|catalog|catalogue|price|prices|brand|model|stock|inventory|in stock|out of stock|buy|shop)\b/.test(q);
|
|
5300
|
-
const productAction = /\b(recommend|suggest|describe|description|detail|details|about|show|find|search|browse|list)\b/.test(q);
|
|
5522
|
+
const productAction = /\b(recommend|suggest|describe|description|detail|details|about|show|find|search|browse|list|explore|exploring)\b/.test(q);
|
|
5301
5523
|
const nonProductEntityTerms = /\b(organization|organisations?|organizations?|company|companies|employee|employees|staff|headcount|workforce)\b/.test(q);
|
|
5302
5524
|
return productTerms && productAction && !nonProductEntityTerms;
|
|
5303
5525
|
}
|
|
@@ -6070,23 +6292,19 @@ function estimateCostUsd(promptTokens, completionTokens, model) {
|
|
|
6070
6292
|
async function scoreHallucination(llm, answer, context) {
|
|
6071
6293
|
const maxContextChars = 3e3;
|
|
6072
6294
|
const truncatedContext = context.length > maxContextChars ? context.slice(0, maxContextChars) + "\n...[truncated]" : context;
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
CONTEXT:
|
|
6295
|
+
try {
|
|
6296
|
+
const raw = await llm.chat(
|
|
6297
|
+
[{ role: "user", content: `CONTEXT:
|
|
6076
6298
|
${truncatedContext}
|
|
6077
6299
|
|
|
6078
6300
|
ANSWER:
|
|
6079
|
-
${answer}
|
|
6080
|
-
|
|
6081
|
-
Return ONLY a valid JSON object with no markdown fences:
|
|
6082
|
-
{"score": <float 0-1>, "reason": "<one sentence>"}
|
|
6083
|
-
|
|
6084
|
-
Where score 0 = fully grounded in context, score 1 = likely hallucinated or unsupported.`;
|
|
6085
|
-
try {
|
|
6086
|
-
const raw = await llm.chat(
|
|
6087
|
-
[{ role: "user", content: prompt }],
|
|
6301
|
+
${answer}` }],
|
|
6088
6302
|
"",
|
|
6089
|
-
{
|
|
6303
|
+
{
|
|
6304
|
+
systemPrompt: 'You are an AI quality checker. Given the CONTEXT retrieved from a knowledge base and the ANSWER generated from it, rate how well-grounded the answer is. Return ONLY a valid JSON object with no markdown fences: {"score": <float 0-1>, "reason": "<one sentence>"} where score 0 = fully grounded in context, score 1 = likely hallucinated or unsupported.',
|
|
6305
|
+
temperature: 0,
|
|
6306
|
+
maxTokens: 120
|
|
6307
|
+
}
|
|
6090
6308
|
);
|
|
6091
6309
|
const jsonMatch = raw.match(/\{[\s\S]*\}/);
|
|
6092
6310
|
if (!jsonMatch) return void 0;
|
|
@@ -6146,7 +6364,10 @@ var Pipeline = class {
|
|
|
6146
6364
|
- For product descriptions, summarize customer-facing details only. Do NOT list internal catalog fields such as Handle, Body (HTML), Vendor, Type, Tags, Published, Option, Variant, SKU, or raw metadata labels.
|
|
6147
6365
|
- Do NOT try to format product lists as tables.
|
|
6148
6366
|
`;
|
|
6149
|
-
this.config.llm.systemPrompt
|
|
6367
|
+
const userPromptPrefix = this.config.llm.systemPrompt ? `${this.config.llm.systemPrompt}
|
|
6368
|
+
|
|
6369
|
+
` : "";
|
|
6370
|
+
this.config.llm.systemPrompt = `${userPromptPrefix}${chartInstruction}`;
|
|
6150
6371
|
this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
|
|
6151
6372
|
this.llmRouter = new LLMRouter(this.config);
|
|
6152
6373
|
const { llmProvider: resolvedLLM, embeddingProvider } = await EmbeddingStrategyResolver.resolve(
|
|
@@ -6324,11 +6545,11 @@ var Pipeline = class {
|
|
|
6324
6545
|
*/
|
|
6325
6546
|
askStream(_0) {
|
|
6326
6547
|
return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
|
|
6327
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
6548
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
|
|
6328
6549
|
yield new __await(this.initialize());
|
|
6329
6550
|
const ns = namespace != null ? namespace : this.config.projectId;
|
|
6330
6551
|
const topK = (_b = (_a = this.config.rag) == null ? void 0 : _a.topK) != null ? _b : 5;
|
|
6331
|
-
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0;
|
|
6552
|
+
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0.3;
|
|
6332
6553
|
const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
6333
6554
|
const requestStart = performance.now();
|
|
6334
6555
|
try {
|
|
@@ -6347,12 +6568,13 @@ var Pipeline = class {
|
|
|
6347
6568
|
const embedStart = performance.now();
|
|
6348
6569
|
const cacheKey = `${ns}::${searchQuery}`;
|
|
6349
6570
|
const cachedVector = this.embeddingCache.get(cacheKey);
|
|
6571
|
+
const useGraph = this.graphDB && ((_h = this.config.rag) == null ? void 0 : _h.useGraphRetrieval);
|
|
6350
6572
|
const [strategyResult, embeddedVector] = yield new __await(Promise.all([
|
|
6351
6573
|
QueryProcessor.determineRetrievalStrategy(
|
|
6352
6574
|
searchQuery,
|
|
6353
|
-
this.llmRouter.get("fast"),
|
|
6354
|
-
(
|
|
6355
|
-
(
|
|
6575
|
+
useGraph ? this.llmRouter.get("fast") : void 0,
|
|
6576
|
+
(_i = this.config.rag) == null ? void 0 : _i.graphKeywords,
|
|
6577
|
+
(_j = this.config.rag) == null ? void 0 : _j.vectorKeywords
|
|
6356
6578
|
),
|
|
6357
6579
|
// Embed immediately regardless of strategy — costs nothing if cached.
|
|
6358
6580
|
// If strategy turns out to be 'graph'-only we just won't use the vector.
|
|
@@ -6362,7 +6584,7 @@ var Pipeline = class {
|
|
|
6362
6584
|
if (!cachedVector && queryVector.length > 0) {
|
|
6363
6585
|
this.embeddingCache.set(cacheKey, queryVector);
|
|
6364
6586
|
}
|
|
6365
|
-
const graphData = (strategyResult === "graph" || strategyResult === "both") && this.graphDB && ((
|
|
6587
|
+
const graphData = (strategyResult === "graph" || strategyResult === "both") && this.graphDB && ((_k = this.config.rag) == null ? void 0 : _k.useGraphRetrieval) ? yield new __await(this.graphDB.query(searchQuery)) : void 0;
|
|
6366
6588
|
const hasMetadataFilter = filter.metadata && Object.keys(filter.metadata).length > 0;
|
|
6367
6589
|
const hasNumericPredicates = Array.isArray(filter.__numericPredicates) && filter.__numericPredicates.length > 0;
|
|
6368
6590
|
const wantsExhaustiveList = hasNumericPredicates || hasMetadataFilter || /\b(list|all|show|provide|give|get|browse|find|search|display)\b/i.test(question);
|
|
@@ -6375,7 +6597,7 @@ var Pipeline = class {
|
|
|
6375
6597
|
const structuredSources = this.applyStructuredFilters(rawSources, filter);
|
|
6376
6598
|
let fullSources = hasNumericPredicates ? structuredSources : structuredSources.filter((m) => m.score >= scoreThreshold);
|
|
6377
6599
|
const rerankLimit = wantsExhaustiveList ? retrievalLimit : topK;
|
|
6378
|
-
if (!hasNumericPredicates && ((
|
|
6600
|
+
if (!hasNumericPredicates && ((_l = this.config.rag) == null ? void 0 : _l.useReranking)) {
|
|
6379
6601
|
fullSources = yield new __await(this.reranker.rerank(fullSources, question, rerankLimit));
|
|
6380
6602
|
} else if (!wantsExhaustiveList) {
|
|
6381
6603
|
fullSources = fullSources.slice(0, topK);
|
|
@@ -6387,7 +6609,8 @@ ${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
|
|
|
6387
6609
|
if (hasMetadataFilter) {
|
|
6388
6610
|
displayCount = fullSources.length;
|
|
6389
6611
|
} else {
|
|
6390
|
-
const
|
|
6612
|
+
const baseThreshold = Math.max(scoreThreshold, 0.4);
|
|
6613
|
+
const highlyRelevant = fullSources.filter((m) => m.score >= baseThreshold);
|
|
6391
6614
|
displayCount = Math.max(highlyRelevant.length, topK);
|
|
6392
6615
|
if (displayCount > 15) {
|
|
6393
6616
|
displayCount = 15;
|
|
@@ -6405,34 +6628,117 @@ VECTOR CONTEXT:
|
|
|
6405
6628
|
${context}`;
|
|
6406
6629
|
}
|
|
6407
6630
|
const allMetadataKeys = Array.from(new Set(sources.flatMap((s) => Object.keys(s.metadata || {}))));
|
|
6408
|
-
const
|
|
6409
|
-
|
|
6410
|
-
(
|
|
6631
|
+
const cachedSchema = allMetadataKeys.length > 0 ? SchemaMapper.getCached(ns, allMetadataKeys) : void 0;
|
|
6632
|
+
if (allMetadataKeys.length > 0 && !cachedSchema) {
|
|
6633
|
+
SchemaMapper.train(this.llmProvider, ns, allMetadataKeys).catch(() => {
|
|
6634
|
+
});
|
|
6635
|
+
}
|
|
6636
|
+
const isProductQ = UITransformer.isProductQueryPublic(question);
|
|
6637
|
+
const uiTransformationPromise = isProductQ ? Promise.resolve(
|
|
6638
|
+
UITransformer.transform(
|
|
6411
6639
|
question,
|
|
6412
6640
|
sources,
|
|
6413
|
-
|
|
6414
|
-
|
|
6641
|
+
this.config,
|
|
6642
|
+
cachedSchema,
|
|
6643
|
+
{ visualizationHint: "product_browse", recommendedChart: "text", filterInStockOnly: false, wantsExplicitTable: false, isTemporal: false, isComparison: false, language: "en" }
|
|
6415
6644
|
)
|
|
6645
|
+
) : this.generateUiTransformation(
|
|
6646
|
+
question,
|
|
6647
|
+
sources,
|
|
6648
|
+
cachedSchema,
|
|
6649
|
+
hasNumericPredicates
|
|
6416
6650
|
).catch((uiError) => {
|
|
6417
6651
|
console.warn("[Pipeline] UI transformation failed concurrently:", uiError);
|
|
6418
|
-
return UITransformer.transform(question, sources, this.config);
|
|
6652
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6419
6653
|
});
|
|
6654
|
+
let hallucinationScoringPromise = Promise.resolve(void 0);
|
|
6420
6655
|
const restrictionSuffix = "\n\n(IMPORTANT: Format your response beautifully using rich Markdown! Use bolding for emphasis, headings (##) for structure, bullet points for lists, and proper line breaks between paragraphs to make it highly readable. However, NEVER generate Markdown tables, HTML figures, or text charts (the UI renders requested charts separately, so NEVER say you cannot create, display, draw, render, or provide a chart/visualization). If listing products, use simple markdown bullet points or comma-separated names. NEVER use plus signs (+) to separate product names, category names, or list items. For product description/detail questions, provide customer-facing prose only and do NOT list internal catalog fields such as Handle, Body (HTML), Vendor, Type, Tags, Published, Option, Variant, SKU, or raw metadata labels. You CAN use numbers for years/counts like 2006 or 5800, but NEVER put a dollar sign ($) before them.)";
|
|
6656
|
+
const isClaude37 = this.config.llm.model.includes("claude-3-7-sonnet");
|
|
6657
|
+
const isNativeThinking = isClaude37 && (((_m = this.config.llm.options) == null ? void 0 : _m.thinking) === true || ((_n = this.config.llm.options) == null ? void 0 : _n.thinking) === "enabled" || ((_o = this.config.llm.options) == null ? void 0 : _o.thinking) !== false);
|
|
6658
|
+
const modelNameLower = this.config.llm.model.toLowerCase();
|
|
6659
|
+
const isReasoningModel = modelNameLower.includes("-r1") || modelNameLower.includes("deepseek-r1") || modelNameLower.includes("reasoning") || modelNameLower.includes("thinking");
|
|
6660
|
+
const isSimulatedThinking = !isNativeThinking && (((_p = this.config.llm.options) == null ? void 0 : _p.thinking) === true || ((_q = this.config.llm.options) == null ? void 0 : _q.thinking) === "enabled" || isReasoningModel && ((_r = this.config.llm.options) == null ? void 0 : _r.thinking) !== false);
|
|
6661
|
+
let finalRestrictionSuffix = restrictionSuffix;
|
|
6662
|
+
if (isSimulatedThinking) {
|
|
6663
|
+
finalRestrictionSuffix += "\n\n(IMPORTANT: You must think step-by-step before answering. Write your thinking process inside a `<think>...</think>` block. Write the `<think>` block first, then follow it with your final response. Keep the thinking process thorough and detailed. Example: `<think>Thinking details here...</think>Final answer text here.`)";
|
|
6664
|
+
}
|
|
6421
6665
|
const hardenedHistory = [...history];
|
|
6422
|
-
const userQuestion = { role: "user", content: question +
|
|
6666
|
+
const userQuestion = { role: "user", content: question + finalRestrictionSuffix };
|
|
6423
6667
|
const messages = [...hardenedHistory, userQuestion];
|
|
6424
|
-
const systemPrompt = (
|
|
6668
|
+
const systemPrompt = (_s = this.config.llm.systemPrompt) != null ? _s : "";
|
|
6425
6669
|
const userPrompt = messages.map((m) => `${m.role}: ${m.content}`).join("\n");
|
|
6426
6670
|
let fullReply = "";
|
|
6671
|
+
let thinkingText = "";
|
|
6672
|
+
let thinkingStartMs = performance.now();
|
|
6673
|
+
let thinkingDurationMs = 0;
|
|
6427
6674
|
const generateStart = performance.now();
|
|
6428
6675
|
if (this.llmProvider.chatStream) {
|
|
6429
6676
|
const stream = this.llmProvider.chatStream(messages, context);
|
|
6430
6677
|
if (!stream) throw new Error(`[Pipeline] ${this.config.llm.provider} chatStream returned undefined`);
|
|
6678
|
+
let inThinking = false;
|
|
6679
|
+
let textBuffer = "";
|
|
6431
6680
|
try {
|
|
6432
6681
|
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
6433
6682
|
const chunk = temp.value;
|
|
6434
|
-
|
|
6435
|
-
|
|
6683
|
+
if (chunk.startsWith("\0THINKING\0")) {
|
|
6684
|
+
const thinkingDelta = chunk.slice("\0THINKING\0".length);
|
|
6685
|
+
thinkingText += thinkingDelta;
|
|
6686
|
+
thinkingDurationMs = performance.now() - thinkingStartMs;
|
|
6687
|
+
yield { type: "thinking", text: thinkingDelta };
|
|
6688
|
+
continue;
|
|
6689
|
+
}
|
|
6690
|
+
if (isSimulatedThinking) {
|
|
6691
|
+
textBuffer += chunk;
|
|
6692
|
+
while (textBuffer.length > 0) {
|
|
6693
|
+
if (!inThinking) {
|
|
6694
|
+
const thinkIndex = textBuffer.indexOf("<think>");
|
|
6695
|
+
if (thinkIndex !== -1) {
|
|
6696
|
+
const before = textBuffer.slice(0, thinkIndex);
|
|
6697
|
+
if (before) {
|
|
6698
|
+
fullReply += before;
|
|
6699
|
+
yield before;
|
|
6700
|
+
}
|
|
6701
|
+
inThinking = true;
|
|
6702
|
+
thinkingStartMs = performance.now();
|
|
6703
|
+
textBuffer = textBuffer.slice(thinkIndex + "<think>".length);
|
|
6704
|
+
} else {
|
|
6705
|
+
const maxSafeLength = textBuffer.length - "<think>".length;
|
|
6706
|
+
if (maxSafeLength > 0) {
|
|
6707
|
+
const safeText = textBuffer.slice(0, maxSafeLength);
|
|
6708
|
+
fullReply += safeText;
|
|
6709
|
+
yield safeText;
|
|
6710
|
+
textBuffer = textBuffer.slice(maxSafeLength);
|
|
6711
|
+
}
|
|
6712
|
+
break;
|
|
6713
|
+
}
|
|
6714
|
+
} else {
|
|
6715
|
+
const endThinkIndex = textBuffer.indexOf("</think>");
|
|
6716
|
+
if (endThinkIndex !== -1) {
|
|
6717
|
+
const thinkingDelta = textBuffer.slice(0, endThinkIndex);
|
|
6718
|
+
if (thinkingDelta) {
|
|
6719
|
+
thinkingText += thinkingDelta;
|
|
6720
|
+
thinkingDurationMs = performance.now() - thinkingStartMs;
|
|
6721
|
+
yield { type: "thinking", text: thinkingDelta };
|
|
6722
|
+
}
|
|
6723
|
+
inThinking = false;
|
|
6724
|
+
textBuffer = textBuffer.slice(endThinkIndex + "</think>".length);
|
|
6725
|
+
} else {
|
|
6726
|
+
const maxSafeLength = textBuffer.length - "</think>".length;
|
|
6727
|
+
if (maxSafeLength > 0) {
|
|
6728
|
+
const thinkingDelta = textBuffer.slice(0, maxSafeLength);
|
|
6729
|
+
thinkingText += thinkingDelta;
|
|
6730
|
+
thinkingDurationMs = performance.now() - thinkingStartMs;
|
|
6731
|
+
yield { type: "thinking", text: thinkingDelta };
|
|
6732
|
+
textBuffer = textBuffer.slice(maxSafeLength);
|
|
6733
|
+
}
|
|
6734
|
+
break;
|
|
6735
|
+
}
|
|
6736
|
+
}
|
|
6737
|
+
}
|
|
6738
|
+
} else {
|
|
6739
|
+
fullReply += chunk;
|
|
6740
|
+
yield chunk;
|
|
6741
|
+
}
|
|
6436
6742
|
}
|
|
6437
6743
|
} catch (temp) {
|
|
6438
6744
|
error = [temp];
|
|
@@ -6444,17 +6750,42 @@ ${context}`;
|
|
|
6444
6750
|
throw error[0];
|
|
6445
6751
|
}
|
|
6446
6752
|
}
|
|
6753
|
+
if (isSimulatedThinking && textBuffer.length > 0) {
|
|
6754
|
+
if (inThinking) {
|
|
6755
|
+
thinkingText += textBuffer;
|
|
6756
|
+
yield { type: "thinking", text: textBuffer };
|
|
6757
|
+
} else {
|
|
6758
|
+
fullReply += textBuffer;
|
|
6759
|
+
yield textBuffer;
|
|
6760
|
+
}
|
|
6761
|
+
}
|
|
6447
6762
|
} else {
|
|
6448
6763
|
const reply = yield new __await(this.llmProvider.chat(messages, context));
|
|
6449
|
-
|
|
6450
|
-
|
|
6764
|
+
if (isSimulatedThinking) {
|
|
6765
|
+
const thinkStart = reply.indexOf("<think>");
|
|
6766
|
+
const thinkEnd = reply.indexOf("</think>");
|
|
6767
|
+
if (thinkStart !== -1 && thinkEnd !== -1 && thinkEnd > thinkStart) {
|
|
6768
|
+
thinkingText = reply.slice(thinkStart + "<think>".length, thinkEnd);
|
|
6769
|
+
fullReply = reply.slice(0, thinkStart) + reply.slice(thinkEnd + "</think>".length);
|
|
6770
|
+
thinkingDurationMs = 100;
|
|
6771
|
+
} else {
|
|
6772
|
+
fullReply = reply;
|
|
6773
|
+
}
|
|
6774
|
+
} else {
|
|
6775
|
+
fullReply = reply;
|
|
6776
|
+
}
|
|
6777
|
+
yield fullReply;
|
|
6778
|
+
}
|
|
6779
|
+
const runHallucination = ((_t = this.config.llm.options) == null ? void 0 : _t.hallucinationScoring) === true || this.config.llm.provider !== "ollama" && ((_u = this.config.llm.options) == null ? void 0 : _u.hallucinationScoring) !== false;
|
|
6780
|
+
if (runHallucination) {
|
|
6781
|
+
hallucinationScoringPromise = scoreHallucination(this.llmProvider, fullReply, context).catch(() => void 0);
|
|
6451
6782
|
}
|
|
6452
6783
|
const generateMs = performance.now() - generateStart;
|
|
6453
6784
|
const totalMs = performance.now() - requestStart;
|
|
6454
6785
|
const latency = {
|
|
6455
6786
|
embedMs: Math.round(embedMs),
|
|
6456
6787
|
retrieveMs: Math.round(retrieveMs),
|
|
6457
|
-
rerankMs: ((
|
|
6788
|
+
rerankMs: ((_v = this.config.rag) == null ? void 0 : _v.useReranking) ? Math.round(rerankMs) : void 0,
|
|
6458
6789
|
generateMs: Math.round(generateMs),
|
|
6459
6790
|
totalMs: Math.round(totalMs)
|
|
6460
6791
|
};
|
|
@@ -6467,12 +6798,16 @@ ${context}`;
|
|
|
6467
6798
|
totalTokens: promptTokens + completionTokens,
|
|
6468
6799
|
estimatedCostUsd: estimateCostUsd(promptTokens, completionTokens, this.config.llm.model)
|
|
6469
6800
|
};
|
|
6470
|
-
const
|
|
6801
|
+
const [uiTransformation, hallucinationResult] = yield new __await(Promise.all([
|
|
6802
|
+
uiTransformationPromise,
|
|
6803
|
+
hallucinationScoringPromise
|
|
6804
|
+
]));
|
|
6805
|
+
const trace = __spreadValues({
|
|
6471
6806
|
requestId,
|
|
6472
6807
|
query: question,
|
|
6473
6808
|
rewrittenQuery,
|
|
6474
6809
|
systemPrompt,
|
|
6475
|
-
userPrompt: question +
|
|
6810
|
+
userPrompt: question + finalRestrictionSuffix,
|
|
6476
6811
|
chunks: sources.map((s) => {
|
|
6477
6812
|
var _a2;
|
|
6478
6813
|
return {
|
|
@@ -6486,22 +6821,19 @@ ${context}`;
|
|
|
6486
6821
|
latency,
|
|
6487
6822
|
tokens,
|
|
6488
6823
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
6489
|
-
}
|
|
6490
|
-
|
|
6824
|
+
}, hallucinationResult ? {
|
|
6825
|
+
hallucinationScore: hallucinationResult.score,
|
|
6826
|
+
hallucinationReason: hallucinationResult.reason
|
|
6827
|
+
} : {});
|
|
6491
6828
|
yield {
|
|
6492
6829
|
reply: "",
|
|
6493
6830
|
sources,
|
|
6494
6831
|
graphData,
|
|
6495
6832
|
ui_transformation: uiTransformation,
|
|
6496
|
-
trace
|
|
6833
|
+
trace,
|
|
6834
|
+
thinking: thinkingText || void 0,
|
|
6835
|
+
thinkingMs: thinkingDurationMs > 0 ? Math.round(thinkingDurationMs) : void 0
|
|
6497
6836
|
};
|
|
6498
|
-
scoreHallucination(this.llmProvider, fullReply, context).then((hallucinationResult) => {
|
|
6499
|
-
if (hallucinationResult) {
|
|
6500
|
-
trace.hallucinationScore = hallucinationResult.score;
|
|
6501
|
-
trace.hallucinationReason = hallucinationResult.reason;
|
|
6502
|
-
}
|
|
6503
|
-
}).catch(() => {
|
|
6504
|
-
});
|
|
6505
6837
|
} catch (error2) {
|
|
6506
6838
|
throw new Error(`[Pipeline] Stream failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
6507
6839
|
}
|
|
@@ -6511,18 +6843,30 @@ ${context}`;
|
|
|
6511
6843
|
* Universal retrieval method combining all enabled providers.
|
|
6512
6844
|
* Uses an LRU-bounded embedding cache to avoid re-embedding the same query.
|
|
6513
6845
|
*/
|
|
6514
|
-
async generateUiTransformation(question, sources,
|
|
6846
|
+
async generateUiTransformation(question, sources, cachedSchema, forceDeterministic = false) {
|
|
6847
|
+
var _a;
|
|
6515
6848
|
if (!sources || sources.length === 0) {
|
|
6516
|
-
return UITransformer.transform(question, sources, this.config,
|
|
6849
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6517
6850
|
}
|
|
6518
|
-
if (
|
|
6519
|
-
return UITransformer.transform(
|
|
6851
|
+
if (UITransformer.isProductQueryPublic(question)) {
|
|
6852
|
+
return UITransformer.transform(
|
|
6853
|
+
question,
|
|
6854
|
+
sources,
|
|
6855
|
+
this.config,
|
|
6856
|
+
cachedSchema,
|
|
6857
|
+
{ visualizationHint: "product_browse", recommendedChart: "text", filterInStockOnly: false, wantsExplicitTable: false, isTemporal: false, isComparison: false, language: "en" }
|
|
6858
|
+
);
|
|
6859
|
+
}
|
|
6860
|
+
const isLocalProvider = this.config.llm.provider === "ollama";
|
|
6861
|
+
const disableLlmUiTransform = ((_a = this.config.llm.options) == null ? void 0 : _a.disableLlmUiTransform) === true;
|
|
6862
|
+
if (forceDeterministic || isLocalProvider || disableLlmUiTransform) {
|
|
6863
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6520
6864
|
}
|
|
6521
6865
|
try {
|
|
6522
6866
|
return await UITransformer.analyzeAndDecide(question, sources, this.llmRouter.get("fast"));
|
|
6523
6867
|
} catch (err) {
|
|
6524
6868
|
console.warn("[Pipeline] generateUiTransformation failed, using heuristic fallback:", err);
|
|
6525
|
-
return UITransformer.transform(question, sources, this.config,
|
|
6869
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6526
6870
|
}
|
|
6527
6871
|
}
|
|
6528
6872
|
applyStructuredFilters(sources, filter) {
|
|
@@ -6606,25 +6950,29 @@ ${context}`;
|
|
|
6606
6950
|
return Number.isFinite(numeric) ? numeric : null;
|
|
6607
6951
|
}
|
|
6608
6952
|
async retrieve(query, options) {
|
|
6609
|
-
var _a, _b, _c, _d, _e, _f;
|
|
6953
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
6610
6954
|
const ns = (_a = options.namespace) != null ? _a : this.config.projectId;
|
|
6611
6955
|
const topK = (_b = options.topK) != null ? _b : 5;
|
|
6612
6956
|
const cacheKey = `${ns}::${query}`;
|
|
6613
6957
|
let queryVector = this.embeddingCache.get(cacheKey);
|
|
6614
|
-
const
|
|
6958
|
+
const useGraph = this.graphDB && ((_c = this.config.rag) == null ? void 0 : _c.useGraphRetrieval);
|
|
6959
|
+
const strategy = await QueryProcessor.determineRetrievalStrategy(
|
|
6960
|
+
query,
|
|
6961
|
+
useGraph ? this.llmRouter.get("fast") : void 0
|
|
6962
|
+
);
|
|
6615
6963
|
console.debug(`[Pipeline] Determined retrieval strategy: ${strategy}`);
|
|
6616
6964
|
const [retrievedVector, graphData] = await Promise.all([
|
|
6617
6965
|
// Only embed if we need vector search (strategy is 'vector' or 'both')
|
|
6618
6966
|
strategy === "vector" || strategy === "both" ? queryVector ? Promise.resolve(queryVector) : this.embeddingProvider.embed(query, { taskType: "query" }) : Promise.resolve([]),
|
|
6619
6967
|
// Only query graph if we need graph search (strategy is 'graph' or 'both')
|
|
6620
|
-
(strategy === "graph" || strategy === "both") && this.graphDB && ((
|
|
6968
|
+
(strategy === "graph" || strategy === "both") && this.graphDB && ((_d = this.config.rag) == null ? void 0 : _d.useGraphRetrieval) ? this.graphDB.query(query) : Promise.resolve(void 0)
|
|
6621
6969
|
]);
|
|
6622
6970
|
if ((strategy === "vector" || strategy === "both") && !queryVector && retrievedVector && retrievedVector.length > 0) {
|
|
6623
6971
|
this.embeddingCache.set(cacheKey, retrievedVector);
|
|
6624
6972
|
queryVector = retrievedVector;
|
|
6625
6973
|
}
|
|
6626
|
-
const baseFilter = __spreadProps(__spreadValues({}, (
|
|
6627
|
-
const numericPredicates = QueryProcessor.extractNumericPredicates(query, (
|
|
6974
|
+
const baseFilter = __spreadProps(__spreadValues({}, (_e = options.filter) != null ? _e : {}), { queryText: query });
|
|
6975
|
+
const numericPredicates = QueryProcessor.extractNumericPredicates(query, (_f = this.config.rag) == null ? void 0 : _f.filterableFields);
|
|
6628
6976
|
if (numericPredicates.length > 0) {
|
|
6629
6977
|
baseFilter.__numericPredicates = numericPredicates;
|
|
6630
6978
|
}
|
|
@@ -6635,7 +6983,7 @@ ${context}`;
|
|
|
6635
6983
|
) : [];
|
|
6636
6984
|
const resolvedSources = [];
|
|
6637
6985
|
for (const source of sources) {
|
|
6638
|
-
const parentId = (
|
|
6986
|
+
const parentId = (_g = source.metadata) == null ? void 0 : _g.parent_id;
|
|
6639
6987
|
if (parentId) {
|
|
6640
6988
|
console.log(`[Pipeline] Multi-Vector: Found child chunk. Parent ID: ${parentId}`);
|
|
6641
6989
|
resolvedSources.push(source);
|
|
@@ -6658,10 +7006,13 @@ New Question: ${question}
|
|
|
6658
7006
|
Optimized Search Query:`;
|
|
6659
7007
|
const rewrite = await this.llmProvider.chat(
|
|
6660
7008
|
[
|
|
6661
|
-
{ role: "system", content: "You are an assistant that optimizes search queries for RAG systems." },
|
|
6662
7009
|
{ role: "user", content: prompt }
|
|
6663
7010
|
],
|
|
6664
|
-
""
|
|
7011
|
+
"",
|
|
7012
|
+
{
|
|
7013
|
+
systemPrompt: "You are an assistant that optimizes search queries for RAG systems. Focus on extracting the core intent and entities. Do not answer the question, just rewrite it.",
|
|
7014
|
+
temperature: 0
|
|
7015
|
+
}
|
|
6665
7016
|
);
|
|
6666
7017
|
return rewrite.trim() || question;
|
|
6667
7018
|
}
|
|
@@ -6685,10 +7036,13 @@ ${context}
|
|
|
6685
7036
|
Suggestions:`;
|
|
6686
7037
|
const response = await this.llmProvider.chat(
|
|
6687
7038
|
[
|
|
6688
|
-
{ role: "system", content: "You are a helpful assistant that generates search suggestions." },
|
|
6689
7039
|
{ role: "user", content: prompt }
|
|
6690
7040
|
],
|
|
6691
|
-
""
|
|
7041
|
+
"",
|
|
7042
|
+
{
|
|
7043
|
+
systemPrompt: "You are a helpful assistant that generates search suggestions based on the provided snippets. Focus on questions that can be answered by the context, keep each under 10 words, and return ONLY a JSON array of strings.",
|
|
7044
|
+
temperature: 0
|
|
7045
|
+
}
|
|
6692
7046
|
);
|
|
6693
7047
|
const match = response.match(/\[[\s\S]*\]/);
|
|
6694
7048
|
if (match) {
|
|
@@ -7027,6 +7381,10 @@ function createStreamHandler(configOrPlugin) {
|
|
|
7027
7381
|
if (!isActive) break;
|
|
7028
7382
|
if (typeof chunk === "string") {
|
|
7029
7383
|
enqueue(sseTextFrame(chunk));
|
|
7384
|
+
} else if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "thinking") {
|
|
7385
|
+
enqueue(`data: ${JSON.stringify(chunk)}
|
|
7386
|
+
|
|
7387
|
+
`);
|
|
7030
7388
|
} else {
|
|
7031
7389
|
enqueue(sseMetaFrame(chunk));
|
|
7032
7390
|
const responseChunk = chunk;
|