@lacneu/openclaw-knowledge 3.2.3 → 3.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +237 -0
- package/dist/config.js +27 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +11 -13
- package/dist/index.js +104 -12
- package/dist/index.js.map +1 -1
- package/dist/jina/classifier.d.ts +7 -2
- package/dist/jina/classifier.js +4 -2
- package/dist/jina/classifier.js.map +1 -1
- package/dist/jina/client.d.ts +11 -1
- package/dist/jina/client.js +5 -1
- package/dist/jina/client.js.map +1 -1
- package/dist/jina/rate-limit.d.ts +62 -0
- package/dist/jina/rate-limit.js +100 -0
- package/dist/jina/rate-limit.js.map +1 -0
- package/dist/jina/reranker.d.ts +4 -1
- package/dist/jina/reranker.js +2 -1
- package/dist/jina/reranker.js.map +1 -1
- package/dist/pgvector.d.ts +83 -0
- package/dist/pgvector.js +62 -9
- package/dist/pgvector.js.map +1 -1
- package/dist/provenance.d.ts +105 -0
- package/dist/provenance.js +186 -0
- package/dist/provenance.js.map +1 -0
- package/dist/router/index.d.ts +15 -0
- package/dist/router/index.js +9 -0
- package/dist/router/index.js.map +1 -1
- package/dist/tracing/events.d.ts +13 -1
- package/dist/tracing/events.js.map +1 -1
- package/dist/types.d.ts +68 -0
- package/openclaw.plugin.json +64 -6
- package/package.json +1 -1
package/dist/pgvector.js
CHANGED
|
@@ -54,17 +54,26 @@ export async function searchCollection(pool, collection, vector, topK, scoreThre
|
|
|
54
54
|
.filter((row) => row.score >= scoreThreshold);
|
|
55
55
|
}
|
|
56
56
|
/**
|
|
57
|
-
* Format pgvector results for injection into the system prompt
|
|
58
|
-
*
|
|
59
|
-
* single oversized chunk — entries are appended whole until the budget is hit.
|
|
57
|
+
* Format pgvector results for injection into the system prompt AND report
|
|
58
|
+
* how many input entries were actually included.
|
|
60
59
|
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
60
|
+
* Internal sibling of the public `formatPgvectorResults`. The public
|
|
61
|
+
* variant returns only the prompt text and is kept backward-compatible;
|
|
62
|
+
* this one exposes `injectedCount` so the plugin's render path can size
|
|
63
|
+
* the provenance report to the injected subset (contract: "emit what
|
|
64
|
+
* was injected, not what was retrieved").
|
|
65
|
+
*
|
|
66
|
+
* Returns `null` when there is nothing useful to inject (empty input OR
|
|
67
|
+
* the budget cannot fit a single entry) so the caller can skip empty
|
|
68
|
+
* sections AND skip emitting a provenance report for zero injected items.
|
|
69
|
+
*
|
|
70
|
+
* @since 3.2.5
|
|
63
71
|
*/
|
|
64
|
-
export function
|
|
72
|
+
export function formatPgvectorResultsDetailed(results, maxChars) {
|
|
65
73
|
if (results.length === 0)
|
|
66
74
|
return null;
|
|
67
75
|
let output = "";
|
|
76
|
+
let injectedCount = 0;
|
|
68
77
|
for (const r of results) {
|
|
69
78
|
const lines = [
|
|
70
79
|
`[${r.collection}] ${r.file_name ?? "unknown"} (score: ${r.score.toFixed(2)})`,
|
|
@@ -80,8 +89,29 @@ export function formatPgvectorResults(results, maxChars) {
|
|
|
80
89
|
if (output.length + entry.length > maxChars)
|
|
81
90
|
break;
|
|
82
91
|
output += entry;
|
|
92
|
+
injectedCount += 1;
|
|
83
93
|
}
|
|
84
|
-
|
|
94
|
+
// No entry fit the budget — equivalent to "nothing to inject" so the
|
|
95
|
+
// caller skips the header AND the provenance report. Preserves the
|
|
96
|
+
// pre-3.2.5 `if (!formatted)` semantics when output was an empty string.
|
|
97
|
+
if (injectedCount === 0)
|
|
98
|
+
return null;
|
|
99
|
+
return { output, injectedCount };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Format pgvector results for injection into the system prompt.
|
|
103
|
+
* Respects a character budget so we never blow the context window with a
|
|
104
|
+
* single oversized chunk — entries are appended whole until the budget is hit.
|
|
105
|
+
*
|
|
106
|
+
* Returns `null` when there is nothing useful to inject so the caller can
|
|
107
|
+
* easily skip empty sections.
|
|
108
|
+
*
|
|
109
|
+
* Public API — signature kept stable across the v3.x line. Callers needing
|
|
110
|
+
* the count of entries actually injected should use
|
|
111
|
+
* `formatPgvectorResultsDetailed` (internal to this package).
|
|
112
|
+
*/
|
|
113
|
+
export function formatPgvectorResults(results, maxChars) {
|
|
114
|
+
return formatPgvectorResultsDetailed(results, maxChars)?.output ?? null;
|
|
85
115
|
}
|
|
86
116
|
/**
|
|
87
117
|
* Re-order `results` using the Jina cross-encoder reranker.
|
|
@@ -107,21 +137,44 @@ export async function rerankPgvectorResults(results, params) {
|
|
|
107
137
|
// The reranker only sees the textual content. Empty/null texts cannot be
|
|
108
138
|
// ranked, so we filter them out BEFORE the API call to avoid wasting
|
|
109
139
|
// tokens on rows the cross-encoder can't score anyway.
|
|
110
|
-
|
|
140
|
+
let indexed = results
|
|
111
141
|
.map((r, i) => ({ row: r, originalIndex: i, text: r.text ?? "" }))
|
|
112
142
|
.filter((x) => x.text.trim().length > 0);
|
|
113
143
|
if (indexed.length === 0)
|
|
114
144
|
return results.slice(0, params.topN ?? results.length);
|
|
145
|
+
// 3.2.4 — payload-size guards. Both are no-ops when undefined so the
|
|
146
|
+
// legacy behavior is preserved for callers that haven't migrated.
|
|
147
|
+
if (typeof params.candidatePoolMax === "number" &&
|
|
148
|
+
params.candidatePoolMax > 0 &&
|
|
149
|
+
indexed.length > params.candidatePoolMax) {
|
|
150
|
+
// `results` is already cosine-sorted by the caller (runPgvectorSource).
|
|
151
|
+
// `indexed` preserves that order via the `originalIndex` mapping, so
|
|
152
|
+
// slicing to the first N keeps the best candidates and drops the tail.
|
|
153
|
+
indexed = indexed.slice(0, params.candidatePoolMax);
|
|
154
|
+
}
|
|
155
|
+
const documents = typeof params.maxCharsPerDoc === "number" && params.maxCharsPerDoc > 0
|
|
156
|
+
? indexed.map((x) => x.text.slice(0, params.maxCharsPerDoc))
|
|
157
|
+
: indexed.map((x) => x.text);
|
|
158
|
+
const startedAt = Date.now();
|
|
115
159
|
try {
|
|
116
160
|
const reranked = await rerank({
|
|
117
161
|
apiKey: params.apiKey,
|
|
118
162
|
query: params.query,
|
|
119
|
-
documents
|
|
163
|
+
documents,
|
|
120
164
|
model: params.model,
|
|
121
165
|
topN: params.topN,
|
|
122
166
|
timeoutMs: params.timeoutMs,
|
|
123
167
|
signal: params.signal,
|
|
168
|
+
rpmMonitor: params.rpmMonitor,
|
|
124
169
|
});
|
|
170
|
+
if (params.onUsage) {
|
|
171
|
+
const totalChars = documents.reduce((sum, d) => sum + d.length, 0);
|
|
172
|
+
params.onUsage({
|
|
173
|
+
inputCount: documents.length,
|
|
174
|
+
totalChars,
|
|
175
|
+
durationMs: Date.now() - startedAt,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
125
178
|
if (reranked.length === 0) {
|
|
126
179
|
// Defensive: reranker returned no usable items. Surface the original
|
|
127
180
|
// cosine order rather than wiping the candidate list.
|
package/dist/pgvector.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pgvector.js","sourceRoot":"","sources":["../src/pgvector.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,EAAE;AACF,iEAAiE;AACjE,2EAA2E;AAC3E,6EAA6E;AAC7E,0EAA0E;AAC1E,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,wEAAwE;AAExE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"pgvector.js","sourceRoot":"","sources":["../src/pgvector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { PluginLogger } from "openclaw/plugin-sdk/plugin-entry";
|
|
2
|
+
import type { PgvectorResult } from "./types.js";
|
|
3
|
+
export type ProvenanceReportLevel = "off" | "metadata" | "full";
|
|
4
|
+
export declare const PROVENANCE_REPORT_LEVELS: readonly ProvenanceReportLevel[];
|
|
5
|
+
/** Gateway-scoped stream this plugin emits on (see contract §2). */
|
|
6
|
+
export declare const PROVENANCE_STREAM = "openclaw-knowledge.provenance";
|
|
7
|
+
/** Mirrors the consumer's per-item excerpt bound (contract §3). */
|
|
8
|
+
export declare const PROVENANCE_EXCERPT_MAX_CHARS = 2000;
|
|
9
|
+
/** Mirrors the consumer's per-report item cap (contract §3). */
|
|
10
|
+
export declare const PROVENANCE_MAX_ITEMS = 24;
|
|
11
|
+
export interface ProvenanceItemV1 {
|
|
12
|
+
id?: string;
|
|
13
|
+
type?: string;
|
|
14
|
+
date?: string;
|
|
15
|
+
score?: number;
|
|
16
|
+
text?: string;
|
|
17
|
+
file_name?: string;
|
|
18
|
+
collection?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ProvenanceReportV1 {
|
|
21
|
+
v: 1;
|
|
22
|
+
source: "knowledge";
|
|
23
|
+
kind: "documents";
|
|
24
|
+
injected?: {
|
|
25
|
+
chars?: number;
|
|
26
|
+
position?: string;
|
|
27
|
+
truncated?: boolean;
|
|
28
|
+
};
|
|
29
|
+
retrieval?: {
|
|
30
|
+
route?: string;
|
|
31
|
+
collections?: string[];
|
|
32
|
+
lightrag?: {
|
|
33
|
+
mode?: string;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
items: ProvenanceItemV1[];
|
|
37
|
+
}
|
|
38
|
+
/** Structural emitAgentEvent surface (the installed SDK types may predate it). */
|
|
39
|
+
export type EmitAgentEventFn = (event: {
|
|
40
|
+
runId: string;
|
|
41
|
+
sessionKey?: string;
|
|
42
|
+
stream: string;
|
|
43
|
+
data: unknown;
|
|
44
|
+
}) => unknown;
|
|
45
|
+
/** Normalize the operator's config value; anything off-list means "off". */
|
|
46
|
+
export declare function resolveProvenanceLevel(raw: unknown): ProvenanceReportLevel;
|
|
47
|
+
/**
|
|
48
|
+
* Feature-detect emitAgentEvent on the (first-registration) plugin api.
|
|
49
|
+
*
|
|
50
|
+
* GATEWAY QUIRK (bench-verified 2026-06-12): the agent runtime RE-REGISTERS
|
|
51
|
+
* plugins per run, and emitting through a re-registration's api is rejected
|
|
52
|
+
* `"plugin is not loaded"` — callers MUST pass the FIRST registration's api
|
|
53
|
+
* (module-level singleton; see registerKnowledgePlugin).
|
|
54
|
+
*/
|
|
55
|
+
export declare function resolveEmitAgentEvent(api: unknown): EmitAgentEventFn | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Report for an injected pgvector section. `data` is the FINAL injected list
|
|
58
|
+
* (post-rerank, post-topN — exactly what reached the LLM). Returns null when
|
|
59
|
+
* the level is "off" or there is nothing citable.
|
|
60
|
+
*/
|
|
61
|
+
export declare function buildPgvectorProvenance(data: PgvectorResult[], collections: string[], level: ProvenanceReportLevel, injectedChars: number): ProvenanceReportV1 | null;
|
|
62
|
+
/**
|
|
63
|
+
* Report for an injected LightRAG section. The graph response is one opaque
|
|
64
|
+
* context blob (no per-document structure yet — the docstore initiative will
|
|
65
|
+
* enrich this), so the report carries ONE item describing the injected
|
|
66
|
+
* context; "full" includes its leading excerpt VERBATIM from what was
|
|
67
|
+
* actually injected (the post-truncation text).
|
|
68
|
+
*
|
|
69
|
+
* @param injectedText body of the section actually delivered to the LLM
|
|
70
|
+
* (post-truncation, header EXCLUDED). Used for the
|
|
71
|
+
* `full`-level excerpt.
|
|
72
|
+
* @param mode LightRAG query mode (`mix`, `hybrid`, …).
|
|
73
|
+
* @param level provenance gate (`off` / `metadata` / `full`).
|
|
74
|
+
* @param injectedChars total length of the system-prompt section actually
|
|
75
|
+
* delivered (header INCLUDED). Defaults to
|
|
76
|
+
* `injectedText.length` for callers that don't track
|
|
77
|
+
* the header separately; the plugin's render path
|
|
78
|
+
* passes the full section length so `injected.chars`
|
|
79
|
+
* reflects exactly what reached the LLM
|
|
80
|
+
* (codex pass #36 P3). Negative values fall back to
|
|
81
|
+
* `injectedText.length` to keep the report
|
|
82
|
+
* well-formed even on caller mistakes.
|
|
83
|
+
*/
|
|
84
|
+
export declare function buildLightRAGProvenance(injectedText: string, mode: string, level: ProvenanceReportLevel, injectedChars?: number): ProvenanceReportV1 | null;
|
|
85
|
+
/**
|
|
86
|
+
* Stable categories logged when emission fails. Operators triage by code;
|
|
87
|
+
* the underlying gateway reason / Error message is intentionally NEVER
|
|
88
|
+
* passed to the logger because in `full` mode it may echo back the
|
|
89
|
+
* payload (excerpts of the injected documents) it was rejecting — and the
|
|
90
|
+
* tracing invariant of this module is that report content never reaches
|
|
91
|
+
* logs.
|
|
92
|
+
*
|
|
93
|
+
* @since 3.2.5 (codex pass #36 P2 — sanitize emission failure logs)
|
|
94
|
+
*/
|
|
95
|
+
export type ProvenanceEmitFailureCode = "plugin_not_loaded" | "missing_run_context" | "invalid_stream" | "validation_error" | "rate_limited" | "rejected" | "throw";
|
|
96
|
+
/**
|
|
97
|
+
* Emit the turn's reports. Every guard degrades to SILENCE (a missing SDK
|
|
98
|
+
* function, a missing runId, a gateway rejection) — provenance must never
|
|
99
|
+
* break or delay a turn. Failures are logged as a STABLE CATEGORY CODE
|
|
100
|
+
* (never the raw gateway reason or Error message) because a silent
|
|
101
|
+
* `{emitted:false}` is undiagnosable in the field, but echoing the raw
|
|
102
|
+
* reason could leak fragments of the injected payload back into the log
|
|
103
|
+
* stream — violating the module's tracing invariant.
|
|
104
|
+
*/
|
|
105
|
+
export declare function emitProvenanceReports(emit: EmitAgentEventFn | undefined, logger: PluginLogger, runId: string | undefined, sessionKey: string | undefined, reports: (ProvenanceReportV1 | null)[]): void;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// Provenance reporting (provenance/v1) — what this plugin ACTUALLY injected
|
|
2
|
+
// into the LLM, emitted on the gateway agent-event bus so a chat frontend
|
|
3
|
+
// (openclaw-webchat) can show the user "which documents fed this reply".
|
|
4
|
+
//
|
|
5
|
+
// Normative contract: openclaw-webchat docs/PROVENANCE_CONTRACT.md.
|
|
6
|
+
// Transport rules (gateway-enforced, bench-verified 2026-06-12):
|
|
7
|
+
// - stream MUST be scoped to this plugin: "openclaw-knowledge.provenance";
|
|
8
|
+
// - runId is REQUIRED (the before_prompt_build ctx carries it);
|
|
9
|
+
// - the gateway stamps pluginId/pluginName into `data` (authenticated
|
|
10
|
+
// emitter identity — we never claim it ourselves);
|
|
11
|
+
// - a violation returns `{emitted:false, reason}` WITHOUT throwing.
|
|
12
|
+
//
|
|
13
|
+
// PRIVACY: this channel is DISTINCT from the tracing events module — reports
|
|
14
|
+
// land in the chat frontend's store under the chat's own ACL. The level knob
|
|
15
|
+
// keeps content out unless the operator opts in:
|
|
16
|
+
// - "off" (default): no emission at all;
|
|
17
|
+
// - "metadata": items WITHOUT text (file names, collections, scores);
|
|
18
|
+
// - "full": plus the exact injected excerpts.
|
|
19
|
+
// The tracing invariant is untouched: NOTHING here goes through logs.
|
|
20
|
+
export const PROVENANCE_REPORT_LEVELS = [
|
|
21
|
+
"off",
|
|
22
|
+
"metadata",
|
|
23
|
+
"full",
|
|
24
|
+
];
|
|
25
|
+
/** Gateway-scoped stream this plugin emits on (see contract §2). */
|
|
26
|
+
export const PROVENANCE_STREAM = "openclaw-knowledge.provenance";
|
|
27
|
+
/** Mirrors the consumer's per-item excerpt bound (contract §3). */
|
|
28
|
+
export const PROVENANCE_EXCERPT_MAX_CHARS = 2_000;
|
|
29
|
+
/** Mirrors the consumer's per-report item cap (contract §3). */
|
|
30
|
+
export const PROVENANCE_MAX_ITEMS = 24;
|
|
31
|
+
/** Normalize the operator's config value; anything off-list means "off". */
|
|
32
|
+
export function resolveProvenanceLevel(raw) {
|
|
33
|
+
return PROVENANCE_REPORT_LEVELS.includes(raw)
|
|
34
|
+
? raw
|
|
35
|
+
: "off";
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Feature-detect emitAgentEvent on the (first-registration) plugin api.
|
|
39
|
+
*
|
|
40
|
+
* GATEWAY QUIRK (bench-verified 2026-06-12): the agent runtime RE-REGISTERS
|
|
41
|
+
* plugins per run, and emitting through a re-registration's api is rejected
|
|
42
|
+
* `"plugin is not loaded"` — callers MUST pass the FIRST registration's api
|
|
43
|
+
* (module-level singleton; see registerKnowledgePlugin).
|
|
44
|
+
*/
|
|
45
|
+
export function resolveEmitAgentEvent(api) {
|
|
46
|
+
const candidate = api?.emitAgentEvent;
|
|
47
|
+
if (typeof candidate !== "function")
|
|
48
|
+
return undefined;
|
|
49
|
+
return candidate.bind(api);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Report for an injected pgvector section. `data` is the FINAL injected list
|
|
53
|
+
* (post-rerank, post-topN — exactly what reached the LLM). Returns null when
|
|
54
|
+
* the level is "off" or there is nothing citable.
|
|
55
|
+
*/
|
|
56
|
+
export function buildPgvectorProvenance(data, collections, level, injectedChars) {
|
|
57
|
+
if (level === "off" || data.length === 0)
|
|
58
|
+
return null;
|
|
59
|
+
const items = data
|
|
60
|
+
.slice(0, PROVENANCE_MAX_ITEMS)
|
|
61
|
+
.map((r) => {
|
|
62
|
+
const item = { collection: r.collection, score: r.score };
|
|
63
|
+
if (r.file_name)
|
|
64
|
+
item.file_name = r.file_name;
|
|
65
|
+
else if (r.file_id)
|
|
66
|
+
item.id = r.file_id;
|
|
67
|
+
if (level === "full" && r.text) {
|
|
68
|
+
item.text = r.text.slice(0, PROVENANCE_EXCERPT_MAX_CHARS);
|
|
69
|
+
}
|
|
70
|
+
return item;
|
|
71
|
+
});
|
|
72
|
+
return {
|
|
73
|
+
v: 1,
|
|
74
|
+
source: "knowledge",
|
|
75
|
+
kind: "documents",
|
|
76
|
+
injected: { chars: injectedChars, position: "system_append" },
|
|
77
|
+
retrieval: { route: "pgvector", collections },
|
|
78
|
+
items,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Report for an injected LightRAG section. The graph response is one opaque
|
|
83
|
+
* context blob (no per-document structure yet — the docstore initiative will
|
|
84
|
+
* enrich this), so the report carries ONE item describing the injected
|
|
85
|
+
* context; "full" includes its leading excerpt VERBATIM from what was
|
|
86
|
+
* actually injected (the post-truncation text).
|
|
87
|
+
*
|
|
88
|
+
* @param injectedText body of the section actually delivered to the LLM
|
|
89
|
+
* (post-truncation, header EXCLUDED). Used for the
|
|
90
|
+
* `full`-level excerpt.
|
|
91
|
+
* @param mode LightRAG query mode (`mix`, `hybrid`, …).
|
|
92
|
+
* @param level provenance gate (`off` / `metadata` / `full`).
|
|
93
|
+
* @param injectedChars total length of the system-prompt section actually
|
|
94
|
+
* delivered (header INCLUDED). Defaults to
|
|
95
|
+
* `injectedText.length` for callers that don't track
|
|
96
|
+
* the header separately; the plugin's render path
|
|
97
|
+
* passes the full section length so `injected.chars`
|
|
98
|
+
* reflects exactly what reached the LLM
|
|
99
|
+
* (codex pass #36 P3). Negative values fall back to
|
|
100
|
+
* `injectedText.length` to keep the report
|
|
101
|
+
* well-formed even on caller mistakes.
|
|
102
|
+
*/
|
|
103
|
+
export function buildLightRAGProvenance(injectedText, mode, level, injectedChars) {
|
|
104
|
+
if (level === "off" || injectedText.length === 0)
|
|
105
|
+
return null;
|
|
106
|
+
const item = { id: "lightrag-context", type: mode };
|
|
107
|
+
if (level === "full") {
|
|
108
|
+
item.text = injectedText.slice(0, PROVENANCE_EXCERPT_MAX_CHARS);
|
|
109
|
+
}
|
|
110
|
+
const reportedChars = typeof injectedChars === "number" && injectedChars >= 0
|
|
111
|
+
? injectedChars
|
|
112
|
+
: injectedText.length;
|
|
113
|
+
return {
|
|
114
|
+
v: 1,
|
|
115
|
+
source: "knowledge",
|
|
116
|
+
kind: "documents",
|
|
117
|
+
injected: { chars: reportedChars, position: "system_append" },
|
|
118
|
+
retrieval: { route: "lightrag", lightrag: { mode } },
|
|
119
|
+
items: [item],
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Map a gateway-supplied `reason` to a stable category code. Matching is
|
|
124
|
+
* substring-based on the lower-cased reason — coarse on purpose so a
|
|
125
|
+
* future gateway wording change does not break the classifier silently.
|
|
126
|
+
* Unknown reasons collapse to `"rejected"` so the field always has a
|
|
127
|
+
* non-empty, content-free value.
|
|
128
|
+
*/
|
|
129
|
+
function classifyRejectionReason(reason) {
|
|
130
|
+
if (!reason)
|
|
131
|
+
return "rejected";
|
|
132
|
+
const lower = reason.toLowerCase();
|
|
133
|
+
if (lower.includes("not loaded"))
|
|
134
|
+
return "plugin_not_loaded";
|
|
135
|
+
if (lower.includes("runid") || lower.includes("session")) {
|
|
136
|
+
return "missing_run_context";
|
|
137
|
+
}
|
|
138
|
+
if (lower.includes("stream"))
|
|
139
|
+
return "invalid_stream";
|
|
140
|
+
if (lower.includes("validation") || lower.includes("schema")) {
|
|
141
|
+
return "validation_error";
|
|
142
|
+
}
|
|
143
|
+
if (lower.includes("rate") || lower.includes("limit"))
|
|
144
|
+
return "rate_limited";
|
|
145
|
+
return "rejected";
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Emit the turn's reports. Every guard degrades to SILENCE (a missing SDK
|
|
149
|
+
* function, a missing runId, a gateway rejection) — provenance must never
|
|
150
|
+
* break or delay a turn. Failures are logged as a STABLE CATEGORY CODE
|
|
151
|
+
* (never the raw gateway reason or Error message) because a silent
|
|
152
|
+
* `{emitted:false}` is undiagnosable in the field, but echoing the raw
|
|
153
|
+
* reason could leak fragments of the injected payload back into the log
|
|
154
|
+
* stream — violating the module's tracing invariant.
|
|
155
|
+
*/
|
|
156
|
+
export function emitProvenanceReports(emit, logger, runId, sessionKey, reports) {
|
|
157
|
+
if (!emit)
|
|
158
|
+
return;
|
|
159
|
+
if (!runId)
|
|
160
|
+
return; // gateway-required; absent on some triggers
|
|
161
|
+
for (const report of reports) {
|
|
162
|
+
if (report === null)
|
|
163
|
+
continue;
|
|
164
|
+
try {
|
|
165
|
+
const res = emit({
|
|
166
|
+
runId,
|
|
167
|
+
...(sessionKey ? { sessionKey } : {}),
|
|
168
|
+
stream: PROVENANCE_STREAM,
|
|
169
|
+
data: report,
|
|
170
|
+
});
|
|
171
|
+
if (res && res.emitted === false) {
|
|
172
|
+
const code = classifyRejectionReason(res.reason);
|
|
173
|
+
logger.warn(`openclaw-knowledge: provenance report rejected — ${code}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
// Log only the Error.name (`TypeError`, `RangeError`, ...) — NEVER the
|
|
178
|
+
// `.message`, which third-party libs commonly enrich with input data.
|
|
179
|
+
const name = err && typeof err === "object" && typeof err.name === "string"
|
|
180
|
+
? err.name
|
|
181
|
+
: "Error";
|
|
182
|
+
logger.warn(`openclaw-knowledge: provenance emit failed — throw:${name}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=provenance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provenance.js","sourceRoot":"","sources":["../src/provenance.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,0EAA0E;AAC1E,yEAAyE;AACzE,EAAE;AACF,oEAAoE;AACpE,iEAAiE;AACjE,6EAA6E;AAC7E,kEAAkE;AAClE,wEAAwE;AACxE,uDAAuD;AACvD,sEAAsE;AACtE,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,iDAAiD;AACjD,gDAAgD;AAChD,wEAAwE;AACxE,oDAAoD;AACpD,sEAAsE;AAOtE,MAAM,CAAC,MAAM,wBAAwB,GAAqC;IACxE,KAAK;IACL,UAAU;IACV,MAAM;CACP,CAAC;AAEF,oEAAoE;AACpE,MAAM,CAAC,MAAM,iBAAiB,GAAG,+BAA+B,CAAC;AAEjE,mEAAmE;AACnE,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAElD,gEAAgE;AAChE,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAiCvC,4EAA4E;AAC5E,MAAM,UAAU,sBAAsB,CAAC,GAAY;IACjD,OAAO,wBAAwB,CAAC,QAAQ,CAAC,GAA4B,CAAC;QACpE,CAAC,CAAE,GAA6B;QAChC,CAAC,CAAC,KAAK,CAAC;AACZ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAY;IAChD,MAAM,SAAS,GAAI,GAAoC,EAAE,cAAc,CAAC;IACxE,IAAI,OAAO,SAAS,KAAK,UAAU;QAAE,OAAO,SAAS,CAAC;IACtD,OAAO,SAAS,CAAC,IAAI,CAAC,GAAG,CAAqB,CAAC;AACjD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CACrC,IAAsB,EACtB,WAAqB,EACrB,KAA4B,EAC5B,aAAqB;IAErB,IAAI,KAAK,KAAK,KAAK,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtD,MAAM,KAAK,GAAuB,IAAI;SACnC,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC;SAC9B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,IAAI,GAAqB,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;QAC5E,IAAI,CAAC,CAAC,SAAS;YAAE,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC;aACzC,IAAI,CAAC,CAAC,OAAO;YAAE,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC;QACxC,IAAI,KAAK,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;YAC/B,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,4BAA4B,CAAC,CAAC;QAC5D,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;IACL,OAAO;QACL,CAAC,EAAE,CAAC;QACJ,MAAM,EAAE,WAAW;QACnB,IAAI,EAAE,WAAW;QACjB,QAAQ,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,eAAe,EAAE;QAC7D,SAAS,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE;QAC7C,KAAK;KACN,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,uBAAuB,CACrC,YAAoB,EACpB,IAAY,EACZ,KAA4B,EAC5B,aAAsB;IAEtB,IAAI,KAAK,KAAK,KAAK,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9D,MAAM,IAAI,GAAqB,EAAE,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACtE,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,4BAA4B,CAAC,CAAC;IAClE,CAAC;IACD,MAAM,aAAa,GACjB,OAAO,aAAa,KAAK,QAAQ,IAAI,aAAa,IAAI,CAAC;QACrD,CAAC,CAAC,aAAa;QACf,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC;IAC1B,OAAO;QACL,CAAC,EAAE,CAAC;QACJ,MAAM,EAAE,WAAW;QACnB,IAAI,EAAE,WAAW;QACjB,QAAQ,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,eAAe,EAAE;QAC7D,SAAS,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE;QACpD,KAAK,EAAE,CAAC,IAAI,CAAC;KACd,CAAC;AACJ,CAAC;AAqBD;;;;;;GAMG;AACH,SAAS,uBAAuB,CAAC,MAA0B;IACzD,IAAI,CAAC,MAAM;QAAE,OAAO,UAAU,CAAC;IAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IACnC,IAAI,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC;QAAE,OAAO,mBAAmB,CAAC;IAC7D,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACzD,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,gBAAgB,CAAC;IACtD,IAAI,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7D,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,cAAc,CAAC;IAC7E,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CACnC,IAAkC,EAClC,MAAoB,EACpB,KAAyB,EACzB,UAA8B,EAC9B,OAAsC;IAEtC,IAAI,CAAC,IAAI;QAAE,OAAO;IAClB,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,4CAA4C;IAChE,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,KAAK,IAAI;YAAE,SAAS;QAC9B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC;gBACf,KAAK;gBACL,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACrC,MAAM,EAAE,iBAAiB;gBACzB,IAAI,EAAE,MAAM;aACb,CAAuD,CAAC;YACzD,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;gBACjC,MAAM,IAAI,GAAG,uBAAuB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBACjD,MAAM,CAAC,IAAI,CAAC,oDAAoD,IAAI,EAAE,CAAC,CAAC;YAC1E,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,uEAAuE;YACvE,sEAAsE;YACtE,MAAM,IAAI,GACR,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAQ,GAAa,CAAC,IAAI,KAAK,QAAQ;gBACvE,CAAC,CAAE,GAAa,CAAC,IAAI;gBACrB,CAAC,CAAC,OAAO,CAAC;YACd,MAAM,CAAC,IAAI,CAAC,sDAAsD,IAAI,EAAE,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;AACH,CAAC"}
|
package/dist/router/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RpmMonitor } from "../jina/rate-limit.js";
|
|
1
2
|
import type { RouterDecision } from "./types.js";
|
|
2
3
|
export interface RouterConfig {
|
|
3
4
|
/** Master switch. When false, every call returns `{route: "ALL"}`. */
|
|
@@ -32,6 +33,20 @@ export interface RouterConfig {
|
|
|
32
33
|
* Trusting only confident scores keeps the gate fail-safe.
|
|
33
34
|
*/
|
|
34
35
|
minConfidence?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Optional callback fired AFTER a successful Jina `/v1/classify` call
|
|
38
|
+
* with payload-level numbers (duration, model). The plugin uses it to
|
|
39
|
+
* emit a `jina` usage event so dashboards can track classify spend
|
|
40
|
+
* per turn independently of the router decision.
|
|
41
|
+
*
|
|
42
|
+
* @since 3.2.4
|
|
43
|
+
*/
|
|
44
|
+
onClassifierUsage?: (usage: {
|
|
45
|
+
durationMs: number;
|
|
46
|
+
model: "zero-shot" | "few-shot";
|
|
47
|
+
}) => void;
|
|
48
|
+
/** Optional RPM monitor (forwarded to the Jina client). @since 3.2.4 */
|
|
49
|
+
rpmMonitor?: RpmMonitor;
|
|
35
50
|
}
|
|
36
51
|
/**
|
|
37
52
|
* Default applied by {@link decideRoute} when a caller omits
|
package/dist/router/index.js
CHANGED
|
@@ -66,6 +66,7 @@ export async function decideRoute(cfg, ctx) {
|
|
|
66
66
|
return { route: FALLBACK, reason: "classifier_fallback", score: null };
|
|
67
67
|
}
|
|
68
68
|
try {
|
|
69
|
+
const startedAt = Date.now();
|
|
69
70
|
const outcome = cfg.classifierId
|
|
70
71
|
? await classifyFewShot({
|
|
71
72
|
apiKey: cfg.jinaApiKey,
|
|
@@ -73,13 +74,21 @@ export async function decideRoute(cfg, ctx) {
|
|
|
73
74
|
classifierId: cfg.classifierId,
|
|
74
75
|
expectedLabels: ROUTER_LABEL_NAMES,
|
|
75
76
|
signal: ctx.signal,
|
|
77
|
+
rpmMonitor: cfg.rpmMonitor,
|
|
76
78
|
})
|
|
77
79
|
: await classifyZeroShot({
|
|
78
80
|
apiKey: cfg.jinaApiKey,
|
|
79
81
|
text: ctx.query,
|
|
80
82
|
labels: (cfg.labels ?? DEFAULT_ROUTER_LABELS),
|
|
81
83
|
signal: ctx.signal,
|
|
84
|
+
rpmMonitor: cfg.rpmMonitor,
|
|
82
85
|
});
|
|
86
|
+
if (cfg.onClassifierUsage) {
|
|
87
|
+
cfg.onClassifierUsage({
|
|
88
|
+
durationMs: Date.now() - startedAt,
|
|
89
|
+
model: cfg.classifierId ? "few-shot" : "zero-shot",
|
|
90
|
+
});
|
|
91
|
+
}
|
|
83
92
|
if (!outcome) {
|
|
84
93
|
return { route: FALLBACK, reason: "classifier_fallback", score: null };
|
|
85
94
|
}
|
package/dist/router/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,2EAA2E;AAC3E,kEAAkE;AAClE,EAAE;AACF,+BAA+B;AAC/B,sEAAsE;AACtE,kDAAkD;AAClD,sEAAsE;AACtE,oEAAoE;AACpE,sEAAsE;AACtE,iDAAiD;AAEjD,OAAO,EACL,eAAe,EACf,gBAAgB,GACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"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"}
|
package/dist/tracing/events.d.ts
CHANGED
|
@@ -78,7 +78,19 @@ export interface CooldownEvent {
|
|
|
78
78
|
scope: "global" | "router" | "pgvector_reranker";
|
|
79
79
|
consecutiveErrors: number;
|
|
80
80
|
}
|
|
81
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Emitted (at most once per 60-second window) when the plugin's
|
|
83
|
+
* Jina RPM soft monitor observes the configured budget being exceeded.
|
|
84
|
+
* Pure observability — does not block any call.
|
|
85
|
+
*
|
|
86
|
+
* @since 3.2.4
|
|
87
|
+
*/
|
|
88
|
+
export interface JinaRpmExceededEvent {
|
|
89
|
+
type: "jina_rpm_exceeded";
|
|
90
|
+
count: number;
|
|
91
|
+
budget: number;
|
|
92
|
+
}
|
|
93
|
+
export type KnowledgeEvent = RouterEvent | PgvectorEvent | LightRAGEvent | JinaUsageEvent | CooldownEvent | JinaRpmExceededEvent;
|
|
82
94
|
/**
|
|
83
95
|
* Emit a structured event line through `logger.info`.
|
|
84
96
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/tracing/events.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,EAAE;AACF,2EAA2E;AAC3E,yEAAyE;AACzE,yDAAyD;AACzD,EAAE;AACF,wEAAwE;AACxE,+DAA+D;AAC/D,wEAAwE;AACxE,+BAA+B;AAC/B,EAAE;AACF,sEAAsE;AACtE,qEAAqE;AACrE,gEAAgE;AAChE,wEAAwE;AACxE,iCAAiC;AACjC,EAAE;AACF,oEAAoE;AACpE,uEAAuE;AACvE,qEAAqE;AACrE,sEAAsE;AACtE,uEAAuE;AACvE,8DAA8D;AAC9D,EAAE;AACF,2EAA2E;AAC3E,0EAA0E;AAC1E,mBAAmB;AAcnB;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,mBAAmB,CAAC;AAsDhD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/tracing/events.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,EAAE;AACF,2EAA2E;AAC3E,yEAAyE;AACzE,yDAAyD;AACzD,EAAE;AACF,wEAAwE;AACxE,+DAA+D;AAC/D,wEAAwE;AACxE,+BAA+B;AAC/B,EAAE;AACF,sEAAsE;AACtE,qEAAqE;AACrE,gEAAgE;AAChE,wEAAwE;AACxE,iCAAiC;AACjC,EAAE;AACF,oEAAoE;AACpE,uEAAuE;AACvE,qEAAqE;AACrE,sEAAsE;AACtE,uEAAuE;AACvE,8DAA8D;AAC9D,EAAE;AACF,2EAA2E;AAC3E,0EAA0E;AAC1E,mBAAmB;AAcnB;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,mBAAmB,CAAC;AAsDhD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,GAAG,CAAC;AAqCnD,8EAA8E;AAC9E,WAAW;AACX,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,MAAqB,EAAE,KAAqB;IACpE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY,IAAI,OAAO,EAAE,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,4DAA4D;IAC9D,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAqB,EACrB,KAAyB,EACzB,WAAmB;IAEnB,IAAI,CAAC,MAAM,CAAC,KAAK;QAAE,OAAO;IAC1B,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;QACzD,MAAM,CAAC,KAAK,CAAC,GAAG,YAAY,wBAAwB,EAAE,SAAS,WAAW,EAAE,CAAC,CAAC;IAChF,CAAC;IAAC,MAAM,CAAC;QACP,gDAAgD;IAClD,CAAC;AACH,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -39,12 +39,34 @@ export interface KnowledgePluginConfig {
|
|
|
39
39
|
lightragMaxChars?: number;
|
|
40
40
|
lightragEnabled?: boolean;
|
|
41
41
|
jina?: JinaPluginConfig;
|
|
42
|
+
/**
|
|
43
|
+
* Provenance reporting toward chat frontends (provenance/v1 — see the
|
|
44
|
+
* openclaw-webchat PROVENANCE_CONTRACT): "off" (default, no emission),
|
|
45
|
+
* "metadata" (file names/collections/scores, no content), "full"
|
|
46
|
+
* (plus the exact injected excerpts).
|
|
47
|
+
*
|
|
48
|
+
* @since 3.3.0
|
|
49
|
+
*/
|
|
50
|
+
provenanceReport?: string;
|
|
42
51
|
}
|
|
43
52
|
export interface JinaPluginConfig {
|
|
44
53
|
/** Jina API key. Required for `router.mode=jina-classifier` or `pgvectorReranker.enabled`. Supports `${ENV_VAR}` substitution. */
|
|
45
54
|
apiKey?: string;
|
|
46
55
|
router?: RouterPluginConfig;
|
|
47
56
|
pgvectorReranker?: PgvectorRerankerPluginConfig;
|
|
57
|
+
/**
|
|
58
|
+
* Soft RPM budget for ALL outbound Jina calls (router + reranker
|
|
59
|
+
* combined). When the sliding 60-second window exceeds this number,
|
|
60
|
+
* a `jina_rpm_exceeded` event is emitted (at most once per window)
|
|
61
|
+
* and a warning is logged. **The call is NEVER blocked** — the
|
|
62
|
+
* existing 429 cooldown breaker is the hard backstop.
|
|
63
|
+
*
|
|
64
|
+
* Default: 60. Set well below the Jina free-tier ceiling (100 RPM)
|
|
65
|
+
* to leave headroom for a shared key (e.g. plugin + Hindsight).
|
|
66
|
+
*
|
|
67
|
+
* @since 3.2.4
|
|
68
|
+
*/
|
|
69
|
+
rpmBudget?: number;
|
|
48
70
|
}
|
|
49
71
|
export interface RouterPluginConfig {
|
|
50
72
|
enabled?: boolean;
|
|
@@ -69,6 +91,25 @@ export interface PgvectorRerankerPluginConfig {
|
|
|
69
91
|
model?: RerankerModel;
|
|
70
92
|
/** Cap on results returned post-rerank. Default: `5`. */
|
|
71
93
|
topN?: number;
|
|
94
|
+
/**
|
|
95
|
+
* Maximum number of candidate documents submitted to Jina per call.
|
|
96
|
+
* Pgvector recall is typically broad (20-50 hits) but only the top
|
|
97
|
+
* 10-15 are worth reranking. Trimming the tail saves Jina tokens
|
|
98
|
+
* linearly. Default: `20`. Set to `0` to disable the cap.
|
|
99
|
+
*
|
|
100
|
+
* @since 3.2.4
|
|
101
|
+
*/
|
|
102
|
+
candidatePoolMax?: number;
|
|
103
|
+
/**
|
|
104
|
+
* Per-candidate text length cap (characters) before submission to
|
|
105
|
+
* Jina. The first ~2000 chars carry most of the relevance signal;
|
|
106
|
+
* longer chunks (transcripts, books) waste tokens on context the
|
|
107
|
+
* cross-encoder gets little additional signal from. Default: `2000`.
|
|
108
|
+
* Set to `0` to disable the truncation.
|
|
109
|
+
*
|
|
110
|
+
* @since 3.2.4
|
|
111
|
+
*/
|
|
112
|
+
maxCharsPerDoc?: number;
|
|
72
113
|
}
|
|
73
114
|
export type LightRAGQueryMode = "naive" | "local" | "global" | "hybrid";
|
|
74
115
|
/**
|
|
@@ -90,6 +131,13 @@ export interface ResolvedKnowledgeConfig {
|
|
|
90
131
|
lightragMaxChars: number;
|
|
91
132
|
lightragEnabled: boolean;
|
|
92
133
|
jinaApiKey: string;
|
|
134
|
+
/**
|
|
135
|
+
* Soft RPM budget for ALL outbound Jina calls. Default: 60.
|
|
136
|
+
* `0` disables the monitor entirely.
|
|
137
|
+
*
|
|
138
|
+
* @since 3.2.4
|
|
139
|
+
*/
|
|
140
|
+
jinaRpmBudget: number;
|
|
93
141
|
routerEnabled: boolean;
|
|
94
142
|
routerMode: "heuristic" | "jina-classifier";
|
|
95
143
|
routerClassifierId: string;
|
|
@@ -102,6 +150,26 @@ export interface ResolvedKnowledgeConfig {
|
|
|
102
150
|
pgvectorRerankerEnabled: boolean;
|
|
103
151
|
pgvectorRerankerModel: RerankerModel;
|
|
104
152
|
pgvectorRerankerTopN: number;
|
|
153
|
+
/**
|
|
154
|
+
* Cap on the number of candidates submitted to Jina /v1/rerank. `0`
|
|
155
|
+
* disables the cap (legacy v3.2.3 behavior). Default: `20`.
|
|
156
|
+
*
|
|
157
|
+
* @since 3.2.4
|
|
158
|
+
*/
|
|
159
|
+
pgvectorRerankerCandidatePoolMax: number;
|
|
160
|
+
/**
|
|
161
|
+
* Per-candidate text truncation length in characters. `0` disables
|
|
162
|
+
* truncation. Default: `2000`.
|
|
163
|
+
*
|
|
164
|
+
* @since 3.2.4
|
|
165
|
+
*/
|
|
166
|
+
pgvectorRerankerMaxCharsPerDoc: number;
|
|
167
|
+
/**
|
|
168
|
+
* Provenance reporting level (provenance/v1). Default "off".
|
|
169
|
+
*
|
|
170
|
+
* @since 3.3.0
|
|
171
|
+
*/
|
|
172
|
+
provenanceReport: "off" | "metadata" | "full";
|
|
105
173
|
}
|
|
106
174
|
/**
|
|
107
175
|
* One search hit from the PostgreSQL `knowledge_vectors` table, after score
|