@yuku123/z-cognition-kb-component 0.0.0-stage → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ComponentDoc-nAcl8jMQ.js +735 -0
- package/dist/index.js +6 -0
- package/dist/pages.js +1419 -0
- package/dist/z-cognition-kb-component.css +1 -0
- package/package.json +54 -4
- package/README.md +0 -3
|
@@ -0,0 +1,735 @@
|
|
|
1
|
+
import { Routes as Se, Route as U, Navigate as xe } from "react-router-dom";
|
|
2
|
+
import Te, { useState as f, useEffect as G } from "react";
|
|
3
|
+
import { Button as Z, Space as pe, Select as X, Tag as J, Input as Ee, Card as _e, InputNumber as we, Alert as Pe, List as H } from "antd";
|
|
4
|
+
import { ReloadOutlined as me, SearchOutlined as Re } from "@ant-design/icons";
|
|
5
|
+
import { ErrorState as Q, LoadingState as ee, ListTable as he, EmptyState as ce, ComponentDoc as je } from "@yuku123/z-frontend-common/ui";
|
|
6
|
+
var z = { exports: {} }, W = {};
|
|
7
|
+
/**
|
|
8
|
+
* @license React
|
|
9
|
+
* react-jsx-runtime.production.js
|
|
10
|
+
*
|
|
11
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
12
|
+
*
|
|
13
|
+
* This source code is licensed under the MIT license found in the
|
|
14
|
+
* LICENSE file in the root directory of this source tree.
|
|
15
|
+
*/
|
|
16
|
+
var ie;
|
|
17
|
+
function Ae() {
|
|
18
|
+
if (ie) return W;
|
|
19
|
+
ie = 1;
|
|
20
|
+
var t = Symbol.for("react.transitional.element"), s = Symbol.for("react.fragment");
|
|
21
|
+
function r(c, l, d) {
|
|
22
|
+
var p = null;
|
|
23
|
+
if (d !== void 0 && (p = "" + d), l.key !== void 0 && (p = "" + l.key), "key" in l) {
|
|
24
|
+
d = {};
|
|
25
|
+
for (var y in l)
|
|
26
|
+
y !== "key" && (d[y] = l[y]);
|
|
27
|
+
} else d = l;
|
|
28
|
+
return l = d.ref, {
|
|
29
|
+
$$typeof: t,
|
|
30
|
+
type: c,
|
|
31
|
+
key: p,
|
|
32
|
+
ref: l !== void 0 ? l : null,
|
|
33
|
+
props: d
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return W.Fragment = s, W.jsx = r, W.jsxs = r, W;
|
|
37
|
+
}
|
|
38
|
+
var L = {};
|
|
39
|
+
/**
|
|
40
|
+
* @license React
|
|
41
|
+
* react-jsx-runtime.development.js
|
|
42
|
+
*
|
|
43
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
44
|
+
*
|
|
45
|
+
* This source code is licensed under the MIT license found in the
|
|
46
|
+
* LICENSE file in the root directory of this source tree.
|
|
47
|
+
*/
|
|
48
|
+
var le;
|
|
49
|
+
function Oe() {
|
|
50
|
+
return le || (le = 1, process.env.NODE_ENV !== "production" && (function() {
|
|
51
|
+
function t(e) {
|
|
52
|
+
if (e == null) return null;
|
|
53
|
+
if (typeof e == "function")
|
|
54
|
+
return e.$$typeof === fe ? null : e.displayName || e.name || null;
|
|
55
|
+
if (typeof e == "string") return e;
|
|
56
|
+
switch (e) {
|
|
57
|
+
case u:
|
|
58
|
+
return "Fragment";
|
|
59
|
+
case E:
|
|
60
|
+
return "Profiler";
|
|
61
|
+
case S:
|
|
62
|
+
return "StrictMode";
|
|
63
|
+
case a:
|
|
64
|
+
return "Suspense";
|
|
65
|
+
case b:
|
|
66
|
+
return "SuspenseList";
|
|
67
|
+
case ke:
|
|
68
|
+
return "Activity";
|
|
69
|
+
case ge:
|
|
70
|
+
return "ViewTransition";
|
|
71
|
+
}
|
|
72
|
+
if (typeof e == "object")
|
|
73
|
+
switch (typeof e.tag == "number" && console.error(
|
|
74
|
+
"Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
|
|
75
|
+
), e.$$typeof) {
|
|
76
|
+
case T:
|
|
77
|
+
return "Portal";
|
|
78
|
+
case K:
|
|
79
|
+
return e.displayName || "Context";
|
|
80
|
+
case I:
|
|
81
|
+
return (e._context.displayName || "Context") + ".Consumer";
|
|
82
|
+
case Y:
|
|
83
|
+
var o = e.render;
|
|
84
|
+
return e = e.displayName, e || (e = o.displayName || o.name || "", e = e !== "" ? "ForwardRef(" + e + ")" : "ForwardRef"), e;
|
|
85
|
+
case v:
|
|
86
|
+
return o = e.displayName || null, o !== null ? o : t(e.type) || "Memo";
|
|
87
|
+
case F:
|
|
88
|
+
o = e._payload, e = e._init;
|
|
89
|
+
try {
|
|
90
|
+
return t(e(o));
|
|
91
|
+
} catch {
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
function s(e) {
|
|
97
|
+
return "" + e;
|
|
98
|
+
}
|
|
99
|
+
function r(e) {
|
|
100
|
+
try {
|
|
101
|
+
s(e);
|
|
102
|
+
var o = !1;
|
|
103
|
+
} catch {
|
|
104
|
+
o = !0;
|
|
105
|
+
}
|
|
106
|
+
if (o) {
|
|
107
|
+
o = console;
|
|
108
|
+
var m = o.error, k = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
|
|
109
|
+
return m.call(
|
|
110
|
+
o,
|
|
111
|
+
"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
|
|
112
|
+
k
|
|
113
|
+
), s(e);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function c(e) {
|
|
117
|
+
if (e === u) return "<>";
|
|
118
|
+
if (typeof e == "object" && e !== null && e.$$typeof === F)
|
|
119
|
+
return "<...>";
|
|
120
|
+
try {
|
|
121
|
+
var o = t(e);
|
|
122
|
+
return o ? "<" + o + ">" : "<...>";
|
|
123
|
+
} catch {
|
|
124
|
+
return "<...>";
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function l() {
|
|
128
|
+
var e = B.A;
|
|
129
|
+
return e === null ? null : e.getOwner();
|
|
130
|
+
}
|
|
131
|
+
function d() {
|
|
132
|
+
return Error("react-stack-top-frame");
|
|
133
|
+
}
|
|
134
|
+
function p(e) {
|
|
135
|
+
if (te.call(e, "key")) {
|
|
136
|
+
var o = Object.getOwnPropertyDescriptor(e, "key").get;
|
|
137
|
+
if (o && o.isReactWarning) return !1;
|
|
138
|
+
}
|
|
139
|
+
return e.key !== void 0;
|
|
140
|
+
}
|
|
141
|
+
function y(e, o) {
|
|
142
|
+
function m() {
|
|
143
|
+
ne || (ne = !0, console.error(
|
|
144
|
+
"%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
|
|
145
|
+
o
|
|
146
|
+
));
|
|
147
|
+
}
|
|
148
|
+
m.isReactWarning = !0, Object.defineProperty(e, "key", {
|
|
149
|
+
get: m,
|
|
150
|
+
configurable: !0
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
function P() {
|
|
154
|
+
var e = t(this.type);
|
|
155
|
+
return se[e] || (se[e] = !0, console.error(
|
|
156
|
+
"Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
|
|
157
|
+
)), e = this.props.ref, e !== void 0 ? e : null;
|
|
158
|
+
}
|
|
159
|
+
function $(e, o, m, k, O, w) {
|
|
160
|
+
var g = m.ref;
|
|
161
|
+
return e = {
|
|
162
|
+
$$typeof: A,
|
|
163
|
+
type: e,
|
|
164
|
+
key: o,
|
|
165
|
+
props: m,
|
|
166
|
+
_owner: k
|
|
167
|
+
}, (g !== void 0 ? g : null) !== null ? Object.defineProperty(e, "ref", {
|
|
168
|
+
enumerable: !1,
|
|
169
|
+
get: P
|
|
170
|
+
}) : Object.defineProperty(e, "ref", { enumerable: !1, value: null }), e._store = {}, Object.defineProperty(e._store, "validated", {
|
|
171
|
+
configurable: !1,
|
|
172
|
+
enumerable: !1,
|
|
173
|
+
writable: !0,
|
|
174
|
+
value: 0
|
|
175
|
+
}), Object.defineProperty(e, "_debugInfo", {
|
|
176
|
+
configurable: !1,
|
|
177
|
+
enumerable: !1,
|
|
178
|
+
writable: !0,
|
|
179
|
+
value: null
|
|
180
|
+
}), Object.defineProperty(e, "_debugStack", {
|
|
181
|
+
configurable: !1,
|
|
182
|
+
enumerable: !1,
|
|
183
|
+
writable: !0,
|
|
184
|
+
value: O
|
|
185
|
+
}), Object.defineProperty(e, "_debugTask", {
|
|
186
|
+
configurable: !1,
|
|
187
|
+
enumerable: !1,
|
|
188
|
+
writable: !0,
|
|
189
|
+
value: w
|
|
190
|
+
}), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e;
|
|
191
|
+
}
|
|
192
|
+
function h(e, o, m, k, O, w) {
|
|
193
|
+
var g = o.children;
|
|
194
|
+
if (g !== void 0)
|
|
195
|
+
if (k)
|
|
196
|
+
if (be(g)) {
|
|
197
|
+
for (k = 0; k < g.length; k++)
|
|
198
|
+
R(g[k]);
|
|
199
|
+
Object.freeze && Object.freeze(g);
|
|
200
|
+
} else
|
|
201
|
+
console.error(
|
|
202
|
+
"React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead."
|
|
203
|
+
);
|
|
204
|
+
else R(g);
|
|
205
|
+
if (te.call(o, "key")) {
|
|
206
|
+
g = t(e);
|
|
207
|
+
var N = Object.keys(o).filter(function(ye) {
|
|
208
|
+
return ye !== "key";
|
|
209
|
+
});
|
|
210
|
+
k = 0 < N.length ? "{key: someKey, " + N.join(": ..., ") + ": ...}" : "{key: someKey}", ae[g + k] || (N = 0 < N.length ? "{" + N.join(": ..., ") + ": ...}" : "{}", console.error(
|
|
211
|
+
`A props object containing a "key" prop is being spread into JSX:
|
|
212
|
+
let props = %s;
|
|
213
|
+
<%s {...props} />
|
|
214
|
+
React keys must be passed directly to JSX without using spread:
|
|
215
|
+
let props = %s;
|
|
216
|
+
<%s key={someKey} {...props} />`,
|
|
217
|
+
k,
|
|
218
|
+
g,
|
|
219
|
+
N,
|
|
220
|
+
g
|
|
221
|
+
), ae[g + k] = !0);
|
|
222
|
+
}
|
|
223
|
+
if (g = null, m !== void 0 && (r(m), g = "" + m), p(o) && (r(o.key), g = "" + o.key), "key" in o) {
|
|
224
|
+
m = {};
|
|
225
|
+
for (var q in o)
|
|
226
|
+
q !== "key" && (m[q] = o[q]);
|
|
227
|
+
} else m = o;
|
|
228
|
+
return g && y(
|
|
229
|
+
m,
|
|
230
|
+
typeof e == "function" ? e.displayName || e.name || "Unknown" : e
|
|
231
|
+
), $(
|
|
232
|
+
e,
|
|
233
|
+
g,
|
|
234
|
+
m,
|
|
235
|
+
l(),
|
|
236
|
+
O,
|
|
237
|
+
w
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
function R(e) {
|
|
241
|
+
j(e) ? e._store && (e._store.validated = 1) : typeof e == "object" && e !== null && e.$$typeof === F && (e._payload.status === "fulfilled" ? j(e._payload.value) && e._payload.value._store && (e._payload.value._store.validated = 1) : e._store && (e._store.validated = 1));
|
|
242
|
+
}
|
|
243
|
+
function j(e) {
|
|
244
|
+
return typeof e == "object" && e !== null && e.$$typeof === A;
|
|
245
|
+
}
|
|
246
|
+
var _ = Te, A = Symbol.for("react.transitional.element"), T = Symbol.for("react.portal"), u = Symbol.for("react.fragment"), S = Symbol.for("react.strict_mode"), E = Symbol.for("react.profiler"), I = Symbol.for("react.consumer"), K = Symbol.for("react.context"), Y = Symbol.for("react.forward_ref"), a = Symbol.for("react.suspense"), b = Symbol.for("react.suspense_list"), v = Symbol.for("react.memo"), F = Symbol.for("react.lazy"), ke = Symbol.for("react.activity"), ge = Symbol.for("react.view_transition"), fe = Symbol.for("react.client.reference"), B = _.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, te = Object.prototype.hasOwnProperty, be = Array.isArray, M = console.createTask ? console.createTask : function() {
|
|
247
|
+
return null;
|
|
248
|
+
};
|
|
249
|
+
_ = {
|
|
250
|
+
react_stack_bottom_frame: function(e) {
|
|
251
|
+
return e();
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
var ne, se = {}, re = _.react_stack_bottom_frame.bind(
|
|
255
|
+
_,
|
|
256
|
+
d
|
|
257
|
+
)(), oe = M(c(d)), ae = {};
|
|
258
|
+
L.Fragment = u, L.jsx = function(e, o, m) {
|
|
259
|
+
var k = 1e4 > B.recentlyCreatedOwnerStacks++;
|
|
260
|
+
if (k) {
|
|
261
|
+
var O = Error.stackTraceLimit;
|
|
262
|
+
Error.stackTraceLimit = 10;
|
|
263
|
+
var w = Error("react-stack-top-frame");
|
|
264
|
+
Error.stackTraceLimit = O;
|
|
265
|
+
} else w = re;
|
|
266
|
+
return h(
|
|
267
|
+
e,
|
|
268
|
+
o,
|
|
269
|
+
m,
|
|
270
|
+
!1,
|
|
271
|
+
w,
|
|
272
|
+
k ? M(c(e)) : oe
|
|
273
|
+
);
|
|
274
|
+
}, L.jsxs = function(e, o, m) {
|
|
275
|
+
var k = 1e4 > B.recentlyCreatedOwnerStacks++;
|
|
276
|
+
if (k) {
|
|
277
|
+
var O = Error.stackTraceLimit;
|
|
278
|
+
Error.stackTraceLimit = 10;
|
|
279
|
+
var w = Error("react-stack-top-frame");
|
|
280
|
+
Error.stackTraceLimit = O;
|
|
281
|
+
} else w = re;
|
|
282
|
+
return h(
|
|
283
|
+
e,
|
|
284
|
+
o,
|
|
285
|
+
m,
|
|
286
|
+
!0,
|
|
287
|
+
w,
|
|
288
|
+
k ? M(c(e)) : oe
|
|
289
|
+
);
|
|
290
|
+
};
|
|
291
|
+
})()), L;
|
|
292
|
+
}
|
|
293
|
+
var ue;
|
|
294
|
+
function Ie() {
|
|
295
|
+
return ue || (ue = 1, process.env.NODE_ENV === "production" ? z.exports = Ae() : z.exports = Oe()), z.exports;
|
|
296
|
+
}
|
|
297
|
+
var n = Ie();
|
|
298
|
+
const ve = "/api/kb", de = "default";
|
|
299
|
+
async function i(t, s = {}) {
|
|
300
|
+
const r = await fetch(`${ve}${t}`, {
|
|
301
|
+
headers: { "Content-Type": "application/json", ...s.headers },
|
|
302
|
+
...s
|
|
303
|
+
});
|
|
304
|
+
if (!r.ok) {
|
|
305
|
+
let d = "";
|
|
306
|
+
try {
|
|
307
|
+
const p = await r.text();
|
|
308
|
+
d = p ? ` · ${p.slice(0, 300)}` : "";
|
|
309
|
+
} catch {
|
|
310
|
+
}
|
|
311
|
+
throw new Error(`${r.status} ${r.statusText}${d}`);
|
|
312
|
+
}
|
|
313
|
+
if (r.status === 204) return null;
|
|
314
|
+
const c = await r.text();
|
|
315
|
+
if (!c) return null;
|
|
316
|
+
let l;
|
|
317
|
+
try {
|
|
318
|
+
l = JSON.parse(c);
|
|
319
|
+
} catch {
|
|
320
|
+
return c;
|
|
321
|
+
}
|
|
322
|
+
return (l == null ? void 0 : l.data) ?? l;
|
|
323
|
+
}
|
|
324
|
+
function Ce(t) {
|
|
325
|
+
return t.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
326
|
+
}
|
|
327
|
+
const De = /* @__PURE__ */ new Set(["frontmatter", "metadata", "properties", "config"]);
|
|
328
|
+
function x(t, s = 0) {
|
|
329
|
+
if (Array.isArray(t)) return t.map((c) => x(c, s));
|
|
330
|
+
if (!t || typeof t != "object" || s > 3) return t;
|
|
331
|
+
const r = {};
|
|
332
|
+
for (const [c, l] of Object.entries(t)) {
|
|
333
|
+
const d = Ce(c), p = De.has(c) ? l : x(l, s + 1);
|
|
334
|
+
r[c] = p, d !== c && !(d in r) && (r[d] = p);
|
|
335
|
+
}
|
|
336
|
+
return r;
|
|
337
|
+
}
|
|
338
|
+
const D = {
|
|
339
|
+
// ── Workspaces(WorkspaceController,/api/kb/workspaces)────────────────
|
|
340
|
+
listWorkspaces: () => i("/workspaces"),
|
|
341
|
+
getWorkspace: (t) => i(`/workspaces/${encodeURIComponent(t)}`),
|
|
342
|
+
createWorkspace: (t) => i("/workspaces", { method: "POST", body: JSON.stringify(x(t)) }),
|
|
343
|
+
getWorkspaceStats: (t) => i(`/workspaces/${encodeURIComponent(t)}/statistics`),
|
|
344
|
+
// ── Documents(DocumentController,/api/kb/documents)──────────────────
|
|
345
|
+
// 上游是服务端分页 (offset/limit, 默认 0/20),总数另走 /count
|
|
346
|
+
listDocuments: (t, s = 0, r = 20) => i(`/documents/${encodeURIComponent(t)}?offset=${s}&limit=${r}`),
|
|
347
|
+
getDocumentCount: (t) => i(`/documents/${encodeURIComponent(t)}/count`),
|
|
348
|
+
getDocument: (t, s) => i(`/documents/${encodeURIComponent(t)}/${encodeURIComponent(s)}`),
|
|
349
|
+
deleteDocument: (t, s) => i(`/documents/${encodeURIComponent(t)}/${encodeURIComponent(s)}`, { method: "DELETE" }),
|
|
350
|
+
// 建索引:body = {document:{...}, overwrite, extractGraph, workspace}
|
|
351
|
+
createDocument: (t) => i("/documents", { method: "POST", body: JSON.stringify(x(t)) }),
|
|
352
|
+
batchCreateDocuments: (t) => i("/documents/batch", { method: "POST", body: JSON.stringify(x(t)) }),
|
|
353
|
+
importMarkdown: (t) => i("/documents/markdown", { method: "POST", body: JSON.stringify(x(t)) }),
|
|
354
|
+
reindexDocument: (t, s) => i(`/documents/${encodeURIComponent(t)}/${encodeURIComponent(s)}/reindex`, { method: "POST" }),
|
|
355
|
+
listDocumentsByTags: (t, s = [], r = 0, c = 20) => {
|
|
356
|
+
const l = s.map((d) => `tags=${encodeURIComponent(d)}`).join("&");
|
|
357
|
+
return i(
|
|
358
|
+
`/documents/${encodeURIComponent(t)}/by-tags?${l}&offset=${r}&limit=${c}`
|
|
359
|
+
);
|
|
360
|
+
},
|
|
361
|
+
// ── Search(SearchController,/api/kb/search)──────────────────────────
|
|
362
|
+
// body: {query, workspace, mode(FUSION|VECTOR|KEYWORD|GRAPH|HYBRID), topK, ...}
|
|
363
|
+
search: (t) => i("/search", { method: "POST", body: JSON.stringify(x(t)) }),
|
|
364
|
+
// ── Chat(ChatController,/api/kb/chat)───────────────────────────────
|
|
365
|
+
chat: (t) => i("/chat", { method: "POST", body: JSON.stringify(x(t)) }),
|
|
366
|
+
createSession: (t) => i("/chat/sessions", { method: "POST", body: JSON.stringify(x(t)) }),
|
|
367
|
+
getSession: (t) => i(`/chat/sessions/${encodeURIComponent(t)}`),
|
|
368
|
+
listSessions: (t, s, r = 50) => {
|
|
369
|
+
const c = new URLSearchParams();
|
|
370
|
+
return t && c.set("workspace", t), s && c.set("userId", s), c.set("limit", String(r)), i(`/chat/sessions?${c.toString()}`);
|
|
371
|
+
},
|
|
372
|
+
deleteSession: (t) => i(`/chat/sessions/${encodeURIComponent(t)}`, { method: "DELETE" }),
|
|
373
|
+
chatInSession: (t, s) => i(`/chat/sessions/${encodeURIComponent(t)}/chat`, { method: "POST", body: JSON.stringify(x(s)) }),
|
|
374
|
+
// ── Graph(GraphController,/api/kb/graph)─────────────────────────────
|
|
375
|
+
// 注意:graph 没有裸根路由,第一条就是 /{workspace}/entities
|
|
376
|
+
listEntities: (t, s = 100) => i(`/graph/${encodeURIComponent(t)}/entities?limit=${s}`),
|
|
377
|
+
getEntity: (t, s) => i(`/graph/${encodeURIComponent(t)}/entities/${encodeURIComponent(s)}`),
|
|
378
|
+
getNeighbors: (t, s, r = 2) => i(`/graph/${encodeURIComponent(t)}/entities/${encodeURIComponent(s)}/neighbors?depth=${r}`),
|
|
379
|
+
// body 是实体名数组(List<String>)
|
|
380
|
+
getSubgraph: (t, s, r = 2) => i(`/graph/${encodeURIComponent(t)}/subgraph?depth=${r}`, {
|
|
381
|
+
method: "POST",
|
|
382
|
+
body: JSON.stringify(s)
|
|
383
|
+
}),
|
|
384
|
+
// ── Ingest(IngestController,/api/kb/ingest)──────────────────────────
|
|
385
|
+
listSources: () => i("/ingest/sources"),
|
|
386
|
+
getIngestStats: () => i("/ingest/stats"),
|
|
387
|
+
// 批量拉取可能很久:这里不设超时,交由浏览器/网关口径
|
|
388
|
+
runIngest: (t, s = {}) => i(`/ingest/run?sourceId=${encodeURIComponent(t)}`, {
|
|
389
|
+
method: "POST",
|
|
390
|
+
body: JSON.stringify(s)
|
|
391
|
+
}),
|
|
392
|
+
submitIngest: (t) => i("/ingest/submit", { method: "POST", body: JSON.stringify(x(t)) }),
|
|
393
|
+
// 通用 webhook 入口:body = {title, content, workspace, metadata}
|
|
394
|
+
webhook: (t, s = {}) => i("/ingest/webhook", {
|
|
395
|
+
method: "POST",
|
|
396
|
+
headers: s,
|
|
397
|
+
body: JSON.stringify(x(t))
|
|
398
|
+
}),
|
|
399
|
+
flushWebhook: () => i("/ingest/webhook/flush", { method: "POST" }),
|
|
400
|
+
// ── Modeling(ModelingController,/api/kb/modeling)────────────────────
|
|
401
|
+
// modeling 也没有裸根路由:/decompose 与 /cheatsheet
|
|
402
|
+
decompose: (t = de) => i(`/modeling/decompose?workspace=${encodeURIComponent(t)}`),
|
|
403
|
+
getCheatsheet: (t = de) => i(`/modeling/cheatsheet?workspace=${encodeURIComponent(t)}`),
|
|
404
|
+
// POST 版:body = ProductModel(整份模型)
|
|
405
|
+
createCheatsheet: (t) => i("/modeling/cheatsheet", { method: "POST", body: JSON.stringify(x(t)) })
|
|
406
|
+
};
|
|
407
|
+
function $e() {
|
|
408
|
+
const [t, s] = f([]), [r, c] = f(!0), [l, d] = f(null), p = async () => {
|
|
409
|
+
c(!0), d(null);
|
|
410
|
+
try {
|
|
411
|
+
const y = await D.listWorkspaces();
|
|
412
|
+
s(Array.isArray(y) ? y : []);
|
|
413
|
+
} catch (y) {
|
|
414
|
+
d(y.message);
|
|
415
|
+
} finally {
|
|
416
|
+
c(!1);
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
return G(() => {
|
|
420
|
+
p();
|
|
421
|
+
}, []), /* @__PURE__ */ n.jsx("div", { children: l ? (
|
|
422
|
+
// 页面级错误:走共享基座 ErrorState(就近显示原因 + 重试入口),
|
|
423
|
+
// 不用手写红字 div —— 全仓错误态口径统一。
|
|
424
|
+
/* @__PURE__ */ n.jsx(
|
|
425
|
+
Q,
|
|
426
|
+
{
|
|
427
|
+
error: l,
|
|
428
|
+
title: "工作空间列表加载失败",
|
|
429
|
+
description: "确认 z-kb 后端已启动且 /api/kb/workspaces 可达。",
|
|
430
|
+
onRetry: p
|
|
431
|
+
}
|
|
432
|
+
)
|
|
433
|
+
) : r ? /* @__PURE__ */ n.jsx(ee, { variant: "table", rows: 4 }) : /* @__PURE__ */ n.jsx(
|
|
434
|
+
he,
|
|
435
|
+
{
|
|
436
|
+
cardTitle: "知识库工作空间",
|
|
437
|
+
rows: t,
|
|
438
|
+
rowKey: "id",
|
|
439
|
+
columns: [
|
|
440
|
+
{ title: "名称", dataIndex: "name", key: "name" },
|
|
441
|
+
{ title: "描述", dataIndex: "description", key: "description" },
|
|
442
|
+
// 字段名按实测响应写:z-team-core 的全局 SNAKE_CASE 会把这些 VO 转成下划线键
|
|
443
|
+
{ title: "文档数", dataIndex: "document_count", key: "document_count" },
|
|
444
|
+
{ title: "切片数", dataIndex: "chunk_count", key: "chunk_count" },
|
|
445
|
+
{ title: "实体数", dataIndex: "entity_count", key: "entity_count" },
|
|
446
|
+
{ title: "关系数", dataIndex: "relation_count", key: "relation_count" },
|
|
447
|
+
{
|
|
448
|
+
title: "状态",
|
|
449
|
+
dataIndex: "active",
|
|
450
|
+
key: "active",
|
|
451
|
+
render: (y) => y === !1 ? "未启用" : "启用"
|
|
452
|
+
},
|
|
453
|
+
{ title: "创建时间", dataIndex: "created_at", key: "created_at" }
|
|
454
|
+
],
|
|
455
|
+
searchKeys: "name,description",
|
|
456
|
+
searchPlaceholder: "按工作空间名 / 描述过滤",
|
|
457
|
+
emptyTitle: "还没有工作空间",
|
|
458
|
+
emptyDescription: "接口通了,但一个工作空间也没有。先在「数据接入」里创建或拉取一个工作空间。",
|
|
459
|
+
cardExtra: /* @__PURE__ */ n.jsx(Z, { icon: /* @__PURE__ */ n.jsx(me, {}), loading: r, onClick: p, children: "刷新" })
|
|
460
|
+
}
|
|
461
|
+
) });
|
|
462
|
+
}
|
|
463
|
+
const V = 20;
|
|
464
|
+
function Ne() {
|
|
465
|
+
const [t, s] = f([]), [r, c] = f(null), [l, d] = f([]), [p, y] = f(null), [P, $] = f(1), [h, R] = f(0), [j, _] = f(!1), [A, T] = f(null);
|
|
466
|
+
return G(() => {
|
|
467
|
+
D.listWorkspaces().then((u) => {
|
|
468
|
+
const S = Array.isArray(u) ? u : [];
|
|
469
|
+
s(S), S.length > 0 && c(S[0].name);
|
|
470
|
+
}).catch((u) => T(u.message));
|
|
471
|
+
}, []), G(() => {
|
|
472
|
+
if (!r) return;
|
|
473
|
+
let u = !1;
|
|
474
|
+
_(!0), T(null);
|
|
475
|
+
const S = (P - 1) * V;
|
|
476
|
+
return Promise.all([
|
|
477
|
+
D.listDocuments(r, S, V),
|
|
478
|
+
D.getDocumentCount(r)
|
|
479
|
+
]).then(([E, I]) => {
|
|
480
|
+
u || (d(Array.isArray(E) ? E : []), y(Number(I == null ? void 0 : I.count)));
|
|
481
|
+
}).catch((E) => {
|
|
482
|
+
u || T(E.message);
|
|
483
|
+
}).finally(() => {
|
|
484
|
+
u || _(!1);
|
|
485
|
+
}), () => {
|
|
486
|
+
u = !0;
|
|
487
|
+
};
|
|
488
|
+
}, [r, P, h]), /* @__PURE__ */ n.jsxs("div", { children: [
|
|
489
|
+
/* @__PURE__ */ n.jsx(pe, { style: { marginBottom: 12 }, children: /* @__PURE__ */ n.jsx(
|
|
490
|
+
X,
|
|
491
|
+
{
|
|
492
|
+
value: r,
|
|
493
|
+
onChange: (u) => {
|
|
494
|
+
c(u), $(1);
|
|
495
|
+
},
|
|
496
|
+
style: { width: 200 },
|
|
497
|
+
placeholder: "选择工作空间",
|
|
498
|
+
options: t.map((u) => ({ label: u.name, value: u.name }))
|
|
499
|
+
}
|
|
500
|
+
) }),
|
|
501
|
+
A ? (
|
|
502
|
+
// 页面级错误:走共享基座 ErrorState(原因 + 重试入口),不用手写红字 div。
|
|
503
|
+
/* @__PURE__ */ n.jsx(Q, { error: A, title: "文档列表加载失败", onRetry: () => R((u) => u + 1) })
|
|
504
|
+
) : j ? /* @__PURE__ */ n.jsx(ee, { variant: "table", rows: 4 }) : /* @__PURE__ */ n.jsx(
|
|
505
|
+
he,
|
|
506
|
+
{
|
|
507
|
+
cardTitle: `文档管理${p === null ? "" : `(${r} 共 ${p} 篇)`}`,
|
|
508
|
+
rows: l,
|
|
509
|
+
rowKey: "id",
|
|
510
|
+
columns: [
|
|
511
|
+
{ title: "标题", dataIndex: "title", key: "title" },
|
|
512
|
+
{ title: "分类", dataIndex: "category", key: "category" },
|
|
513
|
+
{ title: "状态", dataIndex: "status", key: "status" },
|
|
514
|
+
{ title: "字数", dataIndex: "word_count", key: "word_count" },
|
|
515
|
+
{ title: "切片数", dataIndex: "chunk_count", key: "chunk_count" },
|
|
516
|
+
{
|
|
517
|
+
title: "标签",
|
|
518
|
+
dataIndex: "tags",
|
|
519
|
+
key: "tags",
|
|
520
|
+
render: (u) => Array.isArray(u) ? u.map((S) => /* @__PURE__ */ n.jsx(J, { children: S }, S)) : u
|
|
521
|
+
},
|
|
522
|
+
{ title: "更新时间", dataIndex: "updated_at", key: "updated_at" }
|
|
523
|
+
],
|
|
524
|
+
searchKeys: "title,category,status",
|
|
525
|
+
searchPlaceholder: "按标题 / 分类 / 状态过滤",
|
|
526
|
+
emptyTitle: "暂无文档",
|
|
527
|
+
emptyDescription: r ? `接口通了,${r} 下没有文档` : "请先选择工作空间",
|
|
528
|
+
cardExtra: /* @__PURE__ */ n.jsx(Z, { icon: /* @__PURE__ */ n.jsx(me, {}), loading: j, onClick: () => R((u) => u + 1), children: "刷新" }),
|
|
529
|
+
pagination: {
|
|
530
|
+
current: P,
|
|
531
|
+
pageSize: V,
|
|
532
|
+
total: Number.isNaN(p) ? l.length : p,
|
|
533
|
+
showSizeChanger: !1,
|
|
534
|
+
onChange: $
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
)
|
|
538
|
+
] });
|
|
539
|
+
}
|
|
540
|
+
const { TextArea: We } = Ee, Le = [
|
|
541
|
+
{ value: "FUSION", label: "FUSION(向量+关键词+图谱,RRF)" },
|
|
542
|
+
{ value: "HYBRID", label: "HYBRID(向量+关键词)" },
|
|
543
|
+
{ value: "KEYWORD", label: "KEYWORD(仅 BM25)" },
|
|
544
|
+
{ value: "VECTOR", label: "VECTOR(仅向量)" },
|
|
545
|
+
{ value: "GRAPH", label: "GRAPH(仅图谱)" }
|
|
546
|
+
], C = (t) => t == null || t === "" ? null : Number(t), Ue = (t) => t.context_title || (Array.isArray(t.heading_path) && t.heading_path.length ? t.heading_path.join(" > ") : null) || t.document_id;
|
|
547
|
+
function ze() {
|
|
548
|
+
const [t, s] = f([]), [r, c] = f("default"), [l, d] = f(""), [p, y] = f("FUSION"), [P, $] = f(10), [h, R] = f(null), [j, _] = f(!1), [A, T] = f(null);
|
|
549
|
+
G(() => {
|
|
550
|
+
D.listWorkspaces().then((a) => {
|
|
551
|
+
const b = Array.isArray(a) ? a : [];
|
|
552
|
+
s(b.map((v) => ({ label: v.name, value: v.name }))), b.length > 0 && c(b[0].name);
|
|
553
|
+
}).catch((a) => T(a.message));
|
|
554
|
+
}, []);
|
|
555
|
+
const u = async () => {
|
|
556
|
+
const a = l.trim();
|
|
557
|
+
if (a) {
|
|
558
|
+
_(!0), T(null);
|
|
559
|
+
try {
|
|
560
|
+
const b = await D.search({ query: a, workspace: r, mode: p, top_k: P });
|
|
561
|
+
R(b && typeof b == "object" ? b : null);
|
|
562
|
+
} catch (b) {
|
|
563
|
+
T(b.message), R(null);
|
|
564
|
+
} finally {
|
|
565
|
+
_(!1);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}, S = Array.isArray(h == null ? void 0 : h.hits) ? h.hits : [], E = (h == null ? void 0 : h.stats) || {}, I = Array.isArray(h == null ? void 0 : h.matched_entities) ? h.matched_entities : [], K = ["FUSION", "HYBRID", "VECTOR"].includes(p) && C(E.vector) === 0, Y = () => {
|
|
569
|
+
T(null), D.listWorkspaces().then((a) => {
|
|
570
|
+
const b = Array.isArray(a) ? a : [];
|
|
571
|
+
s(b.map((v) => ({ label: v.name, value: v.name }))), b.length > 0 && c(b[0].name);
|
|
572
|
+
}).catch((a) => T(a.message)), l.trim() && u();
|
|
573
|
+
};
|
|
574
|
+
return /* @__PURE__ */ n.jsxs(_e, { title: "知识库检索", children: [
|
|
575
|
+
/* @__PURE__ */ n.jsxs("div", { style: { display: "flex", gap: 8, marginBottom: 16, flexWrap: "wrap" }, children: [
|
|
576
|
+
/* @__PURE__ */ n.jsx(
|
|
577
|
+
X,
|
|
578
|
+
{
|
|
579
|
+
value: r,
|
|
580
|
+
onChange: c,
|
|
581
|
+
style: { width: 160 },
|
|
582
|
+
options: t.length > 0 ? t : [{ label: "default", value: "default" }]
|
|
583
|
+
}
|
|
584
|
+
),
|
|
585
|
+
/* @__PURE__ */ n.jsx(
|
|
586
|
+
We,
|
|
587
|
+
{
|
|
588
|
+
value: l,
|
|
589
|
+
onChange: (a) => d(a.target.value),
|
|
590
|
+
placeholder: "输入搜索关键词...",
|
|
591
|
+
autoSize: { minRows: 1, maxRows: 3 },
|
|
592
|
+
style: { flex: 1, minWidth: 240 },
|
|
593
|
+
onPressEnter: (a) => {
|
|
594
|
+
a.shiftKey || (a.preventDefault(), u());
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
),
|
|
598
|
+
/* @__PURE__ */ n.jsx(X, { value: p, onChange: y, style: { width: 260 }, options: Le }),
|
|
599
|
+
/* @__PURE__ */ n.jsx(
|
|
600
|
+
we,
|
|
601
|
+
{
|
|
602
|
+
value: P,
|
|
603
|
+
onChange: (a) => $(a ?? 10),
|
|
604
|
+
min: 1,
|
|
605
|
+
max: 100,
|
|
606
|
+
addonBefore: "条数",
|
|
607
|
+
style: { width: 130 }
|
|
608
|
+
}
|
|
609
|
+
),
|
|
610
|
+
/* @__PURE__ */ n.jsx(Z, { type: "primary", icon: /* @__PURE__ */ n.jsx(Re, {}), loading: j, onClick: u, children: "搜索" })
|
|
611
|
+
] }),
|
|
612
|
+
A && /* @__PURE__ */ n.jsx(Q, { error: A, title: "检索失败", onRetry: Y, style: { marginBottom: 16 } }),
|
|
613
|
+
j ? /* @__PURE__ */ n.jsx(ee, { variant: "list", rows: 3 }) : h ? /* @__PURE__ */ n.jsxs(n.Fragment, { children: [
|
|
614
|
+
/* @__PURE__ */ n.jsxs("div", { style: { marginBottom: 8, color: "#475569", fontSize: 13 }, children: [
|
|
615
|
+
"命中 ",
|
|
616
|
+
/* @__PURE__ */ n.jsx("b", { children: C(h.total_hits) ?? S.length }),
|
|
617
|
+
" 条 · 耗时 ",
|
|
618
|
+
/* @__PURE__ */ n.jsx("b", { children: C(h.elapsed_millis) }),
|
|
619
|
+
" ms · 模式 ",
|
|
620
|
+
/* @__PURE__ */ n.jsx("b", { children: h.mode }),
|
|
621
|
+
" · 召回分布 向量 ",
|
|
622
|
+
/* @__PURE__ */ n.jsx("b", { children: C(E.vector) ?? 0 }),
|
|
623
|
+
"/ 关键词 ",
|
|
624
|
+
/* @__PURE__ */ n.jsx("b", { children: C(E.keyword) ?? 0 }),
|
|
625
|
+
" / 图谱 ",
|
|
626
|
+
/* @__PURE__ */ n.jsx("b", { children: C(E.graph) ?? 0 })
|
|
627
|
+
] }),
|
|
628
|
+
K && /* @__PURE__ */ n.jsx(
|
|
629
|
+
Pe,
|
|
630
|
+
{
|
|
631
|
+
type: "warning",
|
|
632
|
+
showIcon: !0,
|
|
633
|
+
style: { marginBottom: 16 },
|
|
634
|
+
message: "向量召回恒为 0:不是没有向量,而是 L3 读写两侧集合名不一致",
|
|
635
|
+
description: /* @__PURE__ */ n.jsxs("span", { children: [
|
|
636
|
+
"collection ",
|
|
637
|
+
/* @__PURE__ */ n.jsx("code", { children: "kb_default_chunks_chunks" }),
|
|
638
|
+
" 实测已存 11624 条 512 维向量, 但读侧 ",
|
|
639
|
+
/* @__PURE__ */ n.jsx("code", { children: "HybridSearcher.java:42" }),
|
|
640
|
+
" 用 ",
|
|
641
|
+
/* @__PURE__ */ n.jsx("code", { children: '"kb_chunks"' }),
|
|
642
|
+
"、 写侧 ",
|
|
643
|
+
/* @__PURE__ */ n.jsx("code", { children: "DefaultKnowledgeBaseService.java:48" }),
|
|
644
|
+
" 用 ",
|
|
645
|
+
/* @__PURE__ */ n.jsx("code", { children: '"chunks"' }),
|
|
646
|
+
", 拼出的物理名 ",
|
|
647
|
+
/* @__PURE__ */ n.jsx("code", { children: "kb_default_chunks_kb_chunks" }),
|
|
648
|
+
" 不存在 ⇒",
|
|
649
|
+
/* @__PURE__ */ n.jsx("code", { children: "hasCollection()" }),
|
|
650
|
+
" 恒 false,向量分支静默返回空。 当前有效召回只有关键词与图谱两路。"
|
|
651
|
+
] })
|
|
652
|
+
}
|
|
653
|
+
),
|
|
654
|
+
S.length === 0 ? /* @__PURE__ */ n.jsx(
|
|
655
|
+
ce,
|
|
656
|
+
{
|
|
657
|
+
title: "没有召回到内容",
|
|
658
|
+
description: `接口通了,但「${h.query}」在该工作空间无召回(图谱实体 ${I.length} 个)`
|
|
659
|
+
}
|
|
660
|
+
) : /* @__PURE__ */ n.jsx(
|
|
661
|
+
H,
|
|
662
|
+
{
|
|
663
|
+
dataSource: S,
|
|
664
|
+
renderItem: (a) => {
|
|
665
|
+
var b;
|
|
666
|
+
return /* @__PURE__ */ n.jsx(H.Item, { align: "top", children: /* @__PURE__ */ n.jsx(
|
|
667
|
+
H.Item.Meta,
|
|
668
|
+
{
|
|
669
|
+
title: /* @__PURE__ */ n.jsxs("span", { style: { fontSize: 14 }, children: [
|
|
670
|
+
Ue(a),
|
|
671
|
+
" ",
|
|
672
|
+
/* @__PURE__ */ n.jsxs(pe, { size: 4, style: { display: "inline-flex" }, children: [
|
|
673
|
+
/* @__PURE__ */ n.jsx(J, { children: a.source }),
|
|
674
|
+
/* @__PURE__ */ n.jsxs(J, { color: "blue", children: [
|
|
675
|
+
"score ",
|
|
676
|
+
(b = C(a.score)) == null ? void 0 : b.toFixed(4)
|
|
677
|
+
] }),
|
|
678
|
+
a.ordinal !== null && /* @__PURE__ */ n.jsxs(J, { children: [
|
|
679
|
+
"分片 #",
|
|
680
|
+
a.ordinal
|
|
681
|
+
] })
|
|
682
|
+
] })
|
|
683
|
+
] }),
|
|
684
|
+
description: /* @__PURE__ */ n.jsxs(n.Fragment, { children: [
|
|
685
|
+
/* @__PURE__ */ n.jsx(
|
|
686
|
+
"div",
|
|
687
|
+
{
|
|
688
|
+
style: {
|
|
689
|
+
whiteSpace: "pre-wrap",
|
|
690
|
+
maxHeight: 180,
|
|
691
|
+
overflow: "auto",
|
|
692
|
+
fontSize: 13,
|
|
693
|
+
color: "#334155",
|
|
694
|
+
background: "#f8fafc",
|
|
695
|
+
padding: 8,
|
|
696
|
+
borderRadius: 4
|
|
697
|
+
},
|
|
698
|
+
children: a.content || "(该命中无正文:include_content 未开或切片正文为空)"
|
|
699
|
+
}
|
|
700
|
+
),
|
|
701
|
+
/* @__PURE__ */ n.jsxs("div", { style: { fontSize: 12, color: "#94a3b8", marginTop: 4 }, children: [
|
|
702
|
+
"chunk ",
|
|
703
|
+
a.chunk_id,
|
|
704
|
+
" · doc ",
|
|
705
|
+
a.document_id,
|
|
706
|
+
Array.isArray(a.heading_path) && a.heading_path.length > 0 ? ` · 路径 ${a.heading_path.join(" / ")}` : "",
|
|
707
|
+
Array.isArray(a.matched_entities) && a.matched_entities.length > 0 ? ` · 实体 ${a.matched_entities.join(", ")}` : ""
|
|
708
|
+
] })
|
|
709
|
+
] })
|
|
710
|
+
}
|
|
711
|
+
) }, a.chunk_id);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
)
|
|
715
|
+
] }) : /* @__PURE__ */ n.jsx(ce, { title: "输入关键词开始搜索", description: "例如「InfluxDB 写入性能优化」,回车即搜。" })
|
|
716
|
+
] });
|
|
717
|
+
}
|
|
718
|
+
function Me() {
|
|
719
|
+
return /* @__PURE__ */ n.jsxs(Se, { children: [
|
|
720
|
+
/* @__PURE__ */ n.jsx(U, { index: !0, element: /* @__PURE__ */ n.jsx(xe, { to: "workspaces", replace: !0 }) }),
|
|
721
|
+
/* @__PURE__ */ n.jsx(U, { path: "workspaces", element: /* @__PURE__ */ n.jsx($e, {}) }),
|
|
722
|
+
/* @__PURE__ */ n.jsx(U, { path: "documents", element: /* @__PURE__ */ n.jsx(Ne, {}) }),
|
|
723
|
+
/* @__PURE__ */ n.jsx(U, { path: "search", element: /* @__PURE__ */ n.jsx(ze, {}) })
|
|
724
|
+
] });
|
|
725
|
+
}
|
|
726
|
+
const Je = "# z-kb-component\n\nz-kb(知识库)跨仓唯一前端面:把「工作区 / 文档 / 数据接入 / 建模 / 检索 / 图谱 / 对话」\n这一组知识库页面收敛成一个可复用的 React 组件包,由 `z-kb-suit` 独立壳、z-team 的\nOpsWorkbench(`/kb/*` 通配挂载)或 z-opc 主壳挂载。\n\n- **npm 包名**:`@yuku123/z-kb-component`\n- **宿主接入**:`@yuku123/z-kb-component/pages`(manifest + 页面组件),主壳可只取 `ComponentDocPage`\n- **后端**:z-kb-web 的 `/api/kb/**`(7 个 controller,真实路由 33 条)\n- **技术栈**:React 19 + antd 6 + react-router-dom 7 + echarts 5 + vite\n (library mode,双入口 `.` / `./pages`)\n\n---\n\n## 一、能力概览\n\n这个包里有**两套**页面树、**两套**请求层,路由前缀也不同,别混:\n\n### 1.1 manifest 面(`/z-kb/*`,`src/pages-manifest.jsx`)\n\n| 页面 | 路由(相对 `/z-kb/*`) | 源文件 | 能力 |\n|------|--------------------------|--------|------|\n| 首页 | `home` | `pages/HomePage.jsx` | 读 `userInfo` 显示登录人,按 `menuItems` 渲染功能入口卡片 |\n| 总览 | `dashboard` | `kb/pages/Dashboard.tsx` | 工作区统计(文档数 / 分块数 / 实体数…) |\n| 文档 | `documents` | `kb/pages/Documents.tsx` | 文档入库 / 重建索引 / 删除 |\n| 数据接入 | `ingest` | `kb/pages/DataSources.tsx` | 数据源清单 + 批量接入 + webhook |\n| 建模 | `modeling` | `kb/pages/ModelingPage.tsx` | 拆解工作台 + 小抄(cheatsheet) |\n| 检索 | `search` | `kb/pages/SearchPage.tsx` | 混合检索(向量 / 关键词 / 图谱 / 融合) |\n| 图谱 | `graph` | `kb/pages/GraphPage.tsx` | 实体邻域 / 子图(echarts) |\n| 对话 | `chat` | `kb/pages/ChatPage.tsx` | 会话式问答 |\n| 登录 | (不在菜单内) | `pages/LoginPage.jsx` | mock 登录,写 `token` / `userInfo` 后跳 `/z-kb/home` |\n| 组件文档 | `docs` | `pages/ComponentDoc.jsx` | 本文件渲染页(左侧 240px TOC + 右侧正文) |\n\n`manifest` 里这 7 个 kb 页面都套了一层 `withKbWorkspace(...)`(`kb/workspaceHook`),\n由它统一注入当前工作区。\n\n### 1.2 KbApp 面(`/kb/*`,`src/pages/KbApp.jsx`)\n\n`KbApp` 是给 OpsWorkbench 通配挂载用的**子路由集合**,入口重定向到 `workspaces`:\n\n| 子路由 | 源文件 | 能力 |\n|--------|--------|------|\n| `workspaces` | `pages/WorkspaceListPage.jsx` | 工作区清单 + 统计 |\n| `documents` | `pages/DocumentListPage.jsx` | 文档清单(服务端分页)+ 删除 |\n| `search` | `pages/SearchPage.jsx` | 检索(JS 版页面,与 `kb/pages/SearchPage.tsx` **是两个不同文件**) |\n\n### 1.3 两套请求层\n\n| 层 | 文件 | 出口 | 用在哪些页面 |\n|----|------|------|--------------|\n| TS 版(axios) | `src/kb/services/api.ts` | `DocAPI` / `SearchAPI` / `GraphAPI` / `WorkspaceAPI` / `ChatAPI` / `IngestAPI` / `ModelingAPI` | `kb/pages/*.tsx` 全部 7 个页面 |\n| JS 版(fetch) | `src/services/api.js` | `kbApi` | `pages/WorkspaceListPage.jsx` / `pages/DocumentListPage.jsx` / `pages/SearchPage.jsx` |\n\n主入口(`.` 桶)导的是 **JS 版**的 `kbApi`;`./pages` 桶导的是 **TS 版**的\n`DocAPI` / `SearchAPI` / `GraphAPI` / `WorkspaceAPI`。\n\n## 二、导出面\n\n```js\n// 主入口(. 桶):JS 版请求层 + 域挂载点\nimport {kbApi, toNumber, DEFAULT_WORKSPACE, KbApp} from '@yuku123/z-kb-component'\n\n// 宿主组装面(./pages 桶):manifest + 页面组件(TS 版请求层也从这里出)\nimport {menuItems, routes, appMeta, HomePage, LoginPage,\n DocAPI, SearchAPI, GraphAPI, WorkspaceAPI,\n withKbWorkspace, useKbWorkspace, ComponentDocPage} from '@yuku123/z-kb-component/pages'\n\n// 组件文档页(主壳接线用,名字全仓统一)\nimport {ComponentDocPage} from '@yuku123/z-kb-component'\n```\n\n## 三、API 端点\n\n后端前缀 `/api/kb`。下表路径是 `src/kb/services/api.ts` 与 `src/services/api.js`\n里出现的**实际路径**(两层的路径口径一致,只是实现不同)。\n\n### 3.1 工作区\n\n| 方法 | 路径 | 说明 |\n|---|---|---|\n| GET | `/api/kb/workspaces` | 工作区列表 |\n| POST | `/api/kb/workspaces` | 新建工作区 |\n| GET | `/api/kb/workspaces/{name}` | 单个工作区 |\n| GET | `/api/kb/workspaces/{name}/statistics` | 工作区统计 |\n\n### 3.2 文档\n\n| 方法 | 路径 | 说明 |\n|---|---|---|\n| POST | `/api/kb/documents` | 建索引(body:`{document, overwrite, extractGraph, workspace}`) |\n| POST | `/api/kb/documents/batch` | 批量建索引 |\n| POST | `/api/kb/documents/markdown` | 导入 markdown |\n| GET | `/api/kb/documents/{workspace}` | 文档列表(query:`offset` / `limit`,服务端分页) |\n| GET | `/api/kb/documents/{workspace}/count` | 文档总数 |\n| GET | `/api/kb/documents/{workspace}/by-tags` | 按标签查文档(query:`tags` / `offset` / `limit`) |\n| GET | `/api/kb/documents/{workspace}/{id}` | 单篇文档 |\n| DELETE | `/api/kb/documents/{workspace}/{id}` | 删除文档 |\n| POST | `/api/kb/documents/{workspace}/{id}/reindex` | 重建索引 |\n\n### 3.3 检索 / 对话\n\n| 方法 | 路径 | 说明 |\n|---|---|---|\n| POST | `/api/kb/search` | 检索(body:`{query, workspace, mode, topK, ...}`,`mode` ∈ `FUSION`/`VECTOR`/`KEYWORD`/`GRAPH`/`HYBRID`) |\n| POST | `/api/kb/chat` | 单轮问答 |\n| POST | `/api/kb/chat/sessions` | 建会话 |\n| GET | `/api/kb/chat/sessions` | 会话列表(query:`workspace` / `userId` / `limit`) |\n| GET | `/api/kb/chat/sessions/{id}` | 单个会话 |\n| DELETE | `/api/kb/chat/sessions/{id}` | 删会话 |\n| POST | `/api/kb/chat/sessions/{id}/chat` | 会话内追问 |\n\n### 3.4 图谱\n\n| 方法 | 路径 | 说明 |\n|---|---|---|\n| GET | `/api/kb/graph/{workspace}/entities` | 实体列表(query:`limit`) |\n| GET | `/api/kb/graph/{workspace}/entities/{name}` | 单个实体 |\n| GET | `/api/kb/graph/{workspace}/entities/{name}/neighbors` | 邻域(query:`depth`) |\n| POST | `/api/kb/graph/{workspace}/subgraph?depth=` | 子图(body 是实体名数组) |\n\n### 3.5 数据接入 / 建模\n\n| 方法 | 路径 | 说明 |\n|---|---|---|\n| GET | `/api/kb/ingest/sources` | 数据源清单 + 支持的类型 |\n| GET | `/api/kb/ingest/stats` | 接入统计 |\n| POST | `/api/kb/ingest/run?sourceId=` | 跑一次接入 |\n| POST | `/api/kb/ingest/submit` | 批量提交接入 |\n| POST | `/api/kb/ingest/webhook` | 通用 webhook 入口 |\n| POST | `/api/kb/ingest/webhook/flush` | flush webhook 队列 |\n| GET | `/api/kb/modeling/decompose?workspace=` | 拆解工作台 |\n| GET | `/api/kb/modeling/cheatsheet?workspace=` | 取小抄 |\n| POST | `/api/kb/modeling/cheatsheet` | 写回小抄(body 是整份 ProductModel) |\n\n## 四、页面说明\n\n### 4.1 首页\n\n登录后的着陆页。读 `localStorage.userInfo` 显示欢迎语与角色标签,再把 `menuItems`(去掉首页自身)\n渲染成入口卡片 —— 菜单加一项,首页卡片自动多一张,不用改页面代码。\n\n### 4.2 总览 / 文档 / 数据接入 / 建模 / 检索 / 图谱 / 对话\n\n这 7 个页面在 `src/kb/pages/` 下(`.tsx`),都套 `withKbWorkspace` 注入当前工作区:\n\n- **总览**:工作区统计(文档数 / 分块数 / 实体数 / 命中率等)。\n- **文档**:文档入库(`overwrite` / `extractGraph` 开关)、重建索引、删除。\n- **数据接入**:数据源清单(`sourceId` / 类型 / 启用与否)+ 批量接入 + webhook 入口。\n- **建模**:拆解工作台(慢接口,可能 1–3 分钟)+ 小抄(cheatsheet)读写。\n- **检索**:按 `mode` 切 向量 / 关键词 / 图谱 / 融合检索。\n- **图谱**:实体邻域与子图,用 echarts 画(源码引的是 tree-shaken 子路径)。\n- **对话**:会话式问答。\n\n### 4.3 工作区 / 文档清单 / 检索(KbApp 面)\n\n`src/pages/` 下的三个 JS 页面:**工作区**清单 + 统计、**文档**清单(服务端分页,\n总数另走 `/count`)、**检索**。⚠ `pages/SearchPage.jsx` 与 `kb/pages/SearchPage.tsx`\n**是两个不同的文件**(前者用 `kbApi`,后者用 `SearchAPI`),改一个不会影响另一个。\n\n### 4.4 登录\n\n当前是 **mock 模式**:比对一对写死在 `src/pages/LoginPage.jsx` 里的管理员凭据\n(明文口令不在本文复述,见源码),成功后写 `localStorage.token` 与 `localStorage.userInfo`,\n再 `window.location.href = '/z-kb/home'`。CTC/SSO 尚未接入。\n\n### 4.5 组件文档\n\n本文件。内容以 `?raw` 在编译期内联,零额外请求;左侧 240px TOC 随滚动联动。\n\n## 五、配置\n\n| 配置项 | 默认值 | 说明 |\n|---|---|---|\n| 后端前缀 | `/api/kb` | 两层请求层都用这个前缀 |\n| 默认工作区 | `default` | `DEFAULT_WORKSPACE` |\n| 普通请求超时 | `30000` ms | TS 版 axios 实例 |\n| 慢请求超时 | `300000` ms | TS 版 `slowApi`(建模 / 大批量 ingest 用) |\n| 文档分页 | `offset=0` / `limit=20` | JS 版 `listDocuments` 的默认页长 |\n| 实体列表条数 | `100` | `listEntities` 的默认 `limit` |\n\n## 六、已知限制\n\n- ⚠ **请求体字段两种拼写都发**:宿主(z-opc)全局 Jackson 是 `SNAKE_CASE`,\n z-kb 单跑则是 camelCase。⇒ JS 版 `wire()` 给每个 camelCase 键**补一个 snake_case 别名**\n (不删原键),Boot 默认忽略未知属性 ⇒ 两边都收得下。\n 实测宿主里 `top_k` 生效、`topK` 被忽略。\n `frontmatter` / `metadata` / `properties` / `config` 四个键是**用户自己的 map**,\n 键名任意,不改写。\n- ⚠ **响应字段在宿主里是下划线 + 字符串**:宿主把 long 序列化成字符串\n (`document_count` / `chunk_count` / `created_at` / `count`)。\n JS 版请求层**不改响应形状**(改了会连带把 `WorkspaceListPage` / `DocumentListPage` 弄坏),\n 需要数值时用 `toNumber()`。\n- **两套请求层并存**:TS 版走 axios(有超时、有 `slowApi`),JS 版走原生 `fetch`\n (**没有超时**,慢请求靠浏览器/网关口径)。两层的响应解包口径也不同。\n- **同名不同文件**:`pages/SearchPage.jsx` ≠ `kb/pages/SearchPage.tsx`;\n `DocumentListPage` / `WorkspaceListPage` 也只在 JS 层存在。\n- `kbApi` 的 `createDocument` / `batchCreateDocuments` / `importMarkdown` / `reindexDocument` /\n `listDocumentsByTags` / `getDocument` / `deleteDocument` / `getWorkspace` / `createWorkspace` /\n `getWorkspaceStats` / `chat` / `createSession` / `getSession` / `listSessions` /\n `deleteSession` / `chatInSession` / `listEntities` / `getEntity` / `getNeighbors` /\n `getSubgraph` / `listSources` / `getIngestStats` / `runIngest` / `submitIngest` /\n `webhook` / `flushWebhook` / `decompose` / `getCheatsheet` / `createCheatsheet`\n 在 **JS 层定义了但当前没有页面调用**(这些页面对应的是 TS 版那套)。\n- **graph / ingest / modeling 三个 controller 没有裸根路由**:\n 第一条就是 `/{workspace}/...` 或 `/sources`、`/stats`、`/decompose`。\n- echarts 走 tree-shaken 子路径(`echarts/core`、`echarts/charts`、`echarts-for-react/lib/core`),\n `vite.config.js` 用函数式 `external` 把 `echarts/` 与 `echarts-for-react/` 前缀整体外置 ——\n 否则本包会多带 800KB+ 的 echarts,且绕开宿主的按需注册能力。\n- z-kb 已被 z-opc 主壳的 `domainRoutes.ts` 与 z-team 的 OpsWorkbench 引用\n ⇒ `/z-kb/docs` 在平台主壳里**可达**。\n\n## 七、变更记录\n\n| 版本 | 变更 |\n|---|---|\n| 0.1.2 | 新增组件文档页 `ComponentDocPage`(lead 008 §7):`src/DOC.md` + 基座 `ComponentDoc` 薄包装 |\n| 0.1.2 | 首版:工作区 / 文档 / 接入 / 建模 / 检索 / 图谱 / 对话页面集 + manifest + KbApp 挂载点 |\n";
|
|
727
|
+
function qe() {
|
|
728
|
+
return /* @__PURE__ */ n.jsx(je, { markdown: Je, title: "组件文档 — z-kb" });
|
|
729
|
+
}
|
|
730
|
+
export {
|
|
731
|
+
qe as C,
|
|
732
|
+
Me as K,
|
|
733
|
+
n as j,
|
|
734
|
+
D as k
|
|
735
|
+
};
|