@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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Faruk Ada
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
# Research Memory System built with LangChain TypeScript (RMS)
|
|
2
|
+
|
|
3
|
+
[](https://github.com/sponsors/FarukAda)
|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
RMS is a research caching library for autonomous agents. It searches the web via SearXNG, summarizes results using an LLM, and stores condensed research in a Qdrant vector database with automatic freshness management.
|
|
10
|
+
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
- [What This Package Is](#what-this-package-is)
|
|
14
|
+
- [Responsibility Boundary](#responsibility-boundary)
|
|
15
|
+
- [System Overview](#system-overview)
|
|
16
|
+
- [Quick Start](#quick-start)
|
|
17
|
+
- [Use as Library](#use-as-library)
|
|
18
|
+
- [Streaming](#streaming)
|
|
19
|
+
- [Integration with createAgent](#integration-with-createagent)
|
|
20
|
+
- [Public API Reference](#public-api-reference)
|
|
21
|
+
- [Package Exports](#package-exports)
|
|
22
|
+
- [Tool Catalog](#tool-catalog)
|
|
23
|
+
- [Configuration Reference](#configuration-reference)
|
|
24
|
+
- [Production Considerations](#production-considerations)
|
|
25
|
+
- [Known Non-Goals](#known-non-goals)
|
|
26
|
+
- [Testing and CI](#testing-and-ci)
|
|
27
|
+
- [Documentation](#documentation)
|
|
28
|
+
- [Project Structure](#project-structure)
|
|
29
|
+
- [License](#license)
|
|
30
|
+
|
|
31
|
+
<a id="what-this-package-is"></a>
|
|
32
|
+
|
|
33
|
+
## What This Package Is
|
|
34
|
+
|
|
35
|
+
- **Research caching engine**: searches the web, summarizes, and caches results in a vector store.
|
|
36
|
+
- **Freshness-aware**: automatically re-fetches stale research when cached data expires.
|
|
37
|
+
- **LangChain-native**: exports tools via the `tool()` factory from `@langchain/core/tools`.
|
|
38
|
+
- **Vector-centric**: uses embeddings + Qdrant for semantic research retrieval and deduplication.
|
|
39
|
+
|
|
40
|
+
<a id="responsibility-boundary"></a>
|
|
41
|
+
|
|
42
|
+
## Responsibility Boundary
|
|
43
|
+
|
|
44
|
+
| Concern | Owned by RMS | Owned by your Agent |
|
|
45
|
+
| --------------------------------------------- | ------------ | ------------------- |
|
|
46
|
+
| Web search via SearXNG | Yes | No |
|
|
47
|
+
| LLM-based search result summarization | Yes | No |
|
|
48
|
+
| Research caching and freshness management | Yes | No |
|
|
49
|
+
| Research retrieval, listing, and search | Yes | No |
|
|
50
|
+
| Deciding when research is needed | No | Yes |
|
|
51
|
+
| Acting on research results | No | Yes |
|
|
52
|
+
|
|
53
|
+
<a id="system-overview"></a>
|
|
54
|
+
|
|
55
|
+
## System Overview
|
|
56
|
+
|
|
57
|
+
```mermaid
|
|
58
|
+
flowchart LR
|
|
59
|
+
subgraph agentRuntime [AgentRuntime]
|
|
60
|
+
agent[LangChainAgent]
|
|
61
|
+
rmsTools[RmsTools]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
subgraph rmsLibrary [RmsLibraryInProcess]
|
|
65
|
+
orchestrator[conductResearch]
|
|
66
|
+
freshness[FreshnessEvaluator]
|
|
67
|
+
summarizer[LLMSummarizer]
|
|
68
|
+
lifecycle[LifecycleTools]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
subgraph backingServices [BackingServices]
|
|
72
|
+
qdrant[(Qdrant)]
|
|
73
|
+
searxng[SearXNG]
|
|
74
|
+
ollama[Ollama]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
agent --> rmsTools
|
|
78
|
+
rmsTools --> orchestrator
|
|
79
|
+
rmsTools --> lifecycle
|
|
80
|
+
orchestrator --> freshness
|
|
81
|
+
orchestrator --> summarizer
|
|
82
|
+
freshness --> qdrant
|
|
83
|
+
summarizer --> ollama
|
|
84
|
+
orchestrator --> searxng
|
|
85
|
+
orchestrator --> qdrant
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
<a id="quick-start"></a>
|
|
89
|
+
|
|
90
|
+
## Quick Start
|
|
91
|
+
|
|
92
|
+
### Prerequisites
|
|
93
|
+
|
|
94
|
+
- Node.js >= 22
|
|
95
|
+
- npm
|
|
96
|
+
- Docker (for Qdrant, SearXNG, and optionally Ollama)
|
|
97
|
+
|
|
98
|
+
### Install
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npm add @farukada/langchain-ts-rms
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Development infrastructure
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Start Qdrant (vector storage) and SearXNG (web search)
|
|
108
|
+
docker compose up -d qdrant searxng
|
|
109
|
+
|
|
110
|
+
# Optionally start Ollama (LLM inference)
|
|
111
|
+
docker compose --profile ollama up -d ollama
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Full operational steps and troubleshooting are in [docs/operations.md](./docs/operations.md).
|
|
115
|
+
|
|
116
|
+
<a id="use-as-library"></a>
|
|
117
|
+
|
|
118
|
+
## Use as Library
|
|
119
|
+
|
|
120
|
+
### Minimal registration
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import { createRmsToolFromEnv } from "@farukada/langchain-ts-rms";
|
|
124
|
+
|
|
125
|
+
const researchTool = await createRmsToolFromEnv();
|
|
126
|
+
agent.tools.push(researchTool);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Full lifecycle registration
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import {
|
|
133
|
+
createAllRmsToolsFromEnv,
|
|
134
|
+
} from "@farukada/langchain-ts-rms";
|
|
135
|
+
|
|
136
|
+
const { researchTool, lifecycleTools } = await createAllRmsToolsFromEnv();
|
|
137
|
+
agent.tools.push(researchTool, ...lifecycleTools);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Advanced: LangChain middleware (recommended)
|
|
141
|
+
|
|
142
|
+
When registering RMS tools with a `createAgent` orchestrator, apply [LangChain 1.1 middleware](https://langchain-ai.github.io/langgraphjs/) for production-grade resilience:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { createAgent } from "langchain";
|
|
146
|
+
import { toolRetryMiddleware, modelRetryMiddleware } from "langchain/middleware";
|
|
147
|
+
import { createAllRmsToolsFromEnv } from "@farukada/langchain-ts-rms";
|
|
148
|
+
|
|
149
|
+
const { researchTool, lifecycleTools } = await createAllRmsToolsFromEnv();
|
|
150
|
+
|
|
151
|
+
const agent = createAgent({
|
|
152
|
+
model,
|
|
153
|
+
tools: [researchTool, ...lifecycleTools],
|
|
154
|
+
middleware: [
|
|
155
|
+
toolRetryMiddleware({ maxRetries: 2 }),
|
|
156
|
+
modelRetryMiddleware({ maxRetries: 2 }),
|
|
157
|
+
],
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
> **Note:** Middleware is applied at the _consumer-agent_ level, not inside RMS itself. RMS graph nodes already have `retryPolicy: { maxAttempts: 3 }` for internal resilience.
|
|
162
|
+
|
|
163
|
+
<a id="streaming"></a>
|
|
164
|
+
|
|
165
|
+
## Streaming
|
|
166
|
+
|
|
167
|
+
The RMS workflow supports real-time streaming via LangGraph's `streamEvents` API. Use `streamResearch()` to iterate over node transitions, LLM tokens, and state updates as they happen:
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
import { streamResearch, ResearchRepository, createEmbeddingProvider } from "@farukada/langchain-ts-rms";
|
|
171
|
+
|
|
172
|
+
const embeddings = createEmbeddingProvider();
|
|
173
|
+
const repo = new ResearchRepository({ embeddings });
|
|
174
|
+
|
|
175
|
+
for await (const event of streamResearch(
|
|
176
|
+
{ subject: "quantum computing breakthroughs" },
|
|
177
|
+
{ researchRepository: repo },
|
|
178
|
+
)) {
|
|
179
|
+
// event.event: "on_chain_start" | "on_chain_end" | "on_llm_stream" | ...
|
|
180
|
+
// event.name: node name (e.g. "searcher", "summarizer")
|
|
181
|
+
// event.data: node input/output or token chunk
|
|
182
|
+
console.log(`[${event.event}] ${event.name}`);
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Stream event types
|
|
187
|
+
|
|
188
|
+
| Event | When it fires | Useful for |
|
|
189
|
+
|---|---|---|
|
|
190
|
+
| `on_chain_start` | A graph node begins | Progress indicators |
|
|
191
|
+
| `on_chain_end` | A graph node completes | State snapshots |
|
|
192
|
+
| `on_llm_stream` | LLM emits a token | Real-time text display |
|
|
193
|
+
| `on_llm_end` | LLM call completes | Token usage metrics |
|
|
194
|
+
|
|
195
|
+
> **Note:** `streamResearch()` is complementary to `conductResearchDirect()`. Use `.invoke()` (via `conductResearchDirect`) when you only need the final result. Use streaming when you need real-time progress.
|
|
196
|
+
|
|
197
|
+
<a id="integration-with-createagent"></a>
|
|
198
|
+
|
|
199
|
+
## Integration with createAgent
|
|
200
|
+
|
|
201
|
+
The latest LangChain.js standard is `createAgent()` from the `langchain` package (replacing the deprecated `createReactAgent`). RMS tools integrate directly:
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
import { createAgent } from "langchain";
|
|
205
|
+
import { createAllRmsToolsFromEnv } from "@farukada/langchain-ts-rms";
|
|
206
|
+
|
|
207
|
+
const { researchTool, lifecycleTools } = await createAllRmsToolsFromEnv();
|
|
208
|
+
|
|
209
|
+
const agent = createAgent({
|
|
210
|
+
model: "claude-sonnet-4-5-20250929",
|
|
211
|
+
tools: [researchTool, ...lifecycleTools],
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const result = await agent.invoke({
|
|
215
|
+
messages: [{ role: "user", content: "Research the latest AI safety frameworks" }],
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Middleware hooks
|
|
220
|
+
|
|
221
|
+
`createAgent` supports lifecycle hooks for cross-cutting concerns:
|
|
222
|
+
|
|
223
|
+
| Hook | Runs | Use case |
|
|
224
|
+
|---|---|---|
|
|
225
|
+
| `before_agent` | Once before agent starts | Load user context, long-term memory |
|
|
226
|
+
| `before_model` | Before each LLM call | Prompt injection, context trimming |
|
|
227
|
+
| `after_model` | After each LLM response | Output validation, guardrails |
|
|
228
|
+
| `after_agent` | Once after agent completes | Analytics, cleanup |
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
const agent = createAgent({
|
|
232
|
+
model: "claude-sonnet-4-5-20250929",
|
|
233
|
+
tools: [researchTool, ...lifecycleTools],
|
|
234
|
+
middleware: [
|
|
235
|
+
toolRetryMiddleware({ maxRetries: 2 }),
|
|
236
|
+
modelRetryMiddleware({ maxRetries: 2 }),
|
|
237
|
+
],
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
> **Note:** These hooks run at the _consumer-agent_ level. RMS's internal workflow uses its own retry policies and guardrails independently.
|
|
242
|
+
|
|
243
|
+
### Multi-tenancy
|
|
244
|
+
|
|
245
|
+
All tools support an optional `tenantId` field for data isolation. When set, research search, listing, and freshness evaluation are scoped to the given tenant:
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
const researchTool = await createRmsToolFromEnv();
|
|
249
|
+
// The agent passes tenantId when invoking the tool:
|
|
250
|
+
// { subject: "AI safety", tenantId: "org-123" }
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Qdrant payload indexes on `metadata.tenant_id` ensure filtered queries remain fast.
|
|
254
|
+
|
|
255
|
+
<a id="public-api-reference"></a>
|
|
256
|
+
|
|
257
|
+
## Public API Reference
|
|
258
|
+
|
|
259
|
+
### Main exports
|
|
260
|
+
|
|
261
|
+
- `createResearchTool(deps)`: create the main research tool (`rms_research`).
|
|
262
|
+
- `createRmsToolFromEnv(options)`: env-based research tool factory.
|
|
263
|
+
- `createRmsLifecycleTools(deps)`: create retrieval/mutation toolset.
|
|
264
|
+
- `createRmsLifecycleToolsFromEnv(options)`: env-based lifecycle factory.
|
|
265
|
+
- `createAllRmsToolsFromEnv(options)`: convenience factory for both research + lifecycle tools with shared dependency instances (recommended).
|
|
266
|
+
|
|
267
|
+
### Key result shape (`rms_research`)
|
|
268
|
+
|
|
269
|
+
```ts
|
|
270
|
+
{
|
|
271
|
+
version: "1.0",
|
|
272
|
+
research: Research, // { id, subject, summary, sourceUrls, tags, ... }
|
|
273
|
+
source: "cache" | "web" | "cache+web",
|
|
274
|
+
wasRefreshed: boolean
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Advanced dependency wiring (explicit repositories)
|
|
279
|
+
|
|
280
|
+
```ts
|
|
281
|
+
import {
|
|
282
|
+
createResearchTool,
|
|
283
|
+
ResearchRepository,
|
|
284
|
+
createEmbeddingProvider,
|
|
285
|
+
createChatModelProvider,
|
|
286
|
+
createSearxngClient,
|
|
287
|
+
} from "@farukada/langchain-ts-rms";
|
|
288
|
+
|
|
289
|
+
const embeddings = createEmbeddingProvider();
|
|
290
|
+
const chatModel = createChatModelProvider();
|
|
291
|
+
const searxngClient = createSearxngClient();
|
|
292
|
+
const researchRepository = new ResearchRepository({ embeddings });
|
|
293
|
+
|
|
294
|
+
const researchTool = createResearchTool({
|
|
295
|
+
researchRepository,
|
|
296
|
+
chatModel,
|
|
297
|
+
searxngClient,
|
|
298
|
+
embeddings,
|
|
299
|
+
});
|
|
300
|
+
agent.tools.push(researchTool);
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
<a id="package-exports"></a>
|
|
304
|
+
|
|
305
|
+
## Package Exports
|
|
306
|
+
|
|
307
|
+
The package exposes all public APIs from the main entry point:
|
|
308
|
+
|
|
309
|
+
| Export | Provides |
|
|
310
|
+
| ----------------------------------- | ---------------------------------------------------------- |
|
|
311
|
+
| `createResearchTool` | Main research tool factory |
|
|
312
|
+
| `createRmsToolFromEnv` | Env-driven research tool factory |
|
|
313
|
+
| `createRmsLifecycleTools` | All lifecycle tool factories |
|
|
314
|
+
| `createRmsLifecycleToolsFromEnv` | Env-driven lifecycle tools |
|
|
315
|
+
| `createAllRmsToolsFromEnv` | Both research + lifecycle, shared deps (recommended) |
|
|
316
|
+
| `createGetResearchTool` | Individual get-research tool factory |
|
|
317
|
+
| `createListResearchTool` | Individual list-research tool factory |
|
|
318
|
+
| `createSearchResearchTool` | Individual search-research tool factory |
|
|
319
|
+
| `createDeleteResearchTool` | Individual delete-research tool factory |
|
|
320
|
+
| `createRefreshResearchTool` | Individual refresh-research tool factory |
|
|
321
|
+
| `createGetDatetimeTool` | Individual datetime tool factory |
|
|
322
|
+
| `ResearchRepository` | Vector store repository class |
|
|
323
|
+
| `ResearchSchema`, `ResearchStatusSchema` | Zod schemas for domain validation |
|
|
324
|
+
|
|
325
|
+
<a id="tool-catalog"></a>
|
|
326
|
+
|
|
327
|
+
## Tool Catalog
|
|
328
|
+
|
|
329
|
+
### Research tool
|
|
330
|
+
|
|
331
|
+
- `rms_research`: Search the web, summarize results, cache in Qdrant. Returns cached results if fresh.
|
|
332
|
+
|
|
333
|
+
### Lifecycle tools
|
|
334
|
+
|
|
335
|
+
- `rms_get_research`: fetch a research entry by ID.
|
|
336
|
+
- `rms_list_research`: list entries with filtering and pagination.
|
|
337
|
+
- `rms_search_research`: semantic vector search across stored research.
|
|
338
|
+
- `rms_delete_research`: delete a research entry by ID.
|
|
339
|
+
- `rms_refresh_research`: force-refresh an existing research entry.
|
|
340
|
+
- `rms_get_datetime`: return current date and time information.
|
|
341
|
+
|
|
342
|
+
### Tool input/output reference
|
|
343
|
+
|
|
344
|
+
| Tool | Required input | Output focus |
|
|
345
|
+
| --------------------- | ---------------- | -------------------------------------------------- |
|
|
346
|
+
| `rms_research` | `subject` | `research`, `source`, `wasRefreshed` |
|
|
347
|
+
| `rms_get_research` | `researchId` | Full research object |
|
|
348
|
+
| `rms_list_research` | none | Filtered/paginated list + `total` |
|
|
349
|
+
| `rms_search_research` | `query` | Semantic search results with scores |
|
|
350
|
+
| `rms_delete_research` | `researchId` | Deletion confirmation |
|
|
351
|
+
| `rms_refresh_research`| `researchId` | Refreshed research object |
|
|
352
|
+
| `rms_get_datetime` | none | ISO, unix, date, time, timezone, dayOfWeek |
|
|
353
|
+
|
|
354
|
+
All tools accept **alias fields** for LLM compatibility (e.g., `topic`, `query`, `question` for `subject`; `research_id`, `id` for `researchId`).
|
|
355
|
+
|
|
356
|
+
<a id="configuration-reference"></a>
|
|
357
|
+
|
|
358
|
+
## Configuration Reference
|
|
359
|
+
|
|
360
|
+
Runtime environment variables:
|
|
361
|
+
|
|
362
|
+
| Variable | Required | Default | Purpose |
|
|
363
|
+
| ---------------------------- | -------- | -------------------------------------- | ---------------------------------------------------- |
|
|
364
|
+
| `NODE_ENV` | No | `development` | Runtime mode |
|
|
365
|
+
| `LOG_LEVEL` | No | `info` | Minimum log level (`debug`, `info`, `warn`, `error`) |
|
|
366
|
+
| `QDRANT_URL` | No | `http://localhost:6333` | Qdrant endpoint |
|
|
367
|
+
| `QDRANT_API_KEY` | No | - | Qdrant authentication key |
|
|
368
|
+
| `OLLAMA_HOST` | No | `http://localhost:11434` | Ollama server URL |
|
|
369
|
+
| `OLLAMA_EMBEDDING_MODEL` | No | `nomic-embed-text` | Default embedding model |
|
|
370
|
+
| `OLLAMA_CHAT_MODEL` | No | `qwen3:8b` | Default chat/summarization model |
|
|
371
|
+
| `RMS_OLLAMA_EMBEDDING_MODEL` | No | falls back to `OLLAMA_EMBEDDING_MODEL` | RMS-specific embedding model override |
|
|
372
|
+
| `RMS_OLLAMA_CHAT_MODEL` | No | falls back to `OLLAMA_CHAT_MODEL` | RMS-specific chat model override |
|
|
373
|
+
| `SEARXNG_API_BASE` | No | `http://localhost:8080` | SearXNG search API endpoint |
|
|
374
|
+
| `SEARXNG_NUM_RESULTS` | No | `10` | Default number of search results |
|
|
375
|
+
| `RMS_FRESHNESS_DAYS` | No | `7` | Days before cached research is considered stale |
|
|
376
|
+
|
|
377
|
+
<a id="production-considerations"></a>
|
|
378
|
+
|
|
379
|
+
## Production Considerations
|
|
380
|
+
|
|
381
|
+
- **Freshness tuning**: Adjust `RMS_FRESHNESS_DAYS` based on topic volatility.
|
|
382
|
+
- **SearXNG deployment**: Use a dedicated SearXNG instance with `json` format enabled.
|
|
383
|
+
- **Multi-tenancy**: Pass `tenantId` to isolate research per organization.
|
|
384
|
+
- **Qdrant sizing**: Monitor collection size and shard accordingly for large research volumes.
|
|
385
|
+
|
|
386
|
+
<a id="known-non-goals"></a>
|
|
387
|
+
|
|
388
|
+
## Known Non-Goals
|
|
389
|
+
|
|
390
|
+
- RMS does not execute actions based on research; it only provides factual summaries.
|
|
391
|
+
- RMS does not include citation-level provenance tracking in this package version.
|
|
392
|
+
- RMS does not manage user preferences or personalized search profiles.
|
|
393
|
+
|
|
394
|
+
<a id="testing-and-ci"></a>
|
|
395
|
+
|
|
396
|
+
## Testing and CI
|
|
397
|
+
|
|
398
|
+
### Local checks
|
|
399
|
+
|
|
400
|
+
| Command | Purpose |
|
|
401
|
+
| ----------------------- | -------------------------------------- |
|
|
402
|
+
| `npm test` | Unit tests (CI-safe, no external deps) |
|
|
403
|
+
| `npm run test:ci` | CI-safe unit test suite |
|
|
404
|
+
| `npm run test:watch` | Watch mode for development |
|
|
405
|
+
| `npm run test:coverage` | Unit tests with V8 coverage |
|
|
406
|
+
|
|
407
|
+
### CI pipeline
|
|
408
|
+
|
|
409
|
+
```mermaid
|
|
410
|
+
flowchart LR
|
|
411
|
+
sourceChange[PushOrPullRequest]
|
|
412
|
+
validate[Validate]
|
|
413
|
+
integrationAgent[IntegrationAgent]
|
|
414
|
+
publish[PublishToNpm]
|
|
415
|
+
|
|
416
|
+
sourceChange --> validate
|
|
417
|
+
validate -->|"push to main"| integrationAgent --> publish
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
<a id="documentation"></a>
|
|
421
|
+
|
|
422
|
+
## Documentation
|
|
423
|
+
|
|
424
|
+
- [Documentation Index](./docs/README.md)
|
|
425
|
+
- [Architecture](./docs/architecture.md)
|
|
426
|
+
- [Operations Runbook](./docs/operations.md)
|
|
427
|
+
- [ADR 0001: RAG-Centric RMS](./docs/adr/0001-rag-centric-rms.md)
|
|
428
|
+
|
|
429
|
+
<a id="project-structure"></a>
|
|
430
|
+
|
|
431
|
+
## Project Structure
|
|
432
|
+
|
|
433
|
+
```text
|
|
434
|
+
src/
|
|
435
|
+
├── app/ # Freshness evaluation, summarization, orchestration
|
|
436
|
+
├── lib/ # Public library API + tools
|
|
437
|
+
├── config/ # Environment configuration
|
|
438
|
+
├── domain/ # Core contracts and research utilities
|
|
439
|
+
└── infra/ # Qdrant, embeddings, SearXNG, observability
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
<a id="license"></a>
|
|
443
|
+
|
|
444
|
+
## License
|
|
445
|
+
|
|
446
|
+
MIT
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Research } from "../../domain/contracts.js";
|
|
2
|
+
import type { IResearchRepository } from "../../domain/ports.js";
|
|
3
|
+
export interface FreshnessResult {
|
|
4
|
+
isFresh: boolean;
|
|
5
|
+
cachedResearch: Research | null;
|
|
6
|
+
staleness: "fresh" | "stale" | "missing";
|
|
7
|
+
cacheAge?: number;
|
|
8
|
+
cacheAgeDays?: number;
|
|
9
|
+
score?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Evaluates whether existing cached research for a subject is still fresh.
|
|
13
|
+
*
|
|
14
|
+
* Returns:
|
|
15
|
+
* - `fresh` — cached data exists and has not expired
|
|
16
|
+
* - `stale` — cached data exists but has expired
|
|
17
|
+
* - `missing` — no cached data found for this subject
|
|
18
|
+
*/
|
|
19
|
+
export declare function evaluateFreshness(subject: string, repository: IResearchRepository, opts?: {
|
|
20
|
+
tenantId?: string | undefined;
|
|
21
|
+
now?: Date | undefined;
|
|
22
|
+
}): Promise<FreshnessResult>;
|
|
23
|
+
//# sourceMappingURL=evaluator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluator.d.ts","sourceRoot":"","sources":["../../../src/app/freshness/evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAIjE,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,QAAQ,GAAG,IAAI,CAAC;IAChC,SAAS,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,mBAAmB,EAC/B,IAAI,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,CAAA;CAAE,GAC/D,OAAO,CAAC,eAAe,CAAC,CAmE1B"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { isResearchFresh, getResearchAge, getResearchAgeDays } from "../../domain/researchUtils.js";
|
|
2
|
+
import { logInfo, logDebug } from "../../infra/observability/tracing.js";
|
|
3
|
+
/**
|
|
4
|
+
* Evaluates whether existing cached research for a subject is still fresh.
|
|
5
|
+
*
|
|
6
|
+
* Returns:
|
|
7
|
+
* - `fresh` — cached data exists and has not expired
|
|
8
|
+
* - `stale` — cached data exists but has expired
|
|
9
|
+
* - `missing` — no cached data found for this subject
|
|
10
|
+
*/
|
|
11
|
+
export async function evaluateFreshness(subject, repository, opts) {
|
|
12
|
+
const now = opts?.now ?? new Date();
|
|
13
|
+
logDebug("Evaluating freshness", { node: "evaluateFreshness", researchId: subject });
|
|
14
|
+
// Semantic search (vector similarity)
|
|
15
|
+
const results = await repository.findBySubject(subject, {
|
|
16
|
+
tenantId: opts?.tenantId,
|
|
17
|
+
k: 1,
|
|
18
|
+
});
|
|
19
|
+
// Enhanced retrieval: if semantic search returns low-confidence results,
|
|
20
|
+
// retry with tenant filter to catch cases where unfiltered search
|
|
21
|
+
// returned low confidence (both paths use vector similarity — Qdrant
|
|
22
|
+
// does not support keyword/BM25 search)
|
|
23
|
+
//
|
|
24
|
+
// Use a strict threshold (0.85) because vector similarity yields >0.6 even
|
|
25
|
+
// for completely unrelated sentences. A lower threshold allows conceptually
|
|
26
|
+
// distinct subjects (e.g. "quantum computing" vs "typescript 5.5") to falsely match,
|
|
27
|
+
// which can cause destructive overwrites during forceRefresh.
|
|
28
|
+
let bestResult = results[0];
|
|
29
|
+
if (!bestResult || bestResult.score < 0.85) {
|
|
30
|
+
logDebug("Low semantic score, retrying with tenant filter", {
|
|
31
|
+
node: "evaluateFreshness",
|
|
32
|
+
researchId: subject,
|
|
33
|
+
semanticScore: bestResult?.score,
|
|
34
|
+
});
|
|
35
|
+
const keywordResults = await repository.search(subject, {
|
|
36
|
+
k: 1,
|
|
37
|
+
filter: {
|
|
38
|
+
tenantId: opts?.tenantId,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
const keywordBest = keywordResults[0];
|
|
42
|
+
// Use keyword result if it's better or if semantic returned nothing
|
|
43
|
+
if (keywordBest && (!bestResult || keywordBest.score > bestResult.score)) {
|
|
44
|
+
bestResult = keywordBest;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (!bestResult) {
|
|
48
|
+
logInfo("No cached research found", {
|
|
49
|
+
node: "evaluateFreshness",
|
|
50
|
+
researchId: subject,
|
|
51
|
+
});
|
|
52
|
+
return { isFresh: false, cachedResearch: null, staleness: "missing" };
|
|
53
|
+
}
|
|
54
|
+
const { research, score } = bestResult;
|
|
55
|
+
const fresh = isResearchFresh(research, now);
|
|
56
|
+
const cacheAge = getResearchAge(research, now);
|
|
57
|
+
const cacheAgeDays = getResearchAgeDays(research, now);
|
|
58
|
+
logInfo("Freshness evaluated", {
|
|
59
|
+
node: "evaluateFreshness",
|
|
60
|
+
researchId: research.id,
|
|
61
|
+
durationMs: cacheAge,
|
|
62
|
+
});
|
|
63
|
+
return {
|
|
64
|
+
isFresh: fresh,
|
|
65
|
+
cachedResearch: research,
|
|
66
|
+
staleness: fresh ? "fresh" : "stale",
|
|
67
|
+
cacheAge,
|
|
68
|
+
cacheAgeDays,
|
|
69
|
+
score,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=evaluator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluator.js","sourceRoot":"","sources":["../../../src/app/freshness/evaluator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACpG,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAWzE;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAe,EACf,UAA+B,EAC/B,IAAgE;IAEhE,MAAM,GAAG,GAAG,IAAI,EAAE,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IAEpC,QAAQ,CAAC,sBAAsB,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC;IAErF,sCAAsC;IACtC,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,aAAa,CAAC,OAAO,EAAE;QACtD,QAAQ,EAAE,IAAI,EAAE,QAAQ;QACxB,CAAC,EAAE,CAAC;KACL,CAAC,CAAC;IAEH,yEAAyE;IACzE,kEAAkE;IAClE,qEAAqE;IACrE,wCAAwC;IACxC,EAAE;IACF,2EAA2E;IAC3E,4EAA4E;IAC5E,qFAAqF;IACrF,8DAA8D;IAC9D,IAAI,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC5B,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,KAAK,GAAG,IAAI,EAAE,CAAC;QAC3C,QAAQ,CAAC,iDAAiD,EAAE;YAC1D,IAAI,EAAE,mBAAmB;YACzB,UAAU,EAAE,OAAO;YACnB,aAAa,EAAE,UAAU,EAAE,KAAK;SACjC,CAAC,CAAC;QACH,MAAM,cAAc,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE;YACtD,CAAC,EAAE,CAAC;YACJ,MAAM,EAAE;gBACN,QAAQ,EAAE,IAAI,EAAE,QAAQ;aACzB;SACF,CAAC,CAAC;QACH,MAAM,WAAW,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;QACtC,oEAAoE;QACpE,IAAI,WAAW,IAAI,CAAC,CAAC,UAAU,IAAI,WAAW,CAAC,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACzE,UAAU,GAAG,WAAW,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,CAAC,0BAA0B,EAAE;YAClC,IAAI,EAAE,mBAAmB;YACzB,UAAU,EAAE,OAAO;SACpB,CAAC,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;IACxE,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC;IACvC,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC/C,MAAM,YAAY,GAAG,kBAAkB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAEvD,OAAO,CAAC,qBAAqB,EAAE;QAC7B,IAAI,EAAE,mBAAmB;QACzB,UAAU,EAAE,QAAQ,CAAC,EAAE;QACvB,UAAU,EAAE,QAAQ;KACrB,CAAC,CAAC;IAEH,OAAO;QACL,OAAO,EAAE,KAAK;QACd,cAAc,EAAE,QAAQ;QACxB,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO;QACpC,QAAQ;QACR,YAAY;QACZ,KAAK;KACN,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Research guardrails: policy enforcement for research queries.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors GMS's governance/guardrails.ts pattern but adapted for
|
|
5
|
+
* research operations (subject filtering, result count limits, HITL gating).
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Default forbidden patterns (case-insensitive) that block research queries.
|
|
9
|
+
* These prevent the system from searching for harmful or inappropriate content.
|
|
10
|
+
*/
|
|
11
|
+
export declare const DEFAULT_FORBIDDEN_PATTERNS: readonly string[];
|
|
12
|
+
/** Default maximum search result count before requiring human approval. */
|
|
13
|
+
export declare const DEFAULT_MAX_SEARCH_COUNT = 50;
|
|
14
|
+
/** Default threshold for low-confidence summarizations that trigger review. */
|
|
15
|
+
export declare const DEFAULT_MIN_CONFIDENCE = 0.3;
|
|
16
|
+
export interface GuardrailOptions {
|
|
17
|
+
/** Patterns (case-insensitive substrings) that block research. Defaults to `DEFAULT_FORBIDDEN_PATTERNS`. */
|
|
18
|
+
forbiddenPatterns?: readonly string[];
|
|
19
|
+
}
|
|
20
|
+
export interface HumanApprovalOptions {
|
|
21
|
+
/** Maximum number of search results before requiring human approval. Defaults to `DEFAULT_MAX_SEARCH_COUNT`. */
|
|
22
|
+
maxSearchCount?: number;
|
|
23
|
+
/** Minimum confidence score; below this triggers human review. Defaults to `DEFAULT_MIN_CONFIDENCE`. */
|
|
24
|
+
minConfidence?: number;
|
|
25
|
+
}
|
|
26
|
+
export type GuardrailCheck = {
|
|
27
|
+
allowed: true;
|
|
28
|
+
} | {
|
|
29
|
+
allowed: false;
|
|
30
|
+
reason: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Policy guardrail: checks a research subject against forbidden patterns.
|
|
34
|
+
* Returns { allowed: false, reason } if the subject violates policy.
|
|
35
|
+
*/
|
|
36
|
+
export declare function checkGuardrail(subject: string, options?: GuardrailOptions): GuardrailCheck;
|
|
37
|
+
/**
|
|
38
|
+
* Determines if a research operation requires human approval.
|
|
39
|
+
* Triggers on high result counts or low confidence scores.
|
|
40
|
+
*/
|
|
41
|
+
export declare function requiresHumanApproval(resultCount: number, confidenceScore?: number, options?: HumanApprovalOptions): boolean;
|
|
42
|
+
export interface GuardrailResult {
|
|
43
|
+
check: GuardrailCheck;
|
|
44
|
+
needsHumanApproval: boolean;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Evaluate both policy guardrails and human-approval requirements
|
|
48
|
+
* for a research operation.
|
|
49
|
+
*/
|
|
50
|
+
export declare function evaluateGuardrails(subject: string, resultCount: number, guardOpts?: GuardrailOptions, approvalOpts?: HumanApprovalOptions, confidenceScore?: number): GuardrailResult;
|
|
51
|
+
//# sourceMappingURL=guardrails.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardrails.d.ts","sourceRoot":"","sources":["../../../src/app/governance/guardrails.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EASvD,CAAC;AAEF,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,+EAA+E;AAC/E,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,MAAM,WAAW,gBAAgB;IAC/B,4GAA4G;IAC5G,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,oBAAoB;IACnC,gHAAgH;IAChH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wGAAwG;IACxG,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,cAAc,GAAG;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,cAAc,CAY9F;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,eAAe,CAAC,EAAE,MAAM,EACxB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAQT;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,cAAc,CAAC;IACtB,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,SAAS,GAAE,gBAAqB,EAChC,YAAY,GAAE,oBAAyB,EACvC,eAAe,CAAC,EAAE,MAAM,GACvB,eAAe,CAMjB"}
|