@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.mjs
CHANGED
|
@@ -640,6 +640,11 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
640
640
|
CREATE INDEX IF NOT EXISTS ${this.uploadTable}_embedding_idx
|
|
641
641
|
ON ${this.uploadTable}
|
|
642
642
|
USING hnsw (embedding vector_cosine_ops)
|
|
643
|
+
`);
|
|
644
|
+
await client.query(`
|
|
645
|
+
CREATE INDEX IF NOT EXISTS "${this.uploadTable}_fts_idx"
|
|
646
|
+
ON "${this.uploadTable}"
|
|
647
|
+
USING gin (to_tsvector('english', content))
|
|
643
648
|
`);
|
|
644
649
|
const res = await client.query(`
|
|
645
650
|
SELECT table_name
|
|
@@ -710,6 +715,11 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
710
715
|
CREATE INDEX IF NOT EXISTS "${tableName}_embedding_idx"
|
|
711
716
|
ON "${tableName}"
|
|
712
717
|
USING hnsw (embedding vector_cosine_ops)
|
|
718
|
+
`);
|
|
719
|
+
await client.query(`
|
|
720
|
+
CREATE INDEX IF NOT EXISTS "${tableName}_fts_idx"
|
|
721
|
+
ON "${tableName}"
|
|
722
|
+
USING gin (to_tsvector('english', content))
|
|
713
723
|
`);
|
|
714
724
|
if (!this.tables.includes(tableName)) {
|
|
715
725
|
this.tables.push(tableName);
|
|
@@ -801,8 +811,11 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
801
811
|
const paramIdx = baseOffset + filterParams.length;
|
|
802
812
|
const synonyms = (_a3 = FIELD_SYNONYMS[key]) != null ? _a3 : [];
|
|
803
813
|
const keysToCheck = [key, ...synonyms];
|
|
804
|
-
const coalesceExprs = keysToCheck.
|
|
805
|
-
|
|
814
|
+
const coalesceExprs = keysToCheck.flatMap((k) => [
|
|
815
|
+
`metadata->>'${k}'`,
|
|
816
|
+
`to_jsonb(t)->>'${k}'`
|
|
817
|
+
]);
|
|
818
|
+
return `COALESCE(${coalesceExprs.map((expr) => `LOWER(${expr})`).join(", ")}) = LOWER($${paramIdx})`;
|
|
806
819
|
});
|
|
807
820
|
whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
808
821
|
}
|
|
@@ -813,16 +826,35 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
813
826
|
CASE WHEN LOWER(val) IN (${entityHints.map((h) => `'${h.replace(/'/g, "''")}'`).join(", ")})
|
|
814
827
|
THEN 1.0 ELSE 0.0 END
|
|
815
828
|
), 0)
|
|
816
|
-
FROM jsonb_each_text(
|
|
829
|
+
FROM jsonb_each_text(COALESCE(v.metadata, k.metadata)) AS kv(key, val)
|
|
817
830
|
WHERE key IN (${this.searchFields.map((f) => `'${f}'`).join(", ")})
|
|
818
831
|
) * 3.0` : "";
|
|
819
832
|
sqlQuery = `
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
833
|
+
WITH vector_search AS (
|
|
834
|
+
SELECT *, (1 - (embedding <=> $1::vector)) AS vector_score
|
|
835
|
+
FROM "${table}" t
|
|
836
|
+
${whereClause}
|
|
837
|
+
ORDER BY embedding <=> $1::vector ASC
|
|
838
|
+
LIMIT ${tableLimit}
|
|
839
|
+
),
|
|
840
|
+
keyword_search AS (
|
|
841
|
+
SELECT *, COALESCE(ts_rank(to_tsvector('english', content), to_tsquery('english', $2)), 0) AS keyword_score
|
|
842
|
+
FROM "${table}" t
|
|
843
|
+
WHERE ${whereClause ? whereClause.replace("WHERE", "") : "TRUE"}
|
|
844
|
+
AND to_tsvector('english', content) @@ to_tsquery('english', $2)
|
|
845
|
+
ORDER BY keyword_score DESC
|
|
846
|
+
LIMIT ${tableLimit}
|
|
847
|
+
)
|
|
848
|
+
SELECT
|
|
849
|
+
COALESCE(v.id, k.id) AS id,
|
|
850
|
+
COALESCE(v.namespace, k.namespace) AS namespace,
|
|
851
|
+
COALESCE(v.content, k.content) AS content,
|
|
852
|
+
COALESCE(v.metadata, k.metadata) AS metadata,
|
|
853
|
+
COALESCE(v.vector_score, 0) AS vector_score,
|
|
854
|
+
COALESCE(k.keyword_score, 0) AS keyword_score,
|
|
855
|
+
(COALESCE(v.vector_score, 0) + COALESCE(k.keyword_score, 0) * 2.0 ${exactNameScoreExpr}) AS hybrid_score
|
|
856
|
+
FROM vector_search v
|
|
857
|
+
FULL OUTER JOIN keyword_search k ON v.id = k.id
|
|
826
858
|
ORDER BY hybrid_score DESC
|
|
827
859
|
LIMIT ${tableLimit}
|
|
828
860
|
`;
|
|
@@ -833,7 +865,7 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
833
865
|
(1 - (embedding <=> $1::vector)) AS hybrid_score
|
|
834
866
|
FROM "${table}" t
|
|
835
867
|
${whereClause}
|
|
836
|
-
ORDER BY
|
|
868
|
+
ORDER BY embedding <=> $1::vector ASC
|
|
837
869
|
LIMIT ${tableLimit}
|
|
838
870
|
`;
|
|
839
871
|
params = [vectorLiteral, ...filterParams];
|
|
@@ -1039,7 +1071,11 @@ var init_MongoDBProvider = __esm({
|
|
|
1039
1071
|
if (key === "namespace") {
|
|
1040
1072
|
vectorSearchFilter.namespace = value;
|
|
1041
1073
|
} else {
|
|
1042
|
-
|
|
1074
|
+
if (typeof value === "string") {
|
|
1075
|
+
matchFilter[key] = { $regex: new RegExp(`^${value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "i") };
|
|
1076
|
+
} else {
|
|
1077
|
+
matchFilter[key] = value;
|
|
1078
|
+
}
|
|
1043
1079
|
}
|
|
1044
1080
|
}
|
|
1045
1081
|
const pipeline = [
|
|
@@ -1069,12 +1105,15 @@ var init_MongoDBProvider = __esm({
|
|
|
1069
1105
|
}
|
|
1070
1106
|
);
|
|
1071
1107
|
const results = await this.collection.aggregate(pipeline).toArray();
|
|
1072
|
-
return results.map((res) =>
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1108
|
+
return results.map((res) => {
|
|
1109
|
+
const normalizedScore = res.score * 2 - 1;
|
|
1110
|
+
return {
|
|
1111
|
+
id: res._id,
|
|
1112
|
+
content: res[this.contentKey],
|
|
1113
|
+
metadata: res[this.metadataKey] || {},
|
|
1114
|
+
score: normalizedScore
|
|
1115
|
+
};
|
|
1116
|
+
});
|
|
1078
1117
|
}
|
|
1079
1118
|
async delete(id, namespace) {
|
|
1080
1119
|
await this.collection.deleteOne(__spreadValues({ _id: id }, namespace ? { namespace } : {}));
|
|
@@ -2109,7 +2148,7 @@ function readEnum(env, name, fallback, allowed) {
|
|
|
2109
2148
|
throw new Error(`[getRagConfig] ${name} must be one of: ${allowed.join(", ")}`);
|
|
2110
2149
|
}
|
|
2111
2150
|
function getEnvConfig(env = process.env, base) {
|
|
2112
|
-
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;
|
|
2151
|
+
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;
|
|
2113
2152
|
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__";
|
|
2114
2153
|
const vectorProvider = readEnum(env, "VECTOR_DB_PROVIDER", "pinecone", VECTOR_DB_PROVIDERS);
|
|
2115
2154
|
const llmProvider = readEnum(env, "LLM_PROVIDER", "openai", LLM_PROVIDERS);
|
|
@@ -2179,7 +2218,7 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2179
2218
|
rest: "default",
|
|
2180
2219
|
custom: "default"
|
|
2181
2220
|
};
|
|
2182
|
-
return {
|
|
2221
|
+
return __spreadValues({
|
|
2183
2222
|
projectId,
|
|
2184
2223
|
vectorDb: {
|
|
2185
2224
|
provider: vectorProvider,
|
|
@@ -2195,7 +2234,9 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2195
2234
|
maxTokens: readNumber(env, "LLM_MAX_TOKENS", 4096),
|
|
2196
2235
|
temperature: readNumber(env, "LLM_TEMPERATURE", 0.7),
|
|
2197
2236
|
options: {
|
|
2198
|
-
profile: readString(env, "LLM_UNIVERSAL_PROFILE")
|
|
2237
|
+
profile: readString(env, "LLM_UNIVERSAL_PROFILE"),
|
|
2238
|
+
thinking: readString(env, "LLM_THINKING") === "true" || readString(env, "LLM_THINKING") === "enabled" ? true : readString(env, "LLM_THINKING") === "false" ? false : void 0,
|
|
2239
|
+
thinkingBudget: env.LLM_THINKING_BUDGET ? parseInt(env.LLM_THINKING_BUDGET, 10) : void 0
|
|
2199
2240
|
}
|
|
2200
2241
|
},
|
|
2201
2242
|
embedding: {
|
|
@@ -2227,11 +2268,56 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2227
2268
|
topK: readNumber(env, "RAG_TOP_K", 5),
|
|
2228
2269
|
scoreThreshold: readNumber(env, "RAG_SCORE_THRESHOLD", 0),
|
|
2229
2270
|
chunkSize: readNumber(env, "RAG_CHUNK_SIZE", 1e3),
|
|
2230
|
-
chunkOverlap: readNumber(env, "RAG_CHUNK_OVERLAP", 200)
|
|
2271
|
+
chunkOverlap: readNumber(env, "RAG_CHUNK_OVERLAP", 200),
|
|
2272
|
+
filterableFields: (_Ha = readString(env, "RAG_FILTERABLE_FIELDS")) == null ? void 0 : _Ha.split(",").map((f) => f.trim()),
|
|
2273
|
+
// Query pipeline toggles — read from .env.local
|
|
2274
|
+
useQueryTransformation: readString(env, "RAG_USE_QUERY_TRANSFORMATION") === "true",
|
|
2275
|
+
useReranking: readString(env, "RAG_USE_RERANKING") === "true",
|
|
2276
|
+
useGraphRetrieval: readString(env, "RAG_USE_GRAPH_RETRIEVAL") === "true",
|
|
2277
|
+
architecture: (_Ia = readString(env, "RAG_ARCHITECTURE")) != null ? _Ia : "simple",
|
|
2278
|
+
chunkingStrategy: (_Ja = readString(env, "RAG_CHUNKING_STRATEGY")) != null ? _Ja : "recursive",
|
|
2279
|
+
uiMapping: (() => {
|
|
2280
|
+
const raw = readString(env, "RAG_UI_MAPPING");
|
|
2281
|
+
if (!raw) return void 0;
|
|
2282
|
+
try {
|
|
2283
|
+
return JSON.parse(raw);
|
|
2284
|
+
} catch (e) {
|
|
2285
|
+
return void 0;
|
|
2286
|
+
}
|
|
2287
|
+
})()
|
|
2231
2288
|
}
|
|
2232
|
-
}
|
|
2289
|
+
}, readString(env, "GRAPH_DB_PROVIDER") ? {
|
|
2290
|
+
graphDb: {
|
|
2291
|
+
provider: readString(env, "GRAPH_DB_PROVIDER"),
|
|
2292
|
+
options: {
|
|
2293
|
+
uri: readString(env, "GRAPH_DB_URI"),
|
|
2294
|
+
username: readString(env, "GRAPH_DB_USERNAME"),
|
|
2295
|
+
password: readString(env, "GRAPH_DB_PASSWORD")
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
} : {});
|
|
2233
2299
|
}
|
|
2234
2300
|
|
|
2301
|
+
// src/exceptions/index.ts
|
|
2302
|
+
var RetrivoraError = class extends Error {
|
|
2303
|
+
constructor(message, code, details) {
|
|
2304
|
+
super(message);
|
|
2305
|
+
this.name = new.target.name;
|
|
2306
|
+
this.code = code;
|
|
2307
|
+
this.details = details;
|
|
2308
|
+
}
|
|
2309
|
+
};
|
|
2310
|
+
var ProviderNotFoundException = class extends RetrivoraError {
|
|
2311
|
+
constructor(providerType, provider, details) {
|
|
2312
|
+
super(`Unsupported ${providerType} provider: ${provider}`, "PROVIDER_NOT_FOUND", details);
|
|
2313
|
+
}
|
|
2314
|
+
};
|
|
2315
|
+
var ConfigurationException = class extends RetrivoraError {
|
|
2316
|
+
constructor(message, details) {
|
|
2317
|
+
super(message, "CONFIGURATION_ERROR", details);
|
|
2318
|
+
}
|
|
2319
|
+
};
|
|
2320
|
+
|
|
2235
2321
|
// src/core/ConfigResolver.ts
|
|
2236
2322
|
var ConfigResolver = class {
|
|
2237
2323
|
/**
|
|
@@ -2258,24 +2344,81 @@ var ConfigResolver = class {
|
|
|
2258
2344
|
rag: hostConfig.rag ? __spreadValues(__spreadValues({}, envConfig.rag), hostConfig.rag) : envConfig.rag
|
|
2259
2345
|
});
|
|
2260
2346
|
}
|
|
2347
|
+
/**
|
|
2348
|
+
* Resolves the public SDK config shape used by `new Retrivora({...})`.
|
|
2349
|
+
* Supports aliases from the product prompt while preserving existing env
|
|
2350
|
+
* fallback behavior.
|
|
2351
|
+
*/
|
|
2352
|
+
static resolveUniversal(hostConfig, env = process.env) {
|
|
2353
|
+
var _a;
|
|
2354
|
+
if (!hostConfig) return this.resolve(void 0, env);
|
|
2355
|
+
const normalized = __spreadProps(__spreadValues({}, hostConfig), {
|
|
2356
|
+
vectorDb: (_a = hostConfig.vectorDb) != null ? _a : hostConfig.vectorDatabase,
|
|
2357
|
+
rag: this.mergeRetrievalWorkflow(hostConfig.rag, hostConfig.retrieval, hostConfig.workflow)
|
|
2358
|
+
});
|
|
2359
|
+
return this.resolve(normalized, env);
|
|
2360
|
+
}
|
|
2261
2361
|
/**
|
|
2262
2362
|
* Validates the configuration for required fields.
|
|
2263
2363
|
*/
|
|
2264
2364
|
static validate(config) {
|
|
2265
2365
|
if (!config.projectId) {
|
|
2266
|
-
throw new
|
|
2366
|
+
throw new ConfigurationException("[ConfigResolver] projectId is required");
|
|
2267
2367
|
}
|
|
2268
2368
|
if (!config.vectorDb.provider) {
|
|
2269
|
-
throw new
|
|
2369
|
+
throw new ConfigurationException("[ConfigResolver] vectorDb.provider is required");
|
|
2270
2370
|
}
|
|
2271
2371
|
if (!config.llm.provider) {
|
|
2272
|
-
throw new
|
|
2372
|
+
throw new ConfigurationException("[ConfigResolver] llm.provider is required");
|
|
2273
2373
|
}
|
|
2274
2374
|
}
|
|
2375
|
+
static mergeRetrievalWorkflow(rag, retrieval, workflow) {
|
|
2376
|
+
var _a, _b, _c, _d, _e;
|
|
2377
|
+
if (!rag && !retrieval && !workflow) return void 0;
|
|
2378
|
+
const normalized = __spreadValues({}, rag != null ? rag : {});
|
|
2379
|
+
if (retrieval) {
|
|
2380
|
+
normalized.topK = (_a = retrieval.topK) != null ? _a : normalized.topK;
|
|
2381
|
+
normalized.scoreThreshold = (_b = retrieval.scoreThreshold) != null ? _b : normalized.scoreThreshold;
|
|
2382
|
+
normalized.useReranking = (_c = retrieval.useReranking) != null ? _c : normalized.useReranking;
|
|
2383
|
+
if (retrieval.strategy === "hybrid") normalized.architecture = (_d = normalized.architecture) != null ? _d : "hybrid";
|
|
2384
|
+
if (retrieval.strategy === "agentic") normalized.architecture = "agentic";
|
|
2385
|
+
if (retrieval.strategy === "contextual-compression") normalized.useReranking = true;
|
|
2386
|
+
}
|
|
2387
|
+
if (workflow == null ? void 0 : workflow.type) {
|
|
2388
|
+
const type = workflow.type;
|
|
2389
|
+
if (type === "agentic" || type === "agentic-rag") normalized.architecture = "agentic";
|
|
2390
|
+
else if (type === "hybrid-rag") normalized.architecture = "hybrid";
|
|
2391
|
+
else if (type === "graph-rag") {
|
|
2392
|
+
normalized.architecture = "graph";
|
|
2393
|
+
normalized.useGraphRetrieval = true;
|
|
2394
|
+
} else if (type === "simple-rag" || type === "rag") {
|
|
2395
|
+
normalized.architecture = (_e = normalized.architecture) != null ? _e : "simple";
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2398
|
+
return normalized;
|
|
2399
|
+
}
|
|
2275
2400
|
};
|
|
2276
2401
|
|
|
2277
2402
|
// src/llm/providers/OpenAIProvider.ts
|
|
2278
2403
|
import OpenAI from "openai";
|
|
2404
|
+
|
|
2405
|
+
// src/llm/utils.ts
|
|
2406
|
+
function buildSystemContent(systemPrompt, context) {
|
|
2407
|
+
const noContext = !context || context.trim() === "" || context.trim() === "No relevant context found.";
|
|
2408
|
+
if (systemPrompt.includes("{{context}}")) {
|
|
2409
|
+
return systemPrompt.replace("{{context}}", noContext ? "" : context);
|
|
2410
|
+
}
|
|
2411
|
+
if (noContext) {
|
|
2412
|
+
return systemPrompt;
|
|
2413
|
+
}
|
|
2414
|
+
return `${systemPrompt}
|
|
2415
|
+
|
|
2416
|
+
---
|
|
2417
|
+
Context:
|
|
2418
|
+
${context}`;
|
|
2419
|
+
}
|
|
2420
|
+
|
|
2421
|
+
// src/llm/providers/OpenAIProvider.ts
|
|
2279
2422
|
var OpenAIProvider = class {
|
|
2280
2423
|
constructor(llmConfig, embeddingConfig) {
|
|
2281
2424
|
if (!llmConfig.apiKey) throw new Error("[OpenAIProvider] llmConfig.apiKey is required");
|
|
@@ -2339,16 +2482,10 @@ var OpenAIProvider = class {
|
|
|
2339
2482
|
}
|
|
2340
2483
|
async chat(messages, context, options) {
|
|
2341
2484
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
2342
|
-
const
|
|
2343
|
-
|
|
2344
|
-
Context:
|
|
2345
|
-
${context}`;
|
|
2485
|
+
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.";
|
|
2346
2486
|
const systemMessage = {
|
|
2347
2487
|
role: "system",
|
|
2348
|
-
content:
|
|
2349
|
-
|
|
2350
|
-
Context:
|
|
2351
|
-
${context}`
|
|
2488
|
+
content: buildSystemContent(basePrompt, context)
|
|
2352
2489
|
};
|
|
2353
2490
|
const formattedMessages = [
|
|
2354
2491
|
systemMessage,
|
|
@@ -2369,16 +2506,10 @@ ${context}`
|
|
|
2369
2506
|
chatStream(messages, context, options) {
|
|
2370
2507
|
return __asyncGenerator(this, null, function* () {
|
|
2371
2508
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2372
|
-
const
|
|
2373
|
-
|
|
2374
|
-
Context:
|
|
2375
|
-
${context}`;
|
|
2509
|
+
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.";
|
|
2376
2510
|
const systemMessage = {
|
|
2377
2511
|
role: "system",
|
|
2378
|
-
content:
|
|
2379
|
-
|
|
2380
|
-
Context:
|
|
2381
|
-
${context}`
|
|
2512
|
+
content: buildSystemContent(basePrompt, context)
|
|
2382
2513
|
};
|
|
2383
2514
|
const formattedMessages = [
|
|
2384
2515
|
systemMessage,
|
|
@@ -2499,58 +2630,87 @@ var AnthropicProvider = class {
|
|
|
2499
2630
|
};
|
|
2500
2631
|
}
|
|
2501
2632
|
async chat(messages, context, options) {
|
|
2502
|
-
var _a, _b, _c, _d;
|
|
2503
|
-
const
|
|
2504
|
-
|
|
2505
|
-
Context:
|
|
2506
|
-
${context}`;
|
|
2507
|
-
const system = resolvedPrompt.includes("{{context}}") ? resolvedPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedPrompt : `${resolvedPrompt}
|
|
2508
|
-
|
|
2509
|
-
Context:
|
|
2510
|
-
${context}`;
|
|
2633
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2634
|
+
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.";
|
|
2635
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2511
2636
|
const anthropicMessages = messages.map((m) => ({
|
|
2512
2637
|
role: m.role === "assistant" ? "assistant" : "user",
|
|
2513
2638
|
content: m.content
|
|
2514
2639
|
}));
|
|
2515
|
-
const
|
|
2640
|
+
const isClaude37 = this.llmConfig.model.includes("claude-3-7-sonnet");
|
|
2641
|
+
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;
|
|
2642
|
+
const extraParams = {};
|
|
2643
|
+
if (isThinkingEnabled && isClaude37) {
|
|
2644
|
+
extraParams.betas = ["interleaved-thinking-2025-05-14"];
|
|
2645
|
+
const maxTokens = (_g = (_f = options == null ? void 0 : options.maxTokens) != null ? _f : this.llmConfig.maxTokens) != null ? _g : 4096;
|
|
2646
|
+
const budget = Math.min(
|
|
2647
|
+
typeof ((_h = this.llmConfig.options) == null ? void 0 : _h.thinkingBudget) === "number" ? (_i = this.llmConfig.options) == null ? void 0 : _i.thinkingBudget : 2048,
|
|
2648
|
+
maxTokens - 1
|
|
2649
|
+
);
|
|
2650
|
+
extraParams.thinking = {
|
|
2651
|
+
type: "enabled",
|
|
2652
|
+
budget_tokens: budget
|
|
2653
|
+
};
|
|
2654
|
+
extraParams.max_tokens = Math.max(maxTokens, budget + 1024);
|
|
2655
|
+
}
|
|
2656
|
+
const response = await this.client.messages.create(__spreadValues({
|
|
2516
2657
|
model: this.llmConfig.model,
|
|
2517
|
-
max_tokens: (
|
|
2658
|
+
max_tokens: (_k = (_j = options == null ? void 0 : options.maxTokens) != null ? _j : this.llmConfig.maxTokens) != null ? _k : 1024,
|
|
2518
2659
|
system,
|
|
2519
2660
|
messages: anthropicMessages
|
|
2520
|
-
});
|
|
2521
|
-
|
|
2522
|
-
|
|
2661
|
+
}, extraParams));
|
|
2662
|
+
let reply = "";
|
|
2663
|
+
for (const block of response.content) {
|
|
2664
|
+
if (block.type === "text") {
|
|
2665
|
+
reply += block.text;
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
return reply;
|
|
2523
2669
|
}
|
|
2524
2670
|
chatStream(messages, context, options) {
|
|
2525
2671
|
return __asyncGenerator(this, null, function* () {
|
|
2526
|
-
var _a, _b, _c, _d;
|
|
2527
|
-
const
|
|
2528
|
-
|
|
2529
|
-
Context:
|
|
2530
|
-
${context}`;
|
|
2531
|
-
const system = resolvedPrompt.includes("{{context}}") ? resolvedPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedPrompt : `${resolvedPrompt}
|
|
2532
|
-
|
|
2533
|
-
Context:
|
|
2534
|
-
${context}`;
|
|
2672
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2673
|
+
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.";
|
|
2674
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2535
2675
|
const anthropicMessages = messages.map((m) => ({
|
|
2536
2676
|
role: m.role === "assistant" ? "assistant" : "user",
|
|
2537
2677
|
content: m.content
|
|
2538
2678
|
}));
|
|
2539
|
-
const
|
|
2679
|
+
const isClaude37 = this.llmConfig.model.includes("claude-3-7-sonnet");
|
|
2680
|
+
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;
|
|
2681
|
+
const extraParams = {};
|
|
2682
|
+
if (isThinkingEnabled && isClaude37) {
|
|
2683
|
+
extraParams.betas = ["interleaved-thinking-2025-05-14"];
|
|
2684
|
+
const maxTokens = (_g = (_f = options == null ? void 0 : options.maxTokens) != null ? _f : this.llmConfig.maxTokens) != null ? _g : 4096;
|
|
2685
|
+
const budget = Math.min(
|
|
2686
|
+
typeof ((_h = this.llmConfig.options) == null ? void 0 : _h.thinkingBudget) === "number" ? (_i = this.llmConfig.options) == null ? void 0 : _i.thinkingBudget : 2048,
|
|
2687
|
+
maxTokens - 1
|
|
2688
|
+
);
|
|
2689
|
+
extraParams.thinking = {
|
|
2690
|
+
type: "enabled",
|
|
2691
|
+
budget_tokens: budget
|
|
2692
|
+
};
|
|
2693
|
+
extraParams.max_tokens = Math.max(maxTokens, budget + 1024);
|
|
2694
|
+
}
|
|
2695
|
+
const stream = yield new __await(this.client.messages.create(__spreadValues({
|
|
2540
2696
|
model: this.llmConfig.model,
|
|
2541
|
-
max_tokens: (
|
|
2697
|
+
max_tokens: (_k = (_j = options == null ? void 0 : options.maxTokens) != null ? _j : this.llmConfig.maxTokens) != null ? _k : 1024,
|
|
2542
2698
|
system,
|
|
2543
2699
|
messages: anthropicMessages,
|
|
2544
2700
|
stream: true
|
|
2545
|
-
}));
|
|
2701
|
+
}, extraParams)));
|
|
2546
2702
|
if (!stream) {
|
|
2547
2703
|
throw new Error("[AnthropicProvider] messages.create stream is undefined");
|
|
2548
2704
|
}
|
|
2549
2705
|
try {
|
|
2550
2706
|
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
2551
2707
|
const chunk = temp.value;
|
|
2552
|
-
if (chunk.type === "content_block_delta"
|
|
2553
|
-
|
|
2708
|
+
if (chunk.type === "content_block_delta") {
|
|
2709
|
+
if (chunk.delta.type === "text_delta") {
|
|
2710
|
+
yield chunk.delta.text;
|
|
2711
|
+
} else if (chunk.delta.type === "thinking_delta") {
|
|
2712
|
+
yield `\0THINKING\0${chunk.delta.thinking}`;
|
|
2713
|
+
}
|
|
2554
2714
|
}
|
|
2555
2715
|
}
|
|
2556
2716
|
} catch (temp) {
|
|
@@ -2594,9 +2754,10 @@ ${context}`;
|
|
|
2594
2754
|
import axios from "axios";
|
|
2595
2755
|
var OllamaProvider = class {
|
|
2596
2756
|
constructor(llmConfig, embeddingConfig) {
|
|
2597
|
-
var _a;
|
|
2757
|
+
var _a, _b;
|
|
2598
2758
|
const baseURL = (_a = llmConfig.baseUrl) != null ? _a : "http://localhost:11434";
|
|
2599
|
-
|
|
2759
|
+
const timeout = Number((_b = llmConfig.options) == null ? void 0 : _b.timeout) || 3e5;
|
|
2760
|
+
this.http = axios.create({ baseURL, timeout });
|
|
2600
2761
|
this.llmConfig = llmConfig;
|
|
2601
2762
|
this.embeddingConfig = embeddingConfig;
|
|
2602
2763
|
}
|
|
@@ -2652,14 +2813,15 @@ var OllamaProvider = class {
|
|
|
2652
2813
|
};
|
|
2653
2814
|
}
|
|
2654
2815
|
async chat(messages, context, options) {
|
|
2655
|
-
var _a, _b, _c, _d;
|
|
2656
|
-
const
|
|
2816
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2817
|
+
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.";
|
|
2818
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2657
2819
|
const { data } = await this.http.post("/api/chat", {
|
|
2658
2820
|
model: this.llmConfig.model,
|
|
2659
2821
|
stream: false,
|
|
2660
2822
|
options: {
|
|
2661
|
-
temperature: (
|
|
2662
|
-
num_predict: (
|
|
2823
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : 0.7,
|
|
2824
|
+
num_predict: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : 1024
|
|
2663
2825
|
},
|
|
2664
2826
|
messages: [
|
|
2665
2827
|
{ role: "system", content: system },
|
|
@@ -2670,14 +2832,15 @@ var OllamaProvider = class {
|
|
|
2670
2832
|
}
|
|
2671
2833
|
chatStream(messages, context, options) {
|
|
2672
2834
|
return __asyncGenerator(this, null, function* () {
|
|
2673
|
-
var _a, _b, _c, _d, _e, _f;
|
|
2674
|
-
const
|
|
2835
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2836
|
+
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.";
|
|
2837
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2675
2838
|
const response = yield new __await(this.http.post("/api/chat", {
|
|
2676
2839
|
model: this.llmConfig.model,
|
|
2677
2840
|
stream: true,
|
|
2678
2841
|
options: {
|
|
2679
|
-
temperature: (
|
|
2680
|
-
num_predict: (
|
|
2842
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : 0.7,
|
|
2843
|
+
num_predict: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : 1024
|
|
2681
2844
|
},
|
|
2682
2845
|
messages: [
|
|
2683
2846
|
{ role: "system", content: system },
|
|
@@ -2699,7 +2862,7 @@ var OllamaProvider = class {
|
|
|
2699
2862
|
if (!line.trim()) continue;
|
|
2700
2863
|
try {
|
|
2701
2864
|
const json = JSON.parse(line);
|
|
2702
|
-
if ((
|
|
2865
|
+
if ((_g = json.message) == null ? void 0 : _g.content) {
|
|
2703
2866
|
yield json.message.content;
|
|
2704
2867
|
}
|
|
2705
2868
|
if (json.done) return;
|
|
@@ -2721,26 +2884,12 @@ var OllamaProvider = class {
|
|
|
2721
2884
|
if (lineBuffer.trim()) {
|
|
2722
2885
|
try {
|
|
2723
2886
|
const json = JSON.parse(lineBuffer);
|
|
2724
|
-
if ((
|
|
2887
|
+
if ((_h = json.message) == null ? void 0 : _h.content) yield json.message.content;
|
|
2725
2888
|
} catch (e) {
|
|
2726
2889
|
}
|
|
2727
2890
|
}
|
|
2728
2891
|
});
|
|
2729
2892
|
}
|
|
2730
|
-
buildSystemPrompt(context, overridePrompt) {
|
|
2731
|
-
var _a;
|
|
2732
|
-
if (overridePrompt) {
|
|
2733
|
-
return overridePrompt;
|
|
2734
|
-
}
|
|
2735
|
-
const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the provided context to answer the user's question.
|
|
2736
|
-
|
|
2737
|
-
Context:
|
|
2738
|
-
${context}`;
|
|
2739
|
-
return systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
|
|
2740
|
-
|
|
2741
|
-
Context:
|
|
2742
|
-
${context}`;
|
|
2743
|
-
}
|
|
2744
2893
|
async embed(text, options) {
|
|
2745
2894
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2746
2895
|
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";
|
|
@@ -2887,21 +3036,6 @@ var GeminiProvider = class {
|
|
|
2887
3036
|
var _a, _b;
|
|
2888
3037
|
return sanitizeModel((_b = optionsModel != null ? optionsModel : (_a = this.embeddingConfig) == null ? void 0 : _a.model) != null ? _b : DEFAULT_EMBEDDING_MODEL);
|
|
2889
3038
|
}
|
|
2890
|
-
/**
|
|
2891
|
-
* Build the system instruction string, inserting the RAG context either via
|
|
2892
|
-
* the `{{context}}` placeholder or by appending it.
|
|
2893
|
-
*/
|
|
2894
|
-
buildSystemInstruction(context) {
|
|
2895
|
-
var _a;
|
|
2896
|
-
const base = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Answer questions based on the provided context.
|
|
2897
|
-
|
|
2898
|
-
Context:
|
|
2899
|
-
${context}`;
|
|
2900
|
-
return base.includes("{{context}}") ? base.replace("{{context}}", context) : `${base}
|
|
2901
|
-
|
|
2902
|
-
Context:
|
|
2903
|
-
${context}`;
|
|
2904
|
-
}
|
|
2905
3039
|
/**
|
|
2906
3040
|
* Convert ChatMessage[] to the Gemini contents format.
|
|
2907
3041
|
*
|
|
@@ -2920,16 +3054,17 @@ ${context}`;
|
|
|
2920
3054
|
// ILLMProvider — chat
|
|
2921
3055
|
// -------------------------------------------------------------------------
|
|
2922
3056
|
async chat(messages, context, options) {
|
|
2923
|
-
var _a, _b, _c, _d;
|
|
3057
|
+
var _a, _b, _c, _d, _e, _f;
|
|
3058
|
+
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.";
|
|
2924
3059
|
const model = this.client.getGenerativeModel({
|
|
2925
3060
|
model: this.llmConfig.model,
|
|
2926
|
-
systemInstruction:
|
|
3061
|
+
systemInstruction: buildSystemContent(basePrompt, context)
|
|
2927
3062
|
});
|
|
2928
3063
|
const result = await model.generateContent({
|
|
2929
3064
|
contents: this.buildGeminiContents(messages),
|
|
2930
3065
|
generationConfig: {
|
|
2931
|
-
temperature: (
|
|
2932
|
-
maxOutputTokens: (
|
|
3066
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : DEFAULT_TEMPERATURE,
|
|
3067
|
+
maxOutputTokens: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : DEFAULT_MAX_TOKENS,
|
|
2933
3068
|
stopSequences: options == null ? void 0 : options.stop
|
|
2934
3069
|
}
|
|
2935
3070
|
});
|
|
@@ -2937,16 +3072,17 @@ ${context}`;
|
|
|
2937
3072
|
}
|
|
2938
3073
|
chatStream(messages, context, options) {
|
|
2939
3074
|
return __asyncGenerator(this, null, function* () {
|
|
2940
|
-
var _a, _b, _c, _d;
|
|
3075
|
+
var _a, _b, _c, _d, _e, _f;
|
|
3076
|
+
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.";
|
|
2941
3077
|
const model = this.client.getGenerativeModel({
|
|
2942
3078
|
model: this.llmConfig.model,
|
|
2943
|
-
systemInstruction:
|
|
3079
|
+
systemInstruction: buildSystemContent(basePrompt, context)
|
|
2944
3080
|
});
|
|
2945
3081
|
const result = yield new __await(model.generateContentStream({
|
|
2946
3082
|
contents: this.buildGeminiContents(messages),
|
|
2947
3083
|
generationConfig: {
|
|
2948
|
-
temperature: (
|
|
2949
|
-
maxOutputTokens: (
|
|
3084
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : DEFAULT_TEMPERATURE,
|
|
3085
|
+
maxOutputTokens: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : DEFAULT_MAX_TOKENS,
|
|
2950
3086
|
stopSequences: options == null ? void 0 : options.stop
|
|
2951
3087
|
}
|
|
2952
3088
|
}));
|
|
@@ -3342,7 +3478,9 @@ var LLMFactory = class _LLMFactory {
|
|
|
3342
3478
|
if (llmConfig.baseUrl || ((_b = llmConfig.options) == null ? void 0 : _b.baseUrl)) {
|
|
3343
3479
|
return new UniversalLLMAdapter(llmConfig);
|
|
3344
3480
|
}
|
|
3345
|
-
throw new
|
|
3481
|
+
throw new ProviderNotFoundException(
|
|
3482
|
+
"LLM",
|
|
3483
|
+
String(llmConfig.provider),
|
|
3346
3484
|
`[LLMFactory] Unknown provider "${llmConfig.provider}". Built-in providers: ${_LLMFactory.listProviders().join(", ")}. Register a custom provider with LLMFactory.register().`
|
|
3347
3485
|
);
|
|
3348
3486
|
}
|
|
@@ -3474,7 +3612,7 @@ var ProviderRegistry = class {
|
|
|
3474
3612
|
return UniversalVectorProvider2;
|
|
3475
3613
|
}
|
|
3476
3614
|
default:
|
|
3477
|
-
throw new
|
|
3615
|
+
throw new ProviderNotFoundException("vector", provider);
|
|
3478
3616
|
}
|
|
3479
3617
|
}
|
|
3480
3618
|
static async createVectorProvider(config) {
|
|
@@ -3492,12 +3630,18 @@ var ProviderRegistry = class {
|
|
|
3492
3630
|
return new SimpleGraphProvider2(config);
|
|
3493
3631
|
}
|
|
3494
3632
|
default:
|
|
3495
|
-
throw new
|
|
3633
|
+
throw new ProviderNotFoundException("graph", provider);
|
|
3496
3634
|
}
|
|
3497
3635
|
}
|
|
3498
3636
|
static createLLMProvider(llmConfig, embeddingConfig) {
|
|
3499
3637
|
return LLMFactory.create(llmConfig, embeddingConfig);
|
|
3500
3638
|
}
|
|
3639
|
+
static registerLLMProvider(name, factory) {
|
|
3640
|
+
LLMFactory.register(name, factory);
|
|
3641
|
+
}
|
|
3642
|
+
static createEmbeddingProvider(embeddingConfig) {
|
|
3643
|
+
return LLMFactory.createEmbeddingProvider(embeddingConfig);
|
|
3644
|
+
}
|
|
3501
3645
|
};
|
|
3502
3646
|
ProviderRegistry.vectorProviders = {};
|
|
3503
3647
|
ProviderRegistry.graphProviders = {};
|
|
@@ -3644,8 +3788,8 @@ var ConfigValidator = class {
|
|
|
3644
3788
|
const errorItems = errors.filter((e) => e.severity === "error");
|
|
3645
3789
|
if (errorItems.length > 0) {
|
|
3646
3790
|
const message = errorItems.map((e) => `${e.field}: ${e.message}${e.suggestion ? ` (${e.suggestion})` : ""}`).join("\n ");
|
|
3647
|
-
throw new
|
|
3648
|
-
${message}
|
|
3791
|
+
throw new ConfigurationException(`[ConfigValidator] Configuration validation failed:
|
|
3792
|
+
${message}`, errorItems);
|
|
3649
3793
|
}
|
|
3650
3794
|
}
|
|
3651
3795
|
};
|
|
@@ -3832,20 +3976,17 @@ var Reranker = class {
|
|
|
3832
3976
|
}
|
|
3833
3977
|
try {
|
|
3834
3978
|
const topN = matches.slice(0, 10);
|
|
3835
|
-
const
|
|
3836
|
-
|
|
3837
|
-
Return the result as a comma-separated list of document indices in order of relevance, from most relevant to least relevant.
|
|
3838
|
-
Use the indices provided in brackets like [0], [1], etc.
|
|
3839
|
-
Only return the indices (e.g., "2,0,1"), nothing else. Do not include brackets in the output.
|
|
3840
|
-
|
|
3841
|
-
Query: "${query}"
|
|
3979
|
+
const response = await llm.chat(
|
|
3980
|
+
[{ role: "user", content: `Query: "${query}"
|
|
3842
3981
|
|
|
3843
3982
|
Documents:
|
|
3844
|
-
${topN.map((m, i) => `[${i}] ${m.content.replace(/\n/g, " ")}`).join("\n")}
|
|
3845
|
-
const response = await llm.chat(
|
|
3846
|
-
[{ role: "user", content: prompt }],
|
|
3983
|
+
${topN.map((m, i) => `[${i}] ${m.content.replace(/\n/g, " ")}`).join("\n")}` }],
|
|
3847
3984
|
"",
|
|
3848
|
-
{
|
|
3985
|
+
{
|
|
3986
|
+
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.',
|
|
3987
|
+
temperature: 0,
|
|
3988
|
+
maxTokens: 50
|
|
3989
|
+
}
|
|
3849
3990
|
);
|
|
3850
3991
|
const cleanedResponse = response.trim().replace(/[\[\]\s]/g, "");
|
|
3851
3992
|
const rankedIndices = cleanedResponse.split(",").map(Number).filter((n) => !isNaN(n));
|
|
@@ -4351,6 +4492,18 @@ var QueryProcessor = class {
|
|
|
4351
4492
|
}, normalizedField ? { field: normalizedField } : {}));
|
|
4352
4493
|
}
|
|
4353
4494
|
};
|
|
4495
|
+
const activeValidFields = validFields.length > 0 ? validFields : ["brand", "category", "price", "rating", "stock", "stock_quantity", "status", "name", "title"];
|
|
4496
|
+
const resolveValidField = (fieldStr) => {
|
|
4497
|
+
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();
|
|
4498
|
+
const comparable = (v) => v.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
4499
|
+
const cleanedComparable = comparable(cleaned);
|
|
4500
|
+
if (!cleanedComparable) return null;
|
|
4501
|
+
const matchedField = activeValidFields.find((fieldName) => {
|
|
4502
|
+
const candidate = comparable(fieldName);
|
|
4503
|
+
return candidate === cleanedComparable || candidate.includes(cleanedComparable) || cleanedComparable.includes(candidate);
|
|
4504
|
+
});
|
|
4505
|
+
return matchedField != null ? matchedField : null;
|
|
4506
|
+
};
|
|
4354
4507
|
for (const match of question.matchAll(/["']([^"']{2,100})["']/g)) {
|
|
4355
4508
|
addHint(match[1]);
|
|
4356
4509
|
}
|
|
@@ -4375,7 +4528,13 @@ var QueryProcessor = class {
|
|
|
4375
4528
|
const field = match[1];
|
|
4376
4529
|
const value = match[2];
|
|
4377
4530
|
if (field && !this.isLikelyPromptPhrase(field)) {
|
|
4378
|
-
|
|
4531
|
+
const resolvedField = resolveValidField(field);
|
|
4532
|
+
if (resolvedField) {
|
|
4533
|
+
addHint(value, resolvedField);
|
|
4534
|
+
} else {
|
|
4535
|
+
addHint(value);
|
|
4536
|
+
addHint(field);
|
|
4537
|
+
}
|
|
4379
4538
|
}
|
|
4380
4539
|
}
|
|
4381
4540
|
if (validFields.length > 0) {
|
|
@@ -4421,12 +4580,16 @@ var QueryProcessor = class {
|
|
|
4421
4580
|
];
|
|
4422
4581
|
for (const pattern of fieldValuePatterns) {
|
|
4423
4582
|
for (const match of question.matchAll(pattern)) {
|
|
4424
|
-
const field =
|
|
4583
|
+
const field = (_c = match[1]) != null ? _c : "";
|
|
4425
4584
|
const value = (_d = match[2]) != null ? _d : "";
|
|
4426
|
-
|
|
4427
|
-
|
|
4585
|
+
const resolvedField = resolveValidField(field);
|
|
4586
|
+
if (resolvedField) {
|
|
4587
|
+
addHint(value, resolvedField);
|
|
4428
4588
|
} else {
|
|
4429
4589
|
addHint(value);
|
|
4590
|
+
if (field && !this.isLikelyPromptPhrase(field)) {
|
|
4591
|
+
addHint(field);
|
|
4592
|
+
}
|
|
4430
4593
|
}
|
|
4431
4594
|
}
|
|
4432
4595
|
}
|
|
@@ -4653,7 +4816,7 @@ var LLMRouter = class {
|
|
|
4653
4816
|
|
|
4654
4817
|
// src/utils/UITransformer.ts
|
|
4655
4818
|
init_synonyms();
|
|
4656
|
-
var UITransformer = class {
|
|
4819
|
+
var UITransformer = class _UITransformer {
|
|
4657
4820
|
// ─── Public Entry Points ─────────────────────────────────────────────────
|
|
4658
4821
|
/**
|
|
4659
4822
|
* Heuristic-only transform (no LLM required).
|
|
@@ -4715,7 +4878,7 @@ var UITransformer = class {
|
|
|
4715
4878
|
static async analyzeAndDecide(query, sources, llm) {
|
|
4716
4879
|
let intent;
|
|
4717
4880
|
try {
|
|
4718
|
-
intent = await this.detectIntent(query, llm);
|
|
4881
|
+
intent = await this.detectIntent(query, llm, sources);
|
|
4719
4882
|
console.debug("[UITransformer] Detected intent:", intent);
|
|
4720
4883
|
} catch (err) {
|
|
4721
4884
|
console.warn("[UITransformer] Intent detection failed; using heuristic.", err);
|
|
@@ -4733,11 +4896,23 @@ var UITransformer = class {
|
|
|
4733
4896
|
try {
|
|
4734
4897
|
const context = this.buildContextSummary(sources);
|
|
4735
4898
|
const systemPrompt = this.buildVisualizationSystemPrompt();
|
|
4899
|
+
const profile = this.profileData(sources);
|
|
4900
|
+
const schemaContext = `
|
|
4901
|
+
RETRIEVED DATA SCHEMA:
|
|
4902
|
+
- Numeric fields (measures): ${profile.numericFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4903
|
+
- Category fields (dimensions): ${profile.categoricalFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4904
|
+
- Date fields (temporal): ${profile.dateFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4905
|
+
- Boolean fields: ${profile.booleanFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4906
|
+
- Text/Other fields: ${profile.fields.filter((f) => f.kind === "text").map((f) => f.key).join(", ") || "None"}
|
|
4907
|
+
`;
|
|
4736
4908
|
const userPrompt = [
|
|
4737
4909
|
`USER QUESTION: ${query}`,
|
|
4738
4910
|
"",
|
|
4739
4911
|
`DETECTED INTENT (JSON): ${JSON.stringify(intent)}`,
|
|
4740
4912
|
"",
|
|
4913
|
+
`RETRIEVED DATA SCHEMA:`,
|
|
4914
|
+
schemaContext,
|
|
4915
|
+
"",
|
|
4741
4916
|
"RETRIEVED DATA (JSON):",
|
|
4742
4917
|
context
|
|
4743
4918
|
].join("\n");
|
|
@@ -4748,6 +4923,20 @@ var UITransformer = class {
|
|
|
4748
4923
|
);
|
|
4749
4924
|
const parsed = this.parseTransformationResponse(rawResponse);
|
|
4750
4925
|
if (parsed) {
|
|
4926
|
+
let isCompatible = true;
|
|
4927
|
+
if (parsed.type === "line_chart" && (profile.dateFields.length === 0 || profile.numericFields.length === 0)) {
|
|
4928
|
+
isCompatible = false;
|
|
4929
|
+
} else if (["bar_chart", "horizontal_bar", "pie_chart", "donut_chart"].includes(parsed.type) && (profile.categoricalFields.length === 0 && profile.numericFields.length === 0)) {
|
|
4930
|
+
isCompatible = false;
|
|
4931
|
+
} else if (parsed.type === "scatter_plot" && profile.numericFields.length < 2) {
|
|
4932
|
+
isCompatible = false;
|
|
4933
|
+
} else if (parsed.type === "metric_card" && profile.numericFields.length === 0) {
|
|
4934
|
+
isCompatible = false;
|
|
4935
|
+
}
|
|
4936
|
+
if (!isCompatible) {
|
|
4937
|
+
console.warn(`[UITransformer] LLM chose incompatible visualization type "${parsed.type}" for retrieved schema. Falling back to heuristic.`);
|
|
4938
|
+
return this.transform(query, sources, void 0, void 0, intent);
|
|
4939
|
+
}
|
|
4751
4940
|
const intentAllowsTable = intent.wantsExplicitTable || ["tabular", "table", "geographic"].includes(intent.visualizationHint);
|
|
4752
4941
|
const intentWantsPieLikeChart = ["pie_chart", "donut_chart"].includes(intent.recommendedChart) || ["composition", "category_breakdown"].includes(intent.visualizationHint);
|
|
4753
4942
|
if (parsed.type === "table" && !intentAllowsTable) {
|
|
@@ -4777,7 +4966,26 @@ var UITransformer = class {
|
|
|
4777
4966
|
* - The intent object can be reused across both the heuristic and LLM paths.
|
|
4778
4967
|
* - It is easy to unit-test intent detection in isolation.
|
|
4779
4968
|
*/
|
|
4780
|
-
static async detectIntent(query, llm) {
|
|
4969
|
+
static async detectIntent(query, llm, sources) {
|
|
4970
|
+
let schemaProfileText = "";
|
|
4971
|
+
if (sources && sources.length > 0) {
|
|
4972
|
+
const profile = this.profileData(sources);
|
|
4973
|
+
const hasProducts = sources.some((item) => this.isProductData(item));
|
|
4974
|
+
schemaProfileText = `
|
|
4975
|
+
RETRIEVED DATA SCHEMA:
|
|
4976
|
+
- Numeric fields (measures): ${profile.numericFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4977
|
+
- Category fields (dimensions): ${profile.categoricalFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4978
|
+
- Date fields (temporal): ${profile.dateFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4979
|
+
- Boolean fields: ${profile.booleanFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4980
|
+
- Text fields: ${profile.fields.filter((f) => f.kind === "text").map((f) => f.key).join(", ") || "None"}
|
|
4981
|
+
|
|
4982
|
+
Please choose visualizationHint and recommendedChart dynamically based on the available schema.
|
|
4983
|
+
- Do NOT recommend "trend" or "line_chart" if there are no Date fields.
|
|
4984
|
+
- 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.
|
|
4985
|
+
- If there are multiple metadata fields but no clear categories or numbers, "tabular"/"table" is a highly accurate default.
|
|
4986
|
+
${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".` : ""}
|
|
4987
|
+
`;
|
|
4988
|
+
}
|
|
4781
4989
|
const systemPrompt = `You are an intent classifier for a product-search RAG system.
|
|
4782
4990
|
Given a user query, return ONLY a valid JSON object with this exact shape \u2014 no prose, no markdown:
|
|
4783
4991
|
|
|
@@ -4813,8 +5021,11 @@ RULES:
|
|
|
4813
5021
|
- isTemporal: true if time words appear (trend, historical, over time, last year, monthly, etc.)
|
|
4814
5022
|
- isComparison: true if user compares, ranks, or contrasts entities or categories.
|
|
4815
5023
|
- language: detect from the query text itself; default "en" if uncertain.`;
|
|
5024
|
+
const userPrompt = `QUERY: ${query}${schemaProfileText ? `
|
|
5025
|
+
|
|
5026
|
+
${schemaProfileText}` : ""}`;
|
|
4816
5027
|
const rawResponse = await llm.chat(
|
|
4817
|
-
[{ role: "user", content:
|
|
5028
|
+
[{ role: "user", content: userPrompt }],
|
|
4818
5029
|
"",
|
|
4819
5030
|
{ systemPrompt, temperature: 0 }
|
|
4820
5031
|
);
|
|
@@ -5257,10 +5468,21 @@ RULES:
|
|
|
5257
5468
|
const hasEntityTerms = /\b(organization|organisations?|organizations?|company|companies|employee|employees|staff|headcount|workforce)\b/.test(q);
|
|
5258
5469
|
return asksForRecords && (hasNumericPredicate || hasEntityTerms);
|
|
5259
5470
|
}
|
|
5471
|
+
/** @internal kept private for transform() internal logic */
|
|
5260
5472
|
static isProductQuery(query) {
|
|
5473
|
+
return _UITransformer._productQueryTest(query);
|
|
5474
|
+
}
|
|
5475
|
+
/**
|
|
5476
|
+
* Public entry-point for external callers (e.g. Pipeline)
|
|
5477
|
+
* to check whether a query maps to product_browse without triggering an LLM call.
|
|
5478
|
+
*/
|
|
5479
|
+
static isProductQueryPublic(query) {
|
|
5480
|
+
return _UITransformer._productQueryTest(query);
|
|
5481
|
+
}
|
|
5482
|
+
static _productQueryTest(query) {
|
|
5261
5483
|
const q = query.toLowerCase();
|
|
5262
5484
|
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);
|
|
5263
|
-
const productAction = /\b(recommend|suggest|describe|description|detail|details|about|show|find|search|browse|list)\b/.test(q);
|
|
5485
|
+
const productAction = /\b(recommend|suggest|describe|description|detail|details|about|show|find|search|browse|list|explore|exploring)\b/.test(q);
|
|
5264
5486
|
const nonProductEntityTerms = /\b(organization|organisations?|organizations?|company|companies|employee|employees|staff|headcount|workforce)\b/.test(q);
|
|
5265
5487
|
return productTerms && productAction && !nonProductEntityTerms;
|
|
5266
5488
|
}
|
|
@@ -6033,23 +6255,19 @@ function estimateCostUsd(promptTokens, completionTokens, model) {
|
|
|
6033
6255
|
async function scoreHallucination(llm, answer, context) {
|
|
6034
6256
|
const maxContextChars = 3e3;
|
|
6035
6257
|
const truncatedContext = context.length > maxContextChars ? context.slice(0, maxContextChars) + "\n...[truncated]" : context;
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
CONTEXT:
|
|
6258
|
+
try {
|
|
6259
|
+
const raw = await llm.chat(
|
|
6260
|
+
[{ role: "user", content: `CONTEXT:
|
|
6039
6261
|
${truncatedContext}
|
|
6040
6262
|
|
|
6041
6263
|
ANSWER:
|
|
6042
|
-
${answer}
|
|
6043
|
-
|
|
6044
|
-
Return ONLY a valid JSON object with no markdown fences:
|
|
6045
|
-
{"score": <float 0-1>, "reason": "<one sentence>"}
|
|
6046
|
-
|
|
6047
|
-
Where score 0 = fully grounded in context, score 1 = likely hallucinated or unsupported.`;
|
|
6048
|
-
try {
|
|
6049
|
-
const raw = await llm.chat(
|
|
6050
|
-
[{ role: "user", content: prompt }],
|
|
6264
|
+
${answer}` }],
|
|
6051
6265
|
"",
|
|
6052
|
-
{
|
|
6266
|
+
{
|
|
6267
|
+
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.',
|
|
6268
|
+
temperature: 0,
|
|
6269
|
+
maxTokens: 120
|
|
6270
|
+
}
|
|
6053
6271
|
);
|
|
6054
6272
|
const jsonMatch = raw.match(/\{[\s\S]*\}/);
|
|
6055
6273
|
if (!jsonMatch) return void 0;
|
|
@@ -6109,7 +6327,10 @@ var Pipeline = class {
|
|
|
6109
6327
|
- 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.
|
|
6110
6328
|
- Do NOT try to format product lists as tables.
|
|
6111
6329
|
`;
|
|
6112
|
-
this.config.llm.systemPrompt
|
|
6330
|
+
const userPromptPrefix = this.config.llm.systemPrompt ? `${this.config.llm.systemPrompt}
|
|
6331
|
+
|
|
6332
|
+
` : "";
|
|
6333
|
+
this.config.llm.systemPrompt = `${userPromptPrefix}${chartInstruction}`;
|
|
6113
6334
|
this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
|
|
6114
6335
|
this.llmRouter = new LLMRouter(this.config);
|
|
6115
6336
|
const { llmProvider: resolvedLLM, embeddingProvider } = await EmbeddingStrategyResolver.resolve(
|
|
@@ -6287,11 +6508,11 @@ var Pipeline = class {
|
|
|
6287
6508
|
*/
|
|
6288
6509
|
askStream(_0) {
|
|
6289
6510
|
return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
|
|
6290
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
6511
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
|
|
6291
6512
|
yield new __await(this.initialize());
|
|
6292
6513
|
const ns = namespace != null ? namespace : this.config.projectId;
|
|
6293
6514
|
const topK = (_b = (_a = this.config.rag) == null ? void 0 : _a.topK) != null ? _b : 5;
|
|
6294
|
-
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0;
|
|
6515
|
+
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0.3;
|
|
6295
6516
|
const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
6296
6517
|
const requestStart = performance.now();
|
|
6297
6518
|
try {
|
|
@@ -6310,12 +6531,13 @@ var Pipeline = class {
|
|
|
6310
6531
|
const embedStart = performance.now();
|
|
6311
6532
|
const cacheKey = `${ns}::${searchQuery}`;
|
|
6312
6533
|
const cachedVector = this.embeddingCache.get(cacheKey);
|
|
6534
|
+
const useGraph = this.graphDB && ((_h = this.config.rag) == null ? void 0 : _h.useGraphRetrieval);
|
|
6313
6535
|
const [strategyResult, embeddedVector] = yield new __await(Promise.all([
|
|
6314
6536
|
QueryProcessor.determineRetrievalStrategy(
|
|
6315
6537
|
searchQuery,
|
|
6316
|
-
this.llmRouter.get("fast"),
|
|
6317
|
-
(
|
|
6318
|
-
(
|
|
6538
|
+
useGraph ? this.llmRouter.get("fast") : void 0,
|
|
6539
|
+
(_i = this.config.rag) == null ? void 0 : _i.graphKeywords,
|
|
6540
|
+
(_j = this.config.rag) == null ? void 0 : _j.vectorKeywords
|
|
6319
6541
|
),
|
|
6320
6542
|
// Embed immediately regardless of strategy — costs nothing if cached.
|
|
6321
6543
|
// If strategy turns out to be 'graph'-only we just won't use the vector.
|
|
@@ -6325,7 +6547,7 @@ var Pipeline = class {
|
|
|
6325
6547
|
if (!cachedVector && queryVector.length > 0) {
|
|
6326
6548
|
this.embeddingCache.set(cacheKey, queryVector);
|
|
6327
6549
|
}
|
|
6328
|
-
const graphData = (strategyResult === "graph" || strategyResult === "both") && this.graphDB && ((
|
|
6550
|
+
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;
|
|
6329
6551
|
const hasMetadataFilter = filter.metadata && Object.keys(filter.metadata).length > 0;
|
|
6330
6552
|
const hasNumericPredicates = Array.isArray(filter.__numericPredicates) && filter.__numericPredicates.length > 0;
|
|
6331
6553
|
const wantsExhaustiveList = hasNumericPredicates || hasMetadataFilter || /\b(list|all|show|provide|give|get|browse|find|search|display)\b/i.test(question);
|
|
@@ -6338,7 +6560,7 @@ var Pipeline = class {
|
|
|
6338
6560
|
const structuredSources = this.applyStructuredFilters(rawSources, filter);
|
|
6339
6561
|
let fullSources = hasNumericPredicates ? structuredSources : structuredSources.filter((m) => m.score >= scoreThreshold);
|
|
6340
6562
|
const rerankLimit = wantsExhaustiveList ? retrievalLimit : topK;
|
|
6341
|
-
if (!hasNumericPredicates && ((
|
|
6563
|
+
if (!hasNumericPredicates && ((_l = this.config.rag) == null ? void 0 : _l.useReranking)) {
|
|
6342
6564
|
fullSources = yield new __await(this.reranker.rerank(fullSources, question, rerankLimit));
|
|
6343
6565
|
} else if (!wantsExhaustiveList) {
|
|
6344
6566
|
fullSources = fullSources.slice(0, topK);
|
|
@@ -6350,7 +6572,8 @@ ${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
|
|
|
6350
6572
|
if (hasMetadataFilter) {
|
|
6351
6573
|
displayCount = fullSources.length;
|
|
6352
6574
|
} else {
|
|
6353
|
-
const
|
|
6575
|
+
const baseThreshold = Math.max(scoreThreshold, 0.4);
|
|
6576
|
+
const highlyRelevant = fullSources.filter((m) => m.score >= baseThreshold);
|
|
6354
6577
|
displayCount = Math.max(highlyRelevant.length, topK);
|
|
6355
6578
|
if (displayCount > 15) {
|
|
6356
6579
|
displayCount = 15;
|
|
@@ -6368,34 +6591,117 @@ VECTOR CONTEXT:
|
|
|
6368
6591
|
${context}`;
|
|
6369
6592
|
}
|
|
6370
6593
|
const allMetadataKeys = Array.from(new Set(sources.flatMap((s) => Object.keys(s.metadata || {}))));
|
|
6371
|
-
const
|
|
6372
|
-
|
|
6373
|
-
(
|
|
6594
|
+
const cachedSchema = allMetadataKeys.length > 0 ? SchemaMapper.getCached(ns, allMetadataKeys) : void 0;
|
|
6595
|
+
if (allMetadataKeys.length > 0 && !cachedSchema) {
|
|
6596
|
+
SchemaMapper.train(this.llmProvider, ns, allMetadataKeys).catch(() => {
|
|
6597
|
+
});
|
|
6598
|
+
}
|
|
6599
|
+
const isProductQ = UITransformer.isProductQueryPublic(question);
|
|
6600
|
+
const uiTransformationPromise = isProductQ ? Promise.resolve(
|
|
6601
|
+
UITransformer.transform(
|
|
6374
6602
|
question,
|
|
6375
6603
|
sources,
|
|
6376
|
-
|
|
6377
|
-
|
|
6604
|
+
this.config,
|
|
6605
|
+
cachedSchema,
|
|
6606
|
+
{ visualizationHint: "product_browse", recommendedChart: "text", filterInStockOnly: false, wantsExplicitTable: false, isTemporal: false, isComparison: false, language: "en" }
|
|
6378
6607
|
)
|
|
6608
|
+
) : this.generateUiTransformation(
|
|
6609
|
+
question,
|
|
6610
|
+
sources,
|
|
6611
|
+
cachedSchema,
|
|
6612
|
+
hasNumericPredicates
|
|
6379
6613
|
).catch((uiError) => {
|
|
6380
6614
|
console.warn("[Pipeline] UI transformation failed concurrently:", uiError);
|
|
6381
|
-
return UITransformer.transform(question, sources, this.config);
|
|
6615
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6382
6616
|
});
|
|
6617
|
+
let hallucinationScoringPromise = Promise.resolve(void 0);
|
|
6383
6618
|
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.)";
|
|
6619
|
+
const isClaude37 = this.config.llm.model.includes("claude-3-7-sonnet");
|
|
6620
|
+
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);
|
|
6621
|
+
const modelNameLower = this.config.llm.model.toLowerCase();
|
|
6622
|
+
const isReasoningModel = modelNameLower.includes("-r1") || modelNameLower.includes("deepseek-r1") || modelNameLower.includes("reasoning") || modelNameLower.includes("thinking");
|
|
6623
|
+
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);
|
|
6624
|
+
let finalRestrictionSuffix = restrictionSuffix;
|
|
6625
|
+
if (isSimulatedThinking) {
|
|
6626
|
+
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.`)";
|
|
6627
|
+
}
|
|
6384
6628
|
const hardenedHistory = [...history];
|
|
6385
|
-
const userQuestion = { role: "user", content: question +
|
|
6629
|
+
const userQuestion = { role: "user", content: question + finalRestrictionSuffix };
|
|
6386
6630
|
const messages = [...hardenedHistory, userQuestion];
|
|
6387
|
-
const systemPrompt = (
|
|
6631
|
+
const systemPrompt = (_s = this.config.llm.systemPrompt) != null ? _s : "";
|
|
6388
6632
|
const userPrompt = messages.map((m) => `${m.role}: ${m.content}`).join("\n");
|
|
6389
6633
|
let fullReply = "";
|
|
6634
|
+
let thinkingText = "";
|
|
6635
|
+
let thinkingStartMs = performance.now();
|
|
6636
|
+
let thinkingDurationMs = 0;
|
|
6390
6637
|
const generateStart = performance.now();
|
|
6391
6638
|
if (this.llmProvider.chatStream) {
|
|
6392
6639
|
const stream = this.llmProvider.chatStream(messages, context);
|
|
6393
6640
|
if (!stream) throw new Error(`[Pipeline] ${this.config.llm.provider} chatStream returned undefined`);
|
|
6641
|
+
let inThinking = false;
|
|
6642
|
+
let textBuffer = "";
|
|
6394
6643
|
try {
|
|
6395
6644
|
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
6396
6645
|
const chunk = temp.value;
|
|
6397
|
-
|
|
6398
|
-
|
|
6646
|
+
if (chunk.startsWith("\0THINKING\0")) {
|
|
6647
|
+
const thinkingDelta = chunk.slice("\0THINKING\0".length);
|
|
6648
|
+
thinkingText += thinkingDelta;
|
|
6649
|
+
thinkingDurationMs = performance.now() - thinkingStartMs;
|
|
6650
|
+
yield { type: "thinking", text: thinkingDelta };
|
|
6651
|
+
continue;
|
|
6652
|
+
}
|
|
6653
|
+
if (isSimulatedThinking) {
|
|
6654
|
+
textBuffer += chunk;
|
|
6655
|
+
while (textBuffer.length > 0) {
|
|
6656
|
+
if (!inThinking) {
|
|
6657
|
+
const thinkIndex = textBuffer.indexOf("<think>");
|
|
6658
|
+
if (thinkIndex !== -1) {
|
|
6659
|
+
const before = textBuffer.slice(0, thinkIndex);
|
|
6660
|
+
if (before) {
|
|
6661
|
+
fullReply += before;
|
|
6662
|
+
yield before;
|
|
6663
|
+
}
|
|
6664
|
+
inThinking = true;
|
|
6665
|
+
thinkingStartMs = performance.now();
|
|
6666
|
+
textBuffer = textBuffer.slice(thinkIndex + "<think>".length);
|
|
6667
|
+
} else {
|
|
6668
|
+
const maxSafeLength = textBuffer.length - "<think>".length;
|
|
6669
|
+
if (maxSafeLength > 0) {
|
|
6670
|
+
const safeText = textBuffer.slice(0, maxSafeLength);
|
|
6671
|
+
fullReply += safeText;
|
|
6672
|
+
yield safeText;
|
|
6673
|
+
textBuffer = textBuffer.slice(maxSafeLength);
|
|
6674
|
+
}
|
|
6675
|
+
break;
|
|
6676
|
+
}
|
|
6677
|
+
} else {
|
|
6678
|
+
const endThinkIndex = textBuffer.indexOf("</think>");
|
|
6679
|
+
if (endThinkIndex !== -1) {
|
|
6680
|
+
const thinkingDelta = textBuffer.slice(0, endThinkIndex);
|
|
6681
|
+
if (thinkingDelta) {
|
|
6682
|
+
thinkingText += thinkingDelta;
|
|
6683
|
+
thinkingDurationMs = performance.now() - thinkingStartMs;
|
|
6684
|
+
yield { type: "thinking", text: thinkingDelta };
|
|
6685
|
+
}
|
|
6686
|
+
inThinking = false;
|
|
6687
|
+
textBuffer = textBuffer.slice(endThinkIndex + "</think>".length);
|
|
6688
|
+
} else {
|
|
6689
|
+
const maxSafeLength = textBuffer.length - "</think>".length;
|
|
6690
|
+
if (maxSafeLength > 0) {
|
|
6691
|
+
const thinkingDelta = textBuffer.slice(0, maxSafeLength);
|
|
6692
|
+
thinkingText += thinkingDelta;
|
|
6693
|
+
thinkingDurationMs = performance.now() - thinkingStartMs;
|
|
6694
|
+
yield { type: "thinking", text: thinkingDelta };
|
|
6695
|
+
textBuffer = textBuffer.slice(maxSafeLength);
|
|
6696
|
+
}
|
|
6697
|
+
break;
|
|
6698
|
+
}
|
|
6699
|
+
}
|
|
6700
|
+
}
|
|
6701
|
+
} else {
|
|
6702
|
+
fullReply += chunk;
|
|
6703
|
+
yield chunk;
|
|
6704
|
+
}
|
|
6399
6705
|
}
|
|
6400
6706
|
} catch (temp) {
|
|
6401
6707
|
error = [temp];
|
|
@@ -6407,17 +6713,42 @@ ${context}`;
|
|
|
6407
6713
|
throw error[0];
|
|
6408
6714
|
}
|
|
6409
6715
|
}
|
|
6716
|
+
if (isSimulatedThinking && textBuffer.length > 0) {
|
|
6717
|
+
if (inThinking) {
|
|
6718
|
+
thinkingText += textBuffer;
|
|
6719
|
+
yield { type: "thinking", text: textBuffer };
|
|
6720
|
+
} else {
|
|
6721
|
+
fullReply += textBuffer;
|
|
6722
|
+
yield textBuffer;
|
|
6723
|
+
}
|
|
6724
|
+
}
|
|
6410
6725
|
} else {
|
|
6411
6726
|
const reply = yield new __await(this.llmProvider.chat(messages, context));
|
|
6412
|
-
|
|
6413
|
-
|
|
6727
|
+
if (isSimulatedThinking) {
|
|
6728
|
+
const thinkStart = reply.indexOf("<think>");
|
|
6729
|
+
const thinkEnd = reply.indexOf("</think>");
|
|
6730
|
+
if (thinkStart !== -1 && thinkEnd !== -1 && thinkEnd > thinkStart) {
|
|
6731
|
+
thinkingText = reply.slice(thinkStart + "<think>".length, thinkEnd);
|
|
6732
|
+
fullReply = reply.slice(0, thinkStart) + reply.slice(thinkEnd + "</think>".length);
|
|
6733
|
+
thinkingDurationMs = 100;
|
|
6734
|
+
} else {
|
|
6735
|
+
fullReply = reply;
|
|
6736
|
+
}
|
|
6737
|
+
} else {
|
|
6738
|
+
fullReply = reply;
|
|
6739
|
+
}
|
|
6740
|
+
yield fullReply;
|
|
6741
|
+
}
|
|
6742
|
+
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;
|
|
6743
|
+
if (runHallucination) {
|
|
6744
|
+
hallucinationScoringPromise = scoreHallucination(this.llmProvider, fullReply, context).catch(() => void 0);
|
|
6414
6745
|
}
|
|
6415
6746
|
const generateMs = performance.now() - generateStart;
|
|
6416
6747
|
const totalMs = performance.now() - requestStart;
|
|
6417
6748
|
const latency = {
|
|
6418
6749
|
embedMs: Math.round(embedMs),
|
|
6419
6750
|
retrieveMs: Math.round(retrieveMs),
|
|
6420
|
-
rerankMs: ((
|
|
6751
|
+
rerankMs: ((_v = this.config.rag) == null ? void 0 : _v.useReranking) ? Math.round(rerankMs) : void 0,
|
|
6421
6752
|
generateMs: Math.round(generateMs),
|
|
6422
6753
|
totalMs: Math.round(totalMs)
|
|
6423
6754
|
};
|
|
@@ -6430,12 +6761,16 @@ ${context}`;
|
|
|
6430
6761
|
totalTokens: promptTokens + completionTokens,
|
|
6431
6762
|
estimatedCostUsd: estimateCostUsd(promptTokens, completionTokens, this.config.llm.model)
|
|
6432
6763
|
};
|
|
6433
|
-
const
|
|
6764
|
+
const [uiTransformation, hallucinationResult] = yield new __await(Promise.all([
|
|
6765
|
+
uiTransformationPromise,
|
|
6766
|
+
hallucinationScoringPromise
|
|
6767
|
+
]));
|
|
6768
|
+
const trace = __spreadValues({
|
|
6434
6769
|
requestId,
|
|
6435
6770
|
query: question,
|
|
6436
6771
|
rewrittenQuery,
|
|
6437
6772
|
systemPrompt,
|
|
6438
|
-
userPrompt: question +
|
|
6773
|
+
userPrompt: question + finalRestrictionSuffix,
|
|
6439
6774
|
chunks: sources.map((s) => {
|
|
6440
6775
|
var _a2;
|
|
6441
6776
|
return {
|
|
@@ -6449,22 +6784,19 @@ ${context}`;
|
|
|
6449
6784
|
latency,
|
|
6450
6785
|
tokens,
|
|
6451
6786
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
6452
|
-
}
|
|
6453
|
-
|
|
6787
|
+
}, hallucinationResult ? {
|
|
6788
|
+
hallucinationScore: hallucinationResult.score,
|
|
6789
|
+
hallucinationReason: hallucinationResult.reason
|
|
6790
|
+
} : {});
|
|
6454
6791
|
yield {
|
|
6455
6792
|
reply: "",
|
|
6456
6793
|
sources,
|
|
6457
6794
|
graphData,
|
|
6458
6795
|
ui_transformation: uiTransformation,
|
|
6459
|
-
trace
|
|
6796
|
+
trace,
|
|
6797
|
+
thinking: thinkingText || void 0,
|
|
6798
|
+
thinkingMs: thinkingDurationMs > 0 ? Math.round(thinkingDurationMs) : void 0
|
|
6460
6799
|
};
|
|
6461
|
-
scoreHallucination(this.llmProvider, fullReply, context).then((hallucinationResult) => {
|
|
6462
|
-
if (hallucinationResult) {
|
|
6463
|
-
trace.hallucinationScore = hallucinationResult.score;
|
|
6464
|
-
trace.hallucinationReason = hallucinationResult.reason;
|
|
6465
|
-
}
|
|
6466
|
-
}).catch(() => {
|
|
6467
|
-
});
|
|
6468
6800
|
} catch (error2) {
|
|
6469
6801
|
throw new Error(`[Pipeline] Stream failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
6470
6802
|
}
|
|
@@ -6474,18 +6806,30 @@ ${context}`;
|
|
|
6474
6806
|
* Universal retrieval method combining all enabled providers.
|
|
6475
6807
|
* Uses an LRU-bounded embedding cache to avoid re-embedding the same query.
|
|
6476
6808
|
*/
|
|
6477
|
-
async generateUiTransformation(question, sources,
|
|
6809
|
+
async generateUiTransformation(question, sources, cachedSchema, forceDeterministic = false) {
|
|
6810
|
+
var _a;
|
|
6478
6811
|
if (!sources || sources.length === 0) {
|
|
6479
|
-
return UITransformer.transform(question, sources, this.config,
|
|
6812
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6480
6813
|
}
|
|
6481
|
-
if (
|
|
6482
|
-
return UITransformer.transform(
|
|
6814
|
+
if (UITransformer.isProductQueryPublic(question)) {
|
|
6815
|
+
return UITransformer.transform(
|
|
6816
|
+
question,
|
|
6817
|
+
sources,
|
|
6818
|
+
this.config,
|
|
6819
|
+
cachedSchema,
|
|
6820
|
+
{ visualizationHint: "product_browse", recommendedChart: "text", filterInStockOnly: false, wantsExplicitTable: false, isTemporal: false, isComparison: false, language: "en" }
|
|
6821
|
+
);
|
|
6822
|
+
}
|
|
6823
|
+
const isLocalProvider = this.config.llm.provider === "ollama";
|
|
6824
|
+
const disableLlmUiTransform = ((_a = this.config.llm.options) == null ? void 0 : _a.disableLlmUiTransform) === true;
|
|
6825
|
+
if (forceDeterministic || isLocalProvider || disableLlmUiTransform) {
|
|
6826
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6483
6827
|
}
|
|
6484
6828
|
try {
|
|
6485
6829
|
return await UITransformer.analyzeAndDecide(question, sources, this.llmRouter.get("fast"));
|
|
6486
6830
|
} catch (err) {
|
|
6487
6831
|
console.warn("[Pipeline] generateUiTransformation failed, using heuristic fallback:", err);
|
|
6488
|
-
return UITransformer.transform(question, sources, this.config,
|
|
6832
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6489
6833
|
}
|
|
6490
6834
|
}
|
|
6491
6835
|
applyStructuredFilters(sources, filter) {
|
|
@@ -6569,25 +6913,29 @@ ${context}`;
|
|
|
6569
6913
|
return Number.isFinite(numeric) ? numeric : null;
|
|
6570
6914
|
}
|
|
6571
6915
|
async retrieve(query, options) {
|
|
6572
|
-
var _a, _b, _c, _d, _e, _f;
|
|
6916
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
6573
6917
|
const ns = (_a = options.namespace) != null ? _a : this.config.projectId;
|
|
6574
6918
|
const topK = (_b = options.topK) != null ? _b : 5;
|
|
6575
6919
|
const cacheKey = `${ns}::${query}`;
|
|
6576
6920
|
let queryVector = this.embeddingCache.get(cacheKey);
|
|
6577
|
-
const
|
|
6921
|
+
const useGraph = this.graphDB && ((_c = this.config.rag) == null ? void 0 : _c.useGraphRetrieval);
|
|
6922
|
+
const strategy = await QueryProcessor.determineRetrievalStrategy(
|
|
6923
|
+
query,
|
|
6924
|
+
useGraph ? this.llmRouter.get("fast") : void 0
|
|
6925
|
+
);
|
|
6578
6926
|
console.debug(`[Pipeline] Determined retrieval strategy: ${strategy}`);
|
|
6579
6927
|
const [retrievedVector, graphData] = await Promise.all([
|
|
6580
6928
|
// Only embed if we need vector search (strategy is 'vector' or 'both')
|
|
6581
6929
|
strategy === "vector" || strategy === "both" ? queryVector ? Promise.resolve(queryVector) : this.embeddingProvider.embed(query, { taskType: "query" }) : Promise.resolve([]),
|
|
6582
6930
|
// Only query graph if we need graph search (strategy is 'graph' or 'both')
|
|
6583
|
-
(strategy === "graph" || strategy === "both") && this.graphDB && ((
|
|
6931
|
+
(strategy === "graph" || strategy === "both") && this.graphDB && ((_d = this.config.rag) == null ? void 0 : _d.useGraphRetrieval) ? this.graphDB.query(query) : Promise.resolve(void 0)
|
|
6584
6932
|
]);
|
|
6585
6933
|
if ((strategy === "vector" || strategy === "both") && !queryVector && retrievedVector && retrievedVector.length > 0) {
|
|
6586
6934
|
this.embeddingCache.set(cacheKey, retrievedVector);
|
|
6587
6935
|
queryVector = retrievedVector;
|
|
6588
6936
|
}
|
|
6589
|
-
const baseFilter = __spreadProps(__spreadValues({}, (
|
|
6590
|
-
const numericPredicates = QueryProcessor.extractNumericPredicates(query, (
|
|
6937
|
+
const baseFilter = __spreadProps(__spreadValues({}, (_e = options.filter) != null ? _e : {}), { queryText: query });
|
|
6938
|
+
const numericPredicates = QueryProcessor.extractNumericPredicates(query, (_f = this.config.rag) == null ? void 0 : _f.filterableFields);
|
|
6591
6939
|
if (numericPredicates.length > 0) {
|
|
6592
6940
|
baseFilter.__numericPredicates = numericPredicates;
|
|
6593
6941
|
}
|
|
@@ -6598,7 +6946,7 @@ ${context}`;
|
|
|
6598
6946
|
) : [];
|
|
6599
6947
|
const resolvedSources = [];
|
|
6600
6948
|
for (const source of sources) {
|
|
6601
|
-
const parentId = (
|
|
6949
|
+
const parentId = (_g = source.metadata) == null ? void 0 : _g.parent_id;
|
|
6602
6950
|
if (parentId) {
|
|
6603
6951
|
console.log(`[Pipeline] Multi-Vector: Found child chunk. Parent ID: ${parentId}`);
|
|
6604
6952
|
resolvedSources.push(source);
|
|
@@ -6621,10 +6969,13 @@ New Question: ${question}
|
|
|
6621
6969
|
Optimized Search Query:`;
|
|
6622
6970
|
const rewrite = await this.llmProvider.chat(
|
|
6623
6971
|
[
|
|
6624
|
-
{ role: "system", content: "You are an assistant that optimizes search queries for RAG systems." },
|
|
6625
6972
|
{ role: "user", content: prompt }
|
|
6626
6973
|
],
|
|
6627
|
-
""
|
|
6974
|
+
"",
|
|
6975
|
+
{
|
|
6976
|
+
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.",
|
|
6977
|
+
temperature: 0
|
|
6978
|
+
}
|
|
6628
6979
|
);
|
|
6629
6980
|
return rewrite.trim() || question;
|
|
6630
6981
|
}
|
|
@@ -6648,10 +6999,13 @@ ${context}
|
|
|
6648
6999
|
Suggestions:`;
|
|
6649
7000
|
const response = await this.llmProvider.chat(
|
|
6650
7001
|
[
|
|
6651
|
-
{ role: "system", content: "You are a helpful assistant that generates search suggestions." },
|
|
6652
7002
|
{ role: "user", content: prompt }
|
|
6653
7003
|
],
|
|
6654
|
-
""
|
|
7004
|
+
"",
|
|
7005
|
+
{
|
|
7006
|
+
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.",
|
|
7007
|
+
temperature: 0
|
|
7008
|
+
}
|
|
6655
7009
|
);
|
|
6656
7010
|
const match = response.match(/\[[\s\S]*\]/);
|
|
6657
7011
|
if (match) {
|
|
@@ -6990,6 +7344,10 @@ function createStreamHandler(configOrPlugin) {
|
|
|
6990
7344
|
if (!isActive) break;
|
|
6991
7345
|
if (typeof chunk === "string") {
|
|
6992
7346
|
enqueue(sseTextFrame(chunk));
|
|
7347
|
+
} else if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "thinking") {
|
|
7348
|
+
enqueue(`data: ${JSON.stringify(chunk)}
|
|
7349
|
+
|
|
7350
|
+
`);
|
|
6993
7351
|
} else {
|
|
6994
7352
|
enqueue(sseMetaFrame(chunk));
|
|
6995
7353
|
const responseChunk = chunk;
|