@farukada/langchain-ts-rms 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +446 -0
- package/dist/app/freshness/evaluator.d.ts +23 -0
- package/dist/app/freshness/evaluator.d.ts.map +1 -0
- package/dist/app/freshness/evaluator.js +72 -0
- package/dist/app/freshness/evaluator.js.map +1 -0
- package/dist/app/governance/guardrails.d.ts +51 -0
- package/dist/app/governance/guardrails.d.ts.map +1 -0
- package/dist/app/governance/guardrails.js +68 -0
- package/dist/app/governance/guardrails.js.map +1 -0
- package/dist/app/graph/workflow.d.ts +1159 -0
- package/dist/app/graph/workflow.d.ts.map +1 -0
- package/dist/app/graph/workflow.js +468 -0
- package/dist/app/graph/workflow.js.map +1 -0
- package/dist/app/queryPlanning/planner.d.ts +18 -0
- package/dist/app/queryPlanning/planner.d.ts.map +1 -0
- package/dist/app/queryPlanning/planner.js +80 -0
- package/dist/app/queryPlanning/planner.js.map +1 -0
- package/dist/app/queryRewriting/rewriter.d.ts +32 -0
- package/dist/app/queryRewriting/rewriter.d.ts.map +1 -0
- package/dist/app/queryRewriting/rewriter.js +111 -0
- package/dist/app/queryRewriting/rewriter.js.map +1 -0
- package/dist/app/reranking/reranker.d.ts +27 -0
- package/dist/app/reranking/reranker.d.ts.map +1 -0
- package/dist/app/reranking/reranker.js +67 -0
- package/dist/app/reranking/reranker.js.map +1 -0
- package/dist/app/state/schema.d.ts +121 -0
- package/dist/app/state/schema.d.ts.map +1 -0
- package/dist/app/state/schema.js +88 -0
- package/dist/app/state/schema.js.map +1 -0
- package/dist/app/summarization/summarizationSchema.d.ts +34 -0
- package/dist/app/summarization/summarizationSchema.d.ts.map +1 -0
- package/dist/app/summarization/summarizationSchema.js +65 -0
- package/dist/app/summarization/summarizationSchema.js.map +1 -0
- package/dist/app/summarization/summarizer.d.ts +51 -0
- package/dist/app/summarization/summarizer.d.ts.map +1 -0
- package/dist/app/summarization/summarizer.js +181 -0
- package/dist/app/summarization/summarizer.js.map +1 -0
- package/dist/app/summarization/synthesisSchema.d.ts +16 -0
- package/dist/app/summarization/synthesisSchema.d.ts.map +1 -0
- package/dist/app/summarization/synthesisSchema.js +43 -0
- package/dist/app/summarization/synthesisSchema.js.map +1 -0
- package/dist/app/summarization/synthesizer.d.ts +21 -0
- package/dist/app/summarization/synthesizer.d.ts.map +1 -0
- package/dist/app/summarization/synthesizer.js +86 -0
- package/dist/app/summarization/synthesizer.js.map +1 -0
- package/dist/config/env.d.ts +49 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +54 -0
- package/dist/config/env.js.map +1 -0
- package/dist/domain/contracts.d.ts +59 -0
- package/dist/domain/contracts.d.ts.map +1 -0
- package/dist/domain/contracts.js +52 -0
- package/dist/domain/contracts.js.map +1 -0
- package/dist/domain/ports.d.ts +63 -0
- package/dist/domain/ports.d.ts.map +1 -0
- package/dist/domain/ports.js +2 -0
- package/dist/domain/ports.js.map +1 -0
- package/dist/domain/researchUtils.d.ts +51 -0
- package/dist/domain/researchUtils.d.ts.map +1 -0
- package/dist/domain/researchUtils.js +85 -0
- package/dist/domain/researchUtils.js.map +1 -0
- package/dist/infra/chat/chatModelProvider.d.ts +4 -0
- package/dist/infra/chat/chatModelProvider.d.ts.map +1 -0
- package/dist/infra/chat/chatModelProvider.js +13 -0
- package/dist/infra/chat/chatModelProvider.js.map +1 -0
- package/dist/infra/checkpoint/checkpointerFactory.d.ts +38 -0
- package/dist/infra/checkpoint/checkpointerFactory.d.ts.map +1 -0
- package/dist/infra/checkpoint/checkpointerFactory.js +54 -0
- package/dist/infra/checkpoint/checkpointerFactory.js.map +1 -0
- package/dist/infra/content/contentExtractor.d.ts +58 -0
- package/dist/infra/content/contentExtractor.d.ts.map +1 -0
- package/dist/infra/content/contentExtractor.js +296 -0
- package/dist/infra/content/contentExtractor.js.map +1 -0
- package/dist/infra/embeddings/embeddingProvider.d.ts +4 -0
- package/dist/infra/embeddings/embeddingProvider.d.ts.map +1 -0
- package/dist/infra/embeddings/embeddingProvider.js +11 -0
- package/dist/infra/embeddings/embeddingProvider.js.map +1 -0
- package/dist/infra/healthCheck.d.ts +23 -0
- package/dist/infra/healthCheck.d.ts.map +1 -0
- package/dist/infra/healthCheck.js +57 -0
- package/dist/infra/healthCheck.js.map +1 -0
- package/dist/infra/observability/tokenCounter.d.ts +30 -0
- package/dist/infra/observability/tokenCounter.d.ts.map +1 -0
- package/dist/infra/observability/tokenCounter.js +46 -0
- package/dist/infra/observability/tokenCounter.js.map +1 -0
- package/dist/infra/observability/tracing.d.ts +38 -0
- package/dist/infra/observability/tracing.d.ts.map +1 -0
- package/dist/infra/observability/tracing.js +92 -0
- package/dist/infra/observability/tracing.js.map +1 -0
- package/dist/infra/rateLimit/circuitBreaker.d.ts +54 -0
- package/dist/infra/rateLimit/circuitBreaker.d.ts.map +1 -0
- package/dist/infra/rateLimit/circuitBreaker.js +97 -0
- package/dist/infra/rateLimit/circuitBreaker.js.map +1 -0
- package/dist/infra/rateLimit/rateLimiter.d.ts +42 -0
- package/dist/infra/rateLimit/rateLimiter.d.ts.map +1 -0
- package/dist/infra/rateLimit/rateLimiter.js +89 -0
- package/dist/infra/rateLimit/rateLimiter.js.map +1 -0
- package/dist/infra/search/searxngClient.d.ts +29 -0
- package/dist/infra/search/searxngClient.d.ts.map +1 -0
- package/dist/infra/search/searxngClient.js +85 -0
- package/dist/infra/search/searxngClient.js.map +1 -0
- package/dist/infra/search/urlBlocklist.d.ts +28 -0
- package/dist/infra/search/urlBlocklist.d.ts.map +1 -0
- package/dist/infra/search/urlBlocklist.js +78 -0
- package/dist/infra/search/urlBlocklist.js.map +1 -0
- package/dist/infra/vector/qdrantClient.d.ts +18 -0
- package/dist/infra/vector/qdrantClient.d.ts.map +1 -0
- package/dist/infra/vector/qdrantClient.js +82 -0
- package/dist/infra/vector/qdrantClient.js.map +1 -0
- package/dist/infra/vector/researchRepository.d.ts +39 -0
- package/dist/infra/vector/researchRepository.d.ts.map +1 -0
- package/dist/infra/vector/researchRepository.js +294 -0
- package/dist/infra/vector/researchRepository.js.map +1 -0
- package/dist/lib/helpers.d.ts +50 -0
- package/dist/lib/helpers.d.ts.map +1 -0
- package/dist/lib/helpers.js +124 -0
- package/dist/lib/helpers.js.map +1 -0
- package/dist/lib/index.d.ts +65 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +61 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/rmsTool.d.ts +28 -0
- package/dist/lib/rmsTool.d.ts.map +1 -0
- package/dist/lib/rmsTool.js +79 -0
- package/dist/lib/rmsTool.js.map +1 -0
- package/dist/lib/schemas/lifecycleSchemas.d.ts +42 -0
- package/dist/lib/schemas/lifecycleSchemas.d.ts.map +1 -0
- package/dist/lib/schemas/lifecycleSchemas.js +176 -0
- package/dist/lib/schemas/lifecycleSchemas.js.map +1 -0
- package/dist/lib/schemas/researchSchemas.d.ts +23 -0
- package/dist/lib/schemas/researchSchemas.d.ts.map +1 -0
- package/dist/lib/schemas/researchSchemas.js +83 -0
- package/dist/lib/schemas/researchSchemas.js.map +1 -0
- package/dist/lib/tools/deleteResearch.d.ts +19 -0
- package/dist/lib/tools/deleteResearch.d.ts.map +1 -0
- package/dist/lib/tools/deleteResearch.js +37 -0
- package/dist/lib/tools/deleteResearch.js.map +1 -0
- package/dist/lib/tools/getDatetime.d.ts +7 -0
- package/dist/lib/tools/getDatetime.d.ts.map +1 -0
- package/dist/lib/tools/getDatetime.js +26 -0
- package/dist/lib/tools/getDatetime.js.map +1 -0
- package/dist/lib/tools/getResearch.d.ts +19 -0
- package/dist/lib/tools/getResearch.d.ts.map +1 -0
- package/dist/lib/tools/getResearch.js +32 -0
- package/dist/lib/tools/getResearch.js.map +1 -0
- package/dist/lib/tools/listResearch.d.ts +25 -0
- package/dist/lib/tools/listResearch.d.ts.map +1 -0
- package/dist/lib/tools/listResearch.js +41 -0
- package/dist/lib/tools/listResearch.js.map +1 -0
- package/dist/lib/tools/refreshResearch.d.ts +27 -0
- package/dist/lib/tools/refreshResearch.d.ts.map +1 -0
- package/dist/lib/tools/refreshResearch.js +81 -0
- package/dist/lib/tools/refreshResearch.js.map +1 -0
- package/dist/lib/tools/research.d.ts +108 -0
- package/dist/lib/tools/research.d.ts.map +1 -0
- package/dist/lib/tools/research.js +273 -0
- package/dist/lib/tools/research.js.map +1 -0
- package/dist/lib/tools/searchResearch.d.ts +25 -0
- package/dist/lib/tools/searchResearch.d.ts.map +1 -0
- package/dist/lib/tools/searchResearch.js +45 -0
- package/dist/lib/tools/searchResearch.js.map +1 -0
- package/dist/lib/types.d.ts +51 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/mcp/index.d.ts +12 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +12 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +45 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +440 -0
- package/dist/mcp/server.js.map +1 -0
- package/package.json +132 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../../src/app/graph/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,mBAAmB,EAGzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAYtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAG1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAUrF,8EAA8E;AAC9E,eAAO,MAAM,cAAc;;;;;;;;;;CAUjB,CAAC;AAEX,MAAM,WAAW,YAAY;IAC3B,kBAAkB,EAAE,mBAAmB,CAAC;IAExC,mEAAmE;IACnE,SAAS,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACtC,mEAAmE;IACnE,UAAU,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAC7C,0CAA0C;IAC1C,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC;;;;OAIG;IACH,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAGnC,yDAAyD;IACzD,kBAAkB,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,uDAAuD;IACvD,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnF,4DAA4D;IAC5D,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1D;;;OAGG;IACH,cAAc,CAAC,EAAE,mBAAmB,CAAC;CACtC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAsEnD"}
|
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
import { StateGraph, START, END, interrupt, MemorySaver, } from "@langchain/langgraph";
|
|
2
|
+
import { RmsStateAnnotation } from "../state/schema.js";
|
|
3
|
+
import { evaluateFreshness } from "../freshness/evaluator.js";
|
|
4
|
+
import { checkGuardrail } from "../governance/guardrails.js";
|
|
5
|
+
import { summarizeSearchResults } from "../summarization/summarizer.js";
|
|
6
|
+
import { synthesizeSummary } from "../summarization/synthesizer.js";
|
|
7
|
+
import { evaluateQueryRelevance, MAX_REWRITES } from "../queryRewriting/rewriter.js";
|
|
8
|
+
import { rerankSearchResults } from "../reranking/reranker.js";
|
|
9
|
+
import { planSearchQueries } from "../queryPlanning/planner.js";
|
|
10
|
+
import { performSearch } from "../../infra/search/searxngClient.js";
|
|
11
|
+
import { buildResearch, buildCompositeSummary } from "../../domain/researchUtils.js";
|
|
12
|
+
import { createChatModelProvider } from "../../infra/chat/chatModelProvider.js";
|
|
13
|
+
import { createEmbeddingProvider } from "../../infra/embeddings/embeddingProvider.js";
|
|
14
|
+
import { logInfo, logWarn, logError, ErrorCodes, withNodeTiming, } from "../../infra/observability/tracing.js";
|
|
15
|
+
/** Canonical node name constants used by both the graph and event mapping. */
|
|
16
|
+
export const RMS_NODE_NAMES = {
|
|
17
|
+
FRESHNESS_CHECKER: "freshnessChecker",
|
|
18
|
+
GUARDRAIL: "guardrail",
|
|
19
|
+
QUERY_PLANNER: "queryPlanner",
|
|
20
|
+
SEARCHER: "searcher",
|
|
21
|
+
QUERY_REWRITER: "queryRewriter",
|
|
22
|
+
RERANKER: "reranker",
|
|
23
|
+
HUMAN_APPROVAL: "human_approval",
|
|
24
|
+
SUMMARIZER: "summarizer",
|
|
25
|
+
PERSISTER: "persister",
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Builds the RMS LangGraph workflow:
|
|
29
|
+
* freshnessChecker -> (conditional) -> searcher -> summarizer -> persister
|
|
30
|
+
*
|
|
31
|
+
* RMS produces research summaries for autonomous agents.
|
|
32
|
+
* Guardrail gating and HITL interrupt are supported for sensitive queries.
|
|
33
|
+
*/
|
|
34
|
+
export function createRmsWorkflow(deps) {
|
|
35
|
+
const rawChatModel = deps.chatModel ?? createChatModelProvider();
|
|
36
|
+
const embeddings = deps.embeddings ?? createEmbeddingProvider();
|
|
37
|
+
// Wire token usage tracking: withConfig binds the collector as a callback
|
|
38
|
+
// so every LLM invocation (including withStructuredOutput) feeds usage metadata.
|
|
39
|
+
// The cast is safe because RunnableBinding forwards all calls to the underlying model.
|
|
40
|
+
const chatModel = (deps.tokenCollector
|
|
41
|
+
? rawChatModel.withConfig({ callbacks: [deps.tokenCollector] })
|
|
42
|
+
: rawChatModel);
|
|
43
|
+
const checkpointer = deps.checkpointer ??
|
|
44
|
+
(() => {
|
|
45
|
+
const msg = "No durable checkpointer provided — using in-memory MemorySaver. " +
|
|
46
|
+
"State will be lost on process restart and HITL resume will not work.\n" +
|
|
47
|
+
"For production, inject a checkpointer via deps.checkpointer:\n" +
|
|
48
|
+
" import { SqliteSaver } from '@langchain/langgraph-checkpoint-sqlite';\n" +
|
|
49
|
+
" const checkpointer = SqliteSaver.fromConnString('rms.db');\n" +
|
|
50
|
+
"See: https://langchain-ai.github.io/langgraphjs/reference/classes/checkpoint_sqlite.SqliteSaver.html";
|
|
51
|
+
if (process.env["NODE_ENV"] === "production") {
|
|
52
|
+
throw new Error(msg);
|
|
53
|
+
}
|
|
54
|
+
logWarn(msg);
|
|
55
|
+
return new MemorySaver();
|
|
56
|
+
})();
|
|
57
|
+
const graph = new StateGraph(RmsStateAnnotation)
|
|
58
|
+
.addNode("freshnessChecker", freshnessCheckerNode(deps), {
|
|
59
|
+
retryPolicy: { maxAttempts: 3 },
|
|
60
|
+
})
|
|
61
|
+
.addNode("guardrail", guardrailNode())
|
|
62
|
+
.addNode("queryPlanner", queryPlannerNode(chatModel))
|
|
63
|
+
.addNode("searcher", searcherNode(), {
|
|
64
|
+
retryPolicy: { maxAttempts: 3 },
|
|
65
|
+
})
|
|
66
|
+
.addNode("queryRewriter", queryRewriterNode(chatModel))
|
|
67
|
+
.addNode("reranker", rerankerNode(embeddings))
|
|
68
|
+
.addNode("human_approval", humanApprovalNode(deps, checkpointer))
|
|
69
|
+
.addNode("summarizer", summarizerNode(chatModel), {
|
|
70
|
+
retryPolicy: { maxAttempts: 3 },
|
|
71
|
+
})
|
|
72
|
+
.addNode("persister", persisterNode(deps))
|
|
73
|
+
.addEdge(START, "freshnessChecker")
|
|
74
|
+
.addConditionalEdges("freshnessChecker", routeAfterFreshness, {
|
|
75
|
+
guardrail: "guardrail",
|
|
76
|
+
persister: "persister",
|
|
77
|
+
})
|
|
78
|
+
.addConditionalEdges("guardrail", routeAfterGuardrail, {
|
|
79
|
+
queryPlanner: "queryPlanner",
|
|
80
|
+
persister: "persister",
|
|
81
|
+
})
|
|
82
|
+
.addEdge("queryPlanner", "searcher")
|
|
83
|
+
.addEdge("searcher", "queryRewriter")
|
|
84
|
+
.addConditionalEdges("queryRewriter", routeAfterQueryRewriter, {
|
|
85
|
+
searcher: "searcher",
|
|
86
|
+
reranker: "reranker",
|
|
87
|
+
})
|
|
88
|
+
.addEdge("reranker", "summarizer")
|
|
89
|
+
.addConditionalEdges("summarizer", routeAfterSummarizer, {
|
|
90
|
+
human_approval: "human_approval",
|
|
91
|
+
persister: "persister",
|
|
92
|
+
})
|
|
93
|
+
.addEdge("human_approval", "persister")
|
|
94
|
+
.addEdge("persister", END);
|
|
95
|
+
return graph.compile({ checkpointer });
|
|
96
|
+
}
|
|
97
|
+
// ── Node Implementations ─────────────────────────────────────────────
|
|
98
|
+
function freshnessCheckerNode(deps) {
|
|
99
|
+
const node = async (state) => {
|
|
100
|
+
const { subject, traceId, tenantId } = state;
|
|
101
|
+
return withNodeTiming("freshnessChecker", traceId, subject, async () => {
|
|
102
|
+
logInfo("Checking freshness", { node: "freshnessChecker", researchId: subject, traceId });
|
|
103
|
+
const freshness = await evaluateFreshness(subject, deps.researchRepository, { tenantId });
|
|
104
|
+
return {
|
|
105
|
+
cachedResearch: freshness.cachedResearch ?? undefined,
|
|
106
|
+
isFresh: freshness.isFresh,
|
|
107
|
+
currentPhase: "freshness",
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
return node;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Guards the subject against forbidden patterns before search.
|
|
115
|
+
* Sets `error` if blocked, which causes downstream routing to skip search.
|
|
116
|
+
*/
|
|
117
|
+
function guardrailNode() {
|
|
118
|
+
const node = async (state) => {
|
|
119
|
+
const { subject, traceId } = state;
|
|
120
|
+
return withNodeTiming("guardrail", traceId, subject, () => {
|
|
121
|
+
const check = checkGuardrail(subject);
|
|
122
|
+
if (!check.allowed) {
|
|
123
|
+
logWarn("Guardrail blocked research", {
|
|
124
|
+
node: "guardrail",
|
|
125
|
+
researchId: subject,
|
|
126
|
+
reason: check.reason,
|
|
127
|
+
});
|
|
128
|
+
return { error: check.reason };
|
|
129
|
+
}
|
|
130
|
+
logInfo("Guardrail passed", { node: "guardrail", researchId: subject });
|
|
131
|
+
return {};
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
return node;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Generates optimized search queries from the subject before searching.
|
|
138
|
+
*/
|
|
139
|
+
function queryPlannerNode(chatModel) {
|
|
140
|
+
const node = async (state) => {
|
|
141
|
+
const { subject, traceId } = state;
|
|
142
|
+
return withNodeTiming("queryPlanner", traceId, subject, async () => {
|
|
143
|
+
const queries = await planSearchQueries(subject, chatModel, traceId);
|
|
144
|
+
return {
|
|
145
|
+
plannedQueries: queries,
|
|
146
|
+
currentPhase: "queryPlanning",
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
return node;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Executes web searches for all planned queries concurrently,
|
|
154
|
+
* merging and deduplicating results by URL.
|
|
155
|
+
*
|
|
156
|
+
* When a rewritten query is available (from the query rewriter loop),
|
|
157
|
+
* it takes precedence over planned queries.
|
|
158
|
+
*/
|
|
159
|
+
function searcherNode() {
|
|
160
|
+
const node = async (state) => {
|
|
161
|
+
const { subject, traceId, maxResults, rewrittenQuery, originalSubject, plannedQueries } = state;
|
|
162
|
+
return withNodeTiming("searcher", traceId, subject, async () => {
|
|
163
|
+
try {
|
|
164
|
+
// Determine which queries to execute
|
|
165
|
+
const queries = rewrittenQuery
|
|
166
|
+
? [rewrittenQuery]
|
|
167
|
+
: plannedQueries.length > 0
|
|
168
|
+
? plannedQueries
|
|
169
|
+
: [subject];
|
|
170
|
+
logInfo("Searching web", {
|
|
171
|
+
node: "searcher",
|
|
172
|
+
researchId: subject,
|
|
173
|
+
traceId,
|
|
174
|
+
queryCount: queries.length,
|
|
175
|
+
});
|
|
176
|
+
// Execute all queries concurrently
|
|
177
|
+
const perQueryLimit = Math.max(3, Math.ceil(maxResults / queries.length));
|
|
178
|
+
const allResults = await Promise.all(queries.map((q) => performSearch(q, { numResults: perQueryLimit }).catch((err) => {
|
|
179
|
+
logWarn("Individual query search failed", {
|
|
180
|
+
node: "searcher",
|
|
181
|
+
query: q,
|
|
182
|
+
error: err instanceof Error ? err.message : String(err),
|
|
183
|
+
});
|
|
184
|
+
return [];
|
|
185
|
+
})));
|
|
186
|
+
// Merge and deduplicate by URL, keeping the first occurrence
|
|
187
|
+
const seen = new Set();
|
|
188
|
+
const searchResults = allResults.flat().filter((r) => {
|
|
189
|
+
if (seen.has(r.url))
|
|
190
|
+
return false;
|
|
191
|
+
seen.add(r.url);
|
|
192
|
+
return true;
|
|
193
|
+
});
|
|
194
|
+
return {
|
|
195
|
+
searchResults: searchResults.slice(0, maxResults),
|
|
196
|
+
currentPhase: "searching",
|
|
197
|
+
// Preserve original subject on first search
|
|
198
|
+
originalSubject: originalSubject ?? subject,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
// Degraded mode: return empty results with error instead of throwing
|
|
203
|
+
logError("Search failed, entering degraded mode", {
|
|
204
|
+
node: "searcher",
|
|
205
|
+
researchId: subject,
|
|
206
|
+
traceId,
|
|
207
|
+
errorCode: ErrorCodes.SEARCH_FAILED,
|
|
208
|
+
error: err instanceof Error ? err.message : String(err),
|
|
209
|
+
});
|
|
210
|
+
return {
|
|
211
|
+
searchResults: [],
|
|
212
|
+
error: `Search failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
213
|
+
currentPhase: "searching",
|
|
214
|
+
originalSubject: originalSubject ?? subject,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
return node;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Evaluates relevance of search results and rewrites the query if needed.
|
|
223
|
+
* Loops back to searcher when relevance is low and rewrites are available.
|
|
224
|
+
*/
|
|
225
|
+
function queryRewriterNode(chatModel) {
|
|
226
|
+
const node = async (state) => {
|
|
227
|
+
const { subject, searchResults, traceId, rewriteCount } = state;
|
|
228
|
+
return withNodeTiming("queryRewriter", traceId, subject, async () => {
|
|
229
|
+
const result = await evaluateQueryRelevance(subject, searchResults, chatModel, traceId);
|
|
230
|
+
return {
|
|
231
|
+
relevanceScore: result.relevanceScore,
|
|
232
|
+
rewrittenQuery: result.isRelevant ? undefined : result.rewrittenQuery,
|
|
233
|
+
rewriteCount: result.isRelevant ? rewriteCount : rewriteCount + 1,
|
|
234
|
+
currentPhase: "queryRewriting",
|
|
235
|
+
};
|
|
236
|
+
});
|
|
237
|
+
};
|
|
238
|
+
return node;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Re-ranks search results by semantic similarity to the subject.
|
|
242
|
+
* Filters out low-relevance results before summarization.
|
|
243
|
+
*/
|
|
244
|
+
function rerankerNode(embeddings) {
|
|
245
|
+
const node = async (state) => {
|
|
246
|
+
const { subject, searchResults, traceId } = state;
|
|
247
|
+
return withNodeTiming("reranker", traceId, subject, async () => {
|
|
248
|
+
const ranked = await rerankSearchResults(subject, searchResults, embeddings, { topN: 10 }, traceId);
|
|
249
|
+
return {
|
|
250
|
+
searchResults: ranked.map((r) => r.result),
|
|
251
|
+
currentPhase: "reranking",
|
|
252
|
+
};
|
|
253
|
+
});
|
|
254
|
+
};
|
|
255
|
+
return node;
|
|
256
|
+
}
|
|
257
|
+
function summarizerNode(chatModel) {
|
|
258
|
+
const node = async (state) => {
|
|
259
|
+
const { subject, searchResults, traceId } = state;
|
|
260
|
+
return withNodeTiming("summarizer", traceId, subject, async () => {
|
|
261
|
+
try {
|
|
262
|
+
logInfo("Summarizing", { node: "summarizer", researchId: subject, traceId });
|
|
263
|
+
const summarization = await summarizeSearchResults(subject, searchResults, chatModel);
|
|
264
|
+
// Synthesis step: produce a unified research report from per-source takeaways
|
|
265
|
+
try {
|
|
266
|
+
const synthesis = await synthesizeSummary(subject, summarization.sourceSummaries, chatModel);
|
|
267
|
+
summarization.synthesizedSummary = synthesis.synthesizedSummary;
|
|
268
|
+
summarization.keyFindings = synthesis.keyFindings;
|
|
269
|
+
summarization.limitations = synthesis.limitations;
|
|
270
|
+
logInfo("Synthesis complete", {
|
|
271
|
+
node: "summarizer",
|
|
272
|
+
researchId: subject,
|
|
273
|
+
traceId,
|
|
274
|
+
summaryLength: synthesis.synthesizedSummary.length,
|
|
275
|
+
keyFindingsCount: synthesis.keyFindings.length,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
catch (synthErr) {
|
|
279
|
+
// Synthesis is non-fatal — fall back to composite summary
|
|
280
|
+
logWarn("Synthesis failed, using per-source composite summary", {
|
|
281
|
+
node: "summarizer",
|
|
282
|
+
researchId: subject,
|
|
283
|
+
traceId,
|
|
284
|
+
error: synthErr instanceof Error ? synthErr.message : String(synthErr),
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
// Token usage is captured automatically via the TokenUsageCollector
|
|
288
|
+
// callback wired at graph construction (chatModel.withConfig({ callbacks })).
|
|
289
|
+
// LangChain fires handleLLMEnd at the LLM layer before withStructuredOutput
|
|
290
|
+
// post-processing, so token counts accumulate correctly.
|
|
291
|
+
return {
|
|
292
|
+
summarization,
|
|
293
|
+
currentPhase: "summarizing",
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
catch (err) {
|
|
297
|
+
// Degraded mode: persist raw results without summarization
|
|
298
|
+
logError("Summarization failed, persisting raw results", {
|
|
299
|
+
node: "summarizer",
|
|
300
|
+
researchId: subject,
|
|
301
|
+
traceId,
|
|
302
|
+
errorCode: ErrorCodes.SUMMARIZATION_FAILED,
|
|
303
|
+
error: err instanceof Error ? err.message : String(err),
|
|
304
|
+
});
|
|
305
|
+
return {
|
|
306
|
+
summarization: {
|
|
307
|
+
sourceSummaries: searchResults.map((r) => ({
|
|
308
|
+
url: r.url,
|
|
309
|
+
title: r.title,
|
|
310
|
+
keyTakeaways: r.snippet,
|
|
311
|
+
relevance: 0.1,
|
|
312
|
+
tags: [],
|
|
313
|
+
language: "en",
|
|
314
|
+
})),
|
|
315
|
+
overallConfidence: 0.1,
|
|
316
|
+
tags: [],
|
|
317
|
+
language: "en",
|
|
318
|
+
extractionBreakdown: [],
|
|
319
|
+
},
|
|
320
|
+
currentPhase: "summarizing",
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
};
|
|
325
|
+
return node;
|
|
326
|
+
}
|
|
327
|
+
function persisterNode(deps) {
|
|
328
|
+
const node = async (state) => {
|
|
329
|
+
const { subject, traceId, cachedResearch, searchResults, summarization, error, tenantId, metadata, plannedQueries, } = state;
|
|
330
|
+
return withNodeTiming("persister", traceId, subject, async () => {
|
|
331
|
+
// If error occurred upstream, skip persistence
|
|
332
|
+
if (error) {
|
|
333
|
+
logError("Skipping persistence due to upstream error", {
|
|
334
|
+
node: "persister",
|
|
335
|
+
researchId: subject,
|
|
336
|
+
error,
|
|
337
|
+
});
|
|
338
|
+
return { currentPhase: "persisting" };
|
|
339
|
+
}
|
|
340
|
+
// If returning cached result (isFresh path), just set source
|
|
341
|
+
if (!summarization) {
|
|
342
|
+
if (deps.onCacheHit && cachedResearch) {
|
|
343
|
+
try {
|
|
344
|
+
await deps.onCacheHit(cachedResearch);
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
/* non-fatal: consumer hook */
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
research: cachedResearch,
|
|
352
|
+
source: "cache",
|
|
353
|
+
currentPhase: "persisting",
|
|
354
|
+
tokenUsage: deps.tokenCollector?.usage ?? { promptTokens: 0, completionTokens: 0 },
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
// Delete stale entry before inserting new one
|
|
358
|
+
if (cachedResearch) {
|
|
359
|
+
await deps.researchRepository.deleteByIds([cachedResearch.id]);
|
|
360
|
+
}
|
|
361
|
+
const freshnessDays = deps.freshnessDays ?? state.freshnessDays;
|
|
362
|
+
// Filter out dead sources (relevance === 0 or empty URL)
|
|
363
|
+
const cleanSummaries = summarization.sourceSummaries.filter((s) => s.url.length > 0 && s.relevance > 0);
|
|
364
|
+
// Use synthesis if available and substantial; otherwise fall back to composite
|
|
365
|
+
const synthesisText = summarization.synthesizedSummary?.trim();
|
|
366
|
+
const MIN_SYNTHESIS_LENGTH = 200;
|
|
367
|
+
const summary = synthesisText && synthesisText.length >= MIN_SYNTHESIS_LENGTH
|
|
368
|
+
? synthesisText
|
|
369
|
+
: buildCompositeSummary(cleanSummaries);
|
|
370
|
+
// Use planned queries (if available) instead of just the subject
|
|
371
|
+
const storedQueries = plannedQueries.length > 0 ? [...new Set([subject, ...plannedQueries])] : [subject];
|
|
372
|
+
const research = buildResearch({
|
|
373
|
+
subject,
|
|
374
|
+
sourceSummaries: cleanSummaries,
|
|
375
|
+
summary,
|
|
376
|
+
sourceUrls: searchResults.map((r) => r.url).filter(Boolean),
|
|
377
|
+
searchQueries: storedQueries,
|
|
378
|
+
confidenceScore: summarization.overallConfidence,
|
|
379
|
+
tags: summarization.tags,
|
|
380
|
+
language: summarization.language,
|
|
381
|
+
rawResultCount: searchResults.length,
|
|
382
|
+
keyFindings: summarization.keyFindings,
|
|
383
|
+
limitations: summarization.limitations,
|
|
384
|
+
tenantId,
|
|
385
|
+
metadata,
|
|
386
|
+
freshnessDays,
|
|
387
|
+
});
|
|
388
|
+
await deps.researchRepository.upsert(research);
|
|
389
|
+
if (deps.onResearchComplete) {
|
|
390
|
+
try {
|
|
391
|
+
await deps.onResearchComplete(research);
|
|
392
|
+
}
|
|
393
|
+
catch {
|
|
394
|
+
/* non-fatal: consumer hook */
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
logInfo("Research persisted", { node: "persister", researchId: research.id, traceId });
|
|
398
|
+
return {
|
|
399
|
+
research,
|
|
400
|
+
source: cachedResearch ? "cache+web" : "web",
|
|
401
|
+
currentPhase: "persisting",
|
|
402
|
+
tokenUsage: deps.tokenCollector?.usage ?? { promptTokens: 0, completionTokens: 0 },
|
|
403
|
+
};
|
|
404
|
+
});
|
|
405
|
+
};
|
|
406
|
+
return node;
|
|
407
|
+
}
|
|
408
|
+
/** Emits a LangGraph interrupt payload for external systems to approve/resume. */
|
|
409
|
+
function humanApprovalNode(deps, checkpointer) {
|
|
410
|
+
const node = async (state) => {
|
|
411
|
+
const { subject, searchResults, traceId, summarization } = state;
|
|
412
|
+
return withNodeTiming("human_approval", traceId, subject, async () => {
|
|
413
|
+
if (deps.onApprovalRequired) {
|
|
414
|
+
const confidence = summarization?.overallConfidence ?? 0;
|
|
415
|
+
try {
|
|
416
|
+
await deps.onApprovalRequired(subject, confidence);
|
|
417
|
+
}
|
|
418
|
+
catch {
|
|
419
|
+
/* non-fatal: consumer hook */
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
if (checkpointer instanceof MemorySaver) {
|
|
423
|
+
logWarn("HITL interrupt issued with in-memory checkpointer (MemorySaver). " +
|
|
424
|
+
"State will be lost on process restart — HITL resume will fail. " +
|
|
425
|
+
"Set RMS_CHECKPOINTER=sqlite or inject a durable checkpointer.", { node: "human_approval", subject, traceId });
|
|
426
|
+
}
|
|
427
|
+
interrupt({
|
|
428
|
+
action: "approve_research",
|
|
429
|
+
subject,
|
|
430
|
+
resultCount: searchResults.length,
|
|
431
|
+
message: "Please approve this research before persistence.",
|
|
432
|
+
});
|
|
433
|
+
return {};
|
|
434
|
+
});
|
|
435
|
+
};
|
|
436
|
+
return node;
|
|
437
|
+
}
|
|
438
|
+
// ── Conditional Edge Routers ─────────────────────────────────────────
|
|
439
|
+
/** Route to guardrail when cache is stale/missing; otherwise skip to persister. */
|
|
440
|
+
const routeAfterFreshness = (state) => {
|
|
441
|
+
if (state.isFresh && !state.forceRefresh && state.cachedResearch)
|
|
442
|
+
return "persister";
|
|
443
|
+
return "guardrail";
|
|
444
|
+
};
|
|
445
|
+
/** Route to queryPlanner if guardrail allows; otherwise to persister (blocked). */
|
|
446
|
+
const routeAfterGuardrail = (state) => {
|
|
447
|
+
if (state.error)
|
|
448
|
+
return "persister";
|
|
449
|
+
return "queryPlanner";
|
|
450
|
+
};
|
|
451
|
+
/**
|
|
452
|
+
* Route after query relevance evaluation:
|
|
453
|
+
* - If relevant OR max rewrites reached → reranker
|
|
454
|
+
* - If irrelevant and rewrites left → searcher (with rewritten query)
|
|
455
|
+
*/
|
|
456
|
+
const routeAfterQueryRewriter = (state) => {
|
|
457
|
+
if (state.rewrittenQuery && state.rewriteCount <= MAX_REWRITES)
|
|
458
|
+
return "searcher";
|
|
459
|
+
return "reranker";
|
|
460
|
+
};
|
|
461
|
+
/** Confidence gating: low-confidence summaries (< 0.4) route to human approval for review. */
|
|
462
|
+
const routeAfterSummarizer = (state) => {
|
|
463
|
+
const score = state.summarization?.overallConfidence ?? 1;
|
|
464
|
+
if (score < 0.4)
|
|
465
|
+
return "human_approval";
|
|
466
|
+
return "persister";
|
|
467
|
+
};
|
|
468
|
+
//# sourceMappingURL=workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.js","sourceRoot":"","sources":["../../../src/app/graph/workflow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
import type { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
|
3
|
+
/**
|
|
4
|
+
* Schema for the LLM structured output when planning search queries.
|
|
5
|
+
*/
|
|
6
|
+
export declare const QueryPlanOutputSchema: z.ZodObject<{
|
|
7
|
+
queries: z.ZodArray<z.ZodString>;
|
|
8
|
+
}, z.core.$strip>;
|
|
9
|
+
export type QueryPlanOutput = z.infer<typeof QueryPlanOutputSchema>;
|
|
10
|
+
/**
|
|
11
|
+
* Generates optimized search queries from a natural language research subject.
|
|
12
|
+
*
|
|
13
|
+
* Converts conversational or vague subjects into 2-4 diverse keyword queries
|
|
14
|
+
* that are optimized for web search engines (SearXNG). This dramatically
|
|
15
|
+
* improves search result quality compared to passing raw subjects as queries.
|
|
16
|
+
*/
|
|
17
|
+
export declare function planSearchQueries(subject: string, chatModel: BaseChatModel, traceId?: string): Promise<string[]>;
|
|
18
|
+
//# sourceMappingURL=planner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner.d.ts","sourceRoot":"","sources":["../../../src/app/queryPlanning/planner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAC3B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6CAA6C,CAAC;AAIjF;;GAEG;AACH,eAAO,MAAM,qBAAqB;;iBAehC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAgBpE;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,aAAa,EACxB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,EAAE,CAAC,CA2CnB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
import { HumanMessage, SystemMessage } from "@langchain/core/messages";
|
|
3
|
+
import { logInfo, logWarn, withNodeTiming } from "../../infra/observability/tracing.js";
|
|
4
|
+
/**
|
|
5
|
+
* Schema for the LLM structured output when planning search queries.
|
|
6
|
+
*/
|
|
7
|
+
export const QueryPlanOutputSchema = z.object({
|
|
8
|
+
queries: z
|
|
9
|
+
.array(z.string())
|
|
10
|
+
.min(1)
|
|
11
|
+
.max(4)
|
|
12
|
+
.meta({
|
|
13
|
+
description: "2-4 diverse, optimized keyword search queries designed to retrieve the best web results " +
|
|
14
|
+
"for the given research subject. Each query should approach the topic from a different angle: " +
|
|
15
|
+
"one broad, one specific/technical, and optionally one using alternative terminology.",
|
|
16
|
+
examples: [
|
|
17
|
+
["LangGraph state management best practices", "LangGraph StateGraph TypeScript tutorial"],
|
|
18
|
+
["React server components vs client components", "Next.js RSC architecture 2025"],
|
|
19
|
+
],
|
|
20
|
+
}),
|
|
21
|
+
});
|
|
22
|
+
const SYSTEM_PROMPT = `You are a search query optimization expert. Your queries feed into SearXNG for an automated research pipeline.
|
|
23
|
+
|
|
24
|
+
Before generating, consider: what sub-topics exist, and what alternative terminology could improve coverage?
|
|
25
|
+
|
|
26
|
+
Guidelines:
|
|
27
|
+
1. Generate 2-4 keyword-focused search queries optimized for web search engines.
|
|
28
|
+
2. Use specific keywords with technical terms, version numbers, or product names when relevant.
|
|
29
|
+
3. Cover different angles: broad overview, specific/technical, and alternative terminology.
|
|
30
|
+
4. Prefer queries that return documentation, tutorials, or authoritative sources.
|
|
31
|
+
|
|
32
|
+
Example — subject: "How to handle state in LangGraph"
|
|
33
|
+
Good: "LangGraph StateGraph state management best practices", "LangGraph TypeScript checkpointer persistence tutorial 2025"
|
|
34
|
+
Bad (just rephrasing): "how to handle state in LangGraph"`;
|
|
35
|
+
/**
|
|
36
|
+
* Generates optimized search queries from a natural language research subject.
|
|
37
|
+
*
|
|
38
|
+
* Converts conversational or vague subjects into 2-4 diverse keyword queries
|
|
39
|
+
* that are optimized for web search engines (SearXNG). This dramatically
|
|
40
|
+
* improves search result quality compared to passing raw subjects as queries.
|
|
41
|
+
*/
|
|
42
|
+
export async function planSearchQueries(subject, chatModel, traceId) {
|
|
43
|
+
return withNodeTiming("queryPlanner", traceId, subject, async () => {
|
|
44
|
+
logInfo("Planning search queries", {
|
|
45
|
+
node: "queryPlanner",
|
|
46
|
+
traceId,
|
|
47
|
+
researchId: subject,
|
|
48
|
+
});
|
|
49
|
+
const humanMsg = `Research subject:\n<subject>\n${subject}\n</subject>\n\n` +
|
|
50
|
+
`Generate 2-4 optimized search queries for this research subject.`;
|
|
51
|
+
try {
|
|
52
|
+
const structuredModel = chatModel.withStructuredOutput(QueryPlanOutputSchema, {
|
|
53
|
+
method: "jsonSchema",
|
|
54
|
+
name: "query_plan",
|
|
55
|
+
});
|
|
56
|
+
const result = await structuredModel.invoke([
|
|
57
|
+
new SystemMessage(SYSTEM_PROMPT),
|
|
58
|
+
new HumanMessage(humanMsg),
|
|
59
|
+
]);
|
|
60
|
+
const queries = result.queries.map((q) => q.trim()).filter((q) => q.length > 0);
|
|
61
|
+
logInfo("Search queries planned", {
|
|
62
|
+
node: "queryPlanner",
|
|
63
|
+
traceId,
|
|
64
|
+
researchId: subject,
|
|
65
|
+
queryCount: queries.length,
|
|
66
|
+
});
|
|
67
|
+
return queries.length > 0 ? queries : [subject];
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
// On failure, fall back to using the raw subject as the search query
|
|
71
|
+
logWarn("Query planning failed, using raw subject", {
|
|
72
|
+
node: "queryPlanner",
|
|
73
|
+
traceId,
|
|
74
|
+
error: err instanceof Error ? err.message : String(err),
|
|
75
|
+
});
|
|
76
|
+
return [subject];
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=planner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner.js","sourceRoot":"","sources":["../../../src/app/queryPlanning/planner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAExF;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,OAAO,EAAE,CAAC;SACP,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,CAAC,CAAC;SACN,IAAI,CAAC;QACJ,WAAW,EACT,0FAA0F;YAC1F,+FAA+F;YAC/F,sFAAsF;QACxF,QAAQ,EAAE;YACR,CAAC,2CAA2C,EAAE,0CAA0C,CAAC;YACzF,CAAC,8CAA8C,EAAE,+BAA+B,CAAC;SAClF;KACF,CAAC;CACL,CAAC,CAAC;AAIH,MAAM,aAAa,GAAG;;;;;;;;;;;;4DAYsC,CAAC;AAE7D;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAe,EACf,SAAwB,EACxB,OAAgB;IAEhB,OAAO,cAAc,CAAC,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,IAAI,EAAE;QACjE,OAAO,CAAC,yBAAyB,EAAE;YACjC,IAAI,EAAE,cAAc;YACpB,OAAO;YACP,UAAU,EAAE,OAAO;SACpB,CAAC,CAAC;QAEH,MAAM,QAAQ,GACZ,iCAAiC,OAAO,kBAAkB;YAC1D,kEAAkE,CAAC;QAErE,IAAI,CAAC;YACH,MAAM,eAAe,GAAG,SAAS,CAAC,oBAAoB,CAAC,qBAAqB,EAAE;gBAC5E,MAAM,EAAE,YAAY;gBACpB,IAAI,EAAE,YAAY;aACnB,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC;gBAC1C,IAAI,aAAa,CAAC,aAAa,CAAC;gBAChC,IAAI,YAAY,CAAC,QAAQ,CAAC;aAC3B,CAAC,CAAC;YAEH,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAEhF,OAAO,CAAC,wBAAwB,EAAE;gBAChC,IAAI,EAAE,cAAc;gBACpB,OAAO;gBACP,UAAU,EAAE,OAAO;gBACnB,UAAU,EAAE,OAAO,CAAC,MAAM;aAC3B,CAAC,CAAC;YAEH,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,qEAAqE;YACrE,OAAO,CAAC,0CAA0C,EAAE;gBAClD,IAAI,EAAE,cAAc;gBACpB,OAAO;gBACP,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aACxD,CAAC,CAAC;YACH,OAAO,CAAC,OAAO,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
import type { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
|
3
|
+
import type { SearxngSearchResult } from "../../infra/search/searxngClient.js";
|
|
4
|
+
/** Maximum number of query rewrites before forcing summarization. */
|
|
5
|
+
export declare const MAX_REWRITES = 2;
|
|
6
|
+
/** Minimum relevance score below which a rewrite is triggered. */
|
|
7
|
+
export declare const MIN_RELEVANCE_SCORE = 0.4;
|
|
8
|
+
/**
|
|
9
|
+
* Schema for the LLM structured output when evaluating query relevance.
|
|
10
|
+
*/
|
|
11
|
+
export declare const QueryRewriteOutputSchema: z.ZodObject<{
|
|
12
|
+
isRelevant: z.ZodBoolean;
|
|
13
|
+
relevanceScore: z.ZodDefault<z.ZodNumber>;
|
|
14
|
+
rewrittenQuery: z.ZodOptional<z.ZodString>;
|
|
15
|
+
reasoning: z.ZodString;
|
|
16
|
+
}, z.core.$strip>;
|
|
17
|
+
export type QueryRewriteOutput = z.infer<typeof QueryRewriteOutputSchema>;
|
|
18
|
+
export interface QueryRewriteResult {
|
|
19
|
+
isRelevant: boolean;
|
|
20
|
+
relevanceScore: number;
|
|
21
|
+
rewrittenQuery?: string | undefined;
|
|
22
|
+
reasoning: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Evaluates the relevance of search results to the subject and optionally
|
|
26
|
+
* rewrites the query for better results.
|
|
27
|
+
*
|
|
28
|
+
* Used as a feedback loop node in the LangGraph workflow:
|
|
29
|
+
* searcher → queryRewriter → (relevant? summarizer : searcher with rewritten query)
|
|
30
|
+
*/
|
|
31
|
+
export declare function evaluateQueryRelevance(subject: string, searchResults: SearxngSearchResult[], chatModel: BaseChatModel, traceId?: string): Promise<QueryRewriteResult>;
|
|
32
|
+
//# sourceMappingURL=rewriter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rewriter.d.ts","sourceRoot":"","sources":["../../../src/app/queryRewriting/rewriter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAC3B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6CAA6C,CAAC;AAEjF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAG/E,qEAAqE;AACrE,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B,kEAAkE;AAClE,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC;;GAEG;AACH,eAAO,MAAM,wBAAwB;;;;;iBAgCnC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAe1E,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,mBAAmB,EAAE,EACpC,SAAS,EAAE,aAAa,EACxB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,CAAC,CAyD7B"}
|