hiwork-knowledge 0.1.0 → 0.2.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/README.md +133 -39
- package/cordis.patch.yml +19 -2
- package/lib/client.js +1599 -45
- package/lib/index.js +27793 -6
- package/lib/prompt.js +31 -0
- package/lib/protocol.js +139 -0
- package/lib/rpc.js +103 -0
- package/lib/service.js +344 -0
- package/lib/tool-names.js +15 -0
- package/lib/tools.js +244 -0
- package/lib/types/client/KnowledgeView.d.ts +19 -5
- package/lib/types/client/SettingsSection.d.ts +42 -0
- package/lib/types/client/ToolCards.d.ts +26 -0
- package/lib/types/client/composer.d.ts +98 -0
- package/lib/types/client/contracts.d.ts +100 -2
- package/lib/types/client/focus.d.ts +19 -0
- package/lib/types/client/format.d.ts +74 -0
- package/lib/types/client/icons.d.ts +26 -0
- package/lib/types/client/index.d.ts +31 -2
- package/lib/types/client/locales.d.ts +213 -6
- package/lib/types/client/runtime.d.ts +66 -0
- package/lib/types/client/tool-result.d.ts +90 -0
- package/lib/types/index.d.ts +49 -10
- package/lib/types/prompt.d.ts +21 -0
- package/lib/types/protocol.d.ts +200 -0
- package/lib/types/rpc.d.ts +18 -0
- package/lib/types/service.d.ts +142 -0
- package/lib/types/tool-names.d.ts +11 -0
- package/lib/types/tools.d.ts +21 -0
- package/lib/types/types.d.ts +109 -0
- package/lib/types/weknora.d.ts +112 -0
- package/lib/types.js +112 -0
- package/lib/weknora.js +366 -0
- package/package.json +28 -8
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/hiwork-knowledge` loopback RPC 的线上契约(Host 与 Web 共用)。
|
|
3
|
+
*
|
|
4
|
+
* 规则:
|
|
5
|
+
* - 客户端只提交"意图",永远不能提交凭据、分数或服务端状态;
|
|
6
|
+
* - 请求一律 `.strict()`:多一个字段就 `bad-request`,避免前端悄悄提交 `apiKey`;
|
|
7
|
+
* - **凭据单向**:`KnowledgeConfigView` 只有 `hasApiKey` 布尔,没有任何 Key 片段;
|
|
8
|
+
* - 命中结果里的 `score` 不进线上契约——实测它在未绑 rerank 时是 RRF 定值(0.0164),
|
|
9
|
+
* 展示出来只会误导用户(见设计文档 §3.5 / D5)。
|
|
10
|
+
*/
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import { type KnowledgeErrorCode } from './types.js';
|
|
13
|
+
import type { KnowledgeDocumentContent, SelfCheckResult } from './service.js';
|
|
14
|
+
import type { KnowledgeBaseSummary, KnowledgeDocSummary, KnowledgeHit } from './weknora.js';
|
|
15
|
+
/** 频道名;客户端 `runtime.ts` 里有一份字面量副本,由测试锁定一致。 */
|
|
16
|
+
export declare const KNOWLEDGE_RPC_CHANNEL = "/hiwork-knowledge";
|
|
17
|
+
export declare const KNOWLEDGE_RPC_ENDPOINTS: readonly ["snapshot", "config-save", "config-test", "docs", "document", "search"];
|
|
18
|
+
export type KnowledgeRpcEndpoint = (typeof KNOWLEDGE_RPC_ENDPOINTS)[number];
|
|
19
|
+
export declare function isKnowledgeRpcEndpoint(value: string): value is KnowledgeRpcEndpoint;
|
|
20
|
+
export type KnowledgeRpcErrorCode = KnowledgeErrorCode | 'conflict' | 'transport';
|
|
21
|
+
export interface RpcErrorValue {
|
|
22
|
+
readonly code: KnowledgeRpcErrorCode;
|
|
23
|
+
readonly message: string;
|
|
24
|
+
readonly details?: unknown;
|
|
25
|
+
}
|
|
26
|
+
export type RpcResult<T> = {
|
|
27
|
+
readonly ok: true;
|
|
28
|
+
readonly value: T;
|
|
29
|
+
} | {
|
|
30
|
+
readonly ok: false;
|
|
31
|
+
readonly error: RpcErrorValue;
|
|
32
|
+
};
|
|
33
|
+
/** 知识库视图(与 Host 的 `KnowledgeBaseSummary` 同形,但不含模型 id)。 */
|
|
34
|
+
export interface KnowledgeBaseView {
|
|
35
|
+
readonly id: string;
|
|
36
|
+
readonly name: string;
|
|
37
|
+
readonly description: string;
|
|
38
|
+
readonly documentCount: number | null;
|
|
39
|
+
readonly chunkCount: number | null;
|
|
40
|
+
readonly updatedAt: string;
|
|
41
|
+
}
|
|
42
|
+
export interface KnowledgeDocView {
|
|
43
|
+
readonly id: string;
|
|
44
|
+
readonly title: string;
|
|
45
|
+
readonly fileName: string;
|
|
46
|
+
readonly fileType: string;
|
|
47
|
+
readonly fileSize: number | null;
|
|
48
|
+
readonly parseStatus: string;
|
|
49
|
+
readonly enableStatus: string;
|
|
50
|
+
readonly summaryStatus: string;
|
|
51
|
+
readonly folderPath: string;
|
|
52
|
+
readonly createdAt: string;
|
|
53
|
+
}
|
|
54
|
+
/** 检索命中视图:**不含 score**(口径见文件头注释)。 */
|
|
55
|
+
export interface KnowledgeHitView {
|
|
56
|
+
readonly chunkId: string;
|
|
57
|
+
readonly knowledgeId: string;
|
|
58
|
+
readonly knowledgeTitle: string;
|
|
59
|
+
readonly fileName: string;
|
|
60
|
+
readonly chunkIndex: number | null;
|
|
61
|
+
readonly chunkType: string;
|
|
62
|
+
readonly content: string;
|
|
63
|
+
readonly truncated: boolean;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 文档正文视图(中央页阅读器用)。
|
|
67
|
+
*
|
|
68
|
+
* `truncated` 是**多义**的:既可能是「还有下一页」,也可能是「某段被 `maxChunkChars` 裁了」。
|
|
69
|
+
* 本插件照抄 Host 的口径(`service.readDocument` 就是这么算的),UI 用 `page`/`chunkTotal`
|
|
70
|
+
* 自己判断还有没有下一页,避免把「正好整页」误报成「已截断」。
|
|
71
|
+
*/
|
|
72
|
+
export interface KnowledgeDocumentView {
|
|
73
|
+
readonly knowledgeId: string;
|
|
74
|
+
readonly title: string;
|
|
75
|
+
readonly chunkTotal: number;
|
|
76
|
+
readonly page: number;
|
|
77
|
+
readonly pageSize: number;
|
|
78
|
+
readonly truncated: boolean;
|
|
79
|
+
readonly chunks: readonly {
|
|
80
|
+
readonly index: number | null;
|
|
81
|
+
readonly type: string;
|
|
82
|
+
readonly content: string;
|
|
83
|
+
readonly truncated: boolean;
|
|
84
|
+
}[];
|
|
85
|
+
}
|
|
86
|
+
export interface KnowledgeSnapshotView {
|
|
87
|
+
readonly config: {
|
|
88
|
+
readonly baseUrl: string;
|
|
89
|
+
readonly tenantId: string;
|
|
90
|
+
readonly hasApiKey: boolean;
|
|
91
|
+
readonly apiKeySource: 'settings' | 'env' | 'none';
|
|
92
|
+
readonly defaultBaseIds: readonly string[];
|
|
93
|
+
readonly maxResults: number;
|
|
94
|
+
readonly maxChunkChars: number;
|
|
95
|
+
readonly agentId: string;
|
|
96
|
+
readonly chatModelId: string;
|
|
97
|
+
};
|
|
98
|
+
readonly selfCheck: SelfCheckResult | null;
|
|
99
|
+
readonly bases: readonly KnowledgeBaseView[];
|
|
100
|
+
/** 拉取知识库列表失败时的原因(凭据缺失/后端不可达),成功为 null。 */
|
|
101
|
+
readonly lastError: string | null;
|
|
102
|
+
readonly serverNow: string;
|
|
103
|
+
}
|
|
104
|
+
export interface SnapshotRequest {
|
|
105
|
+
readonly sessionId?: string;
|
|
106
|
+
}
|
|
107
|
+
export interface ConfigSaveRequest {
|
|
108
|
+
readonly sessionId?: string;
|
|
109
|
+
readonly patch: {
|
|
110
|
+
readonly baseUrl?: string | undefined;
|
|
111
|
+
readonly apiKey?: string | undefined;
|
|
112
|
+
readonly tenantId?: string | undefined;
|
|
113
|
+
readonly defaultBaseIds?: readonly string[] | undefined;
|
|
114
|
+
readonly maxResults?: number | undefined;
|
|
115
|
+
readonly maxChunkChars?: number | undefined;
|
|
116
|
+
readonly agentId?: string | undefined;
|
|
117
|
+
readonly chatModelId?: string | undefined;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
export interface ConfigTestRequest {
|
|
121
|
+
readonly sessionId?: string;
|
|
122
|
+
}
|
|
123
|
+
export interface DocsRequest {
|
|
124
|
+
readonly sessionId?: string;
|
|
125
|
+
readonly baseId: string;
|
|
126
|
+
}
|
|
127
|
+
export interface DocumentRequest {
|
|
128
|
+
readonly sessionId?: string;
|
|
129
|
+
readonly knowledgeId: string;
|
|
130
|
+
readonly page?: number | undefined;
|
|
131
|
+
}
|
|
132
|
+
export interface SearchRequest {
|
|
133
|
+
readonly sessionId?: string;
|
|
134
|
+
readonly query: string;
|
|
135
|
+
readonly baseId?: string | undefined;
|
|
136
|
+
}
|
|
137
|
+
export interface ConfigSaveResponse {
|
|
138
|
+
readonly config: KnowledgeSnapshotView['config'];
|
|
139
|
+
}
|
|
140
|
+
export interface ConfigTestResponse {
|
|
141
|
+
readonly selfCheck: SelfCheckResult;
|
|
142
|
+
}
|
|
143
|
+
export interface DocsResponse {
|
|
144
|
+
readonly baseId: string;
|
|
145
|
+
readonly docs: readonly KnowledgeDocView[];
|
|
146
|
+
}
|
|
147
|
+
export interface DocumentResponse {
|
|
148
|
+
readonly document: KnowledgeDocumentView;
|
|
149
|
+
}
|
|
150
|
+
export interface SearchResponse {
|
|
151
|
+
readonly query: string;
|
|
152
|
+
readonly hits: readonly KnowledgeHitView[];
|
|
153
|
+
}
|
|
154
|
+
/** 把 Host 的结构投影成线上视图(剥掉凭据与分数)。 */
|
|
155
|
+
export declare function toBaseView(base: KnowledgeBaseSummary): KnowledgeBaseView;
|
|
156
|
+
export declare function toDocView(doc: KnowledgeDocSummary): KnowledgeDocView;
|
|
157
|
+
export declare function toDocumentView(document: KnowledgeDocumentContent): KnowledgeDocumentView;
|
|
158
|
+
export declare function toHitView(hit: KnowledgeHit, maxChunkChars: number): KnowledgeHitView;
|
|
159
|
+
export declare const snapshotRequestSchema: z.ZodObject<{
|
|
160
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
161
|
+
}, z.core.$strict>;
|
|
162
|
+
/** 设置补丁:字段全部可选,但**不接受 schema 之外任何字段**(尤其是不允许未知键)。 */
|
|
163
|
+
export declare const configSaveRequestSchema: z.ZodObject<{
|
|
164
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
165
|
+
patch: z.ZodObject<{
|
|
166
|
+
baseUrl: z.ZodOptional<z.ZodString>;
|
|
167
|
+
apiKey: z.ZodOptional<z.ZodString>;
|
|
168
|
+
tenantId: z.ZodOptional<z.ZodString>;
|
|
169
|
+
defaultBaseIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
170
|
+
maxResults: z.ZodOptional<z.ZodNumber>;
|
|
171
|
+
maxChunkChars: z.ZodOptional<z.ZodNumber>;
|
|
172
|
+
agentId: z.ZodOptional<z.ZodString>;
|
|
173
|
+
chatModelId: z.ZodOptional<z.ZodString>;
|
|
174
|
+
}, z.core.$strict>;
|
|
175
|
+
}, z.core.$strict>;
|
|
176
|
+
export declare const configTestRequestSchema: z.ZodObject<{
|
|
177
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
178
|
+
}, z.core.$strict>;
|
|
179
|
+
/**
|
|
180
|
+
* 页码上限 500:翻到第 500 页还没找到想看的内容,说明该换个检索词而不是继续翻,
|
|
181
|
+
* 而一个不带上限的 `page` 能让 `(page-1)*pageSize` 溢出成负索引。
|
|
182
|
+
*/
|
|
183
|
+
export declare const documentRequestSchema: z.ZodObject<{
|
|
184
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
185
|
+
knowledgeId: z.ZodString;
|
|
186
|
+
page: z.ZodOptional<z.ZodNumber>;
|
|
187
|
+
}, z.core.$strict>;
|
|
188
|
+
export declare const docsRequestSchema: z.ZodObject<{
|
|
189
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
190
|
+
baseId: z.ZodString;
|
|
191
|
+
}, z.core.$strict>;
|
|
192
|
+
export declare const searchRequestSchema: z.ZodObject<{
|
|
193
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
194
|
+
query: z.ZodString;
|
|
195
|
+
baseId: z.ZodOptional<z.ZodString>;
|
|
196
|
+
}, z.core.$strict>;
|
|
197
|
+
/** 任意异常 → 稳定 `{ code, message }`;未知异常降级为 `internal`。 */
|
|
198
|
+
export declare function toRpcErrorValue(error: unknown, aborted?: boolean): RpcErrorValue;
|
|
199
|
+
/** 解信封(客户端用;同样只认 `{ ok, value | error }`)。 */
|
|
200
|
+
export declare function unwrapRpcResult<T>(value: unknown): T;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type KnowledgeSnapshotView } from './protocol.js';
|
|
2
|
+
import type { KnowledgeService } from './service.js';
|
|
3
|
+
/** Host 侧 RPC 依赖的最小上下文(结构兼容,便于单测伪造)。 */
|
|
4
|
+
export interface KnowledgeRpcContext {
|
|
5
|
+
readonly logger: {
|
|
6
|
+
warn(message: string): void;
|
|
7
|
+
};
|
|
8
|
+
readonly connection: {
|
|
9
|
+
rpc: {
|
|
10
|
+
handle(channel: string, handler: (endpoint: string, payload: unknown, signal: AbortSignal) => Promise<unknown>, options: {
|
|
11
|
+
authority: 'loopback';
|
|
12
|
+
}): () => Promise<void> | void;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/** 组装快照:凭据视图 + 知识库列表 + (可选)上次自检结果。 */
|
|
17
|
+
export declare function buildSnapshot(service: KnowledgeService, signal: AbortSignal, selfCheck?: KnowledgeSnapshotView['selfCheck']): Promise<KnowledgeSnapshotView>;
|
|
18
|
+
export declare function registerKnowledgeRpc(ctx: KnowledgeRpcContext, service: KnowledgeService): () => Promise<void>;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host 侧知识库服务:凭据解析、WeKnora 调用、自检。
|
|
3
|
+
*
|
|
4
|
+
* 不变量:
|
|
5
|
+
* - **凭据只在这里落地**:读取(存储域 / 环境变量 / bundle 默认)与使用(拼请求头)都在
|
|
6
|
+
* Host 半边,Web 半边只看得到 `hasApiKey` 这类布尔;
|
|
7
|
+
* - 出网客户端按「有效配置」缓存,配置一变(保存设置)立刻重建,避免用旧 Key 打后端;
|
|
8
|
+
* - 自检分两层:**连接**(认证 + 列库)与**检索健康**(探针词是否命中、分数是否像重排分)。
|
|
9
|
+
* 后者是必须的——实测未绑重排模型时后端会"静默给错结果"(任意查询返回同一分块、
|
|
10
|
+
* 分数恒为 RRF 值),HTTP 层完全看不出来;
|
|
11
|
+
* - 所有对外方法都接受 `AbortSignal` 并把它透传到出网调用。
|
|
12
|
+
*/
|
|
13
|
+
import { knowledgeDomainSpec, type KnowledgeConfig } from './types.js';
|
|
14
|
+
import { type KnowledgeAnswer, type KnowledgeBaseSummary, type KnowledgeDocSummary, type KnowledgeHit, type WeKnoraClient, type WeKnoraTransport } from './weknora.js';
|
|
15
|
+
/** 存储表端口(结构兼容 `@deepseek-ai/dsh-storage-domain` 的 KvTable)。 */
|
|
16
|
+
export interface KnowledgeStorageTable<T = unknown> {
|
|
17
|
+
get(key: string): T | undefined;
|
|
18
|
+
entries(): IterableIterator<[string, T]>;
|
|
19
|
+
put(key: string, value: T): Promise<void>;
|
|
20
|
+
delete(key: string): Promise<boolean>;
|
|
21
|
+
}
|
|
22
|
+
/** 已打开的存储域端口。 */
|
|
23
|
+
export interface KnowledgeStorageDomain {
|
|
24
|
+
readonly name: string;
|
|
25
|
+
table(name: string): KnowledgeStorageTable;
|
|
26
|
+
close(): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
/** Host 服务需要的最小上下文端口(不 import cordis 运行时,便于单测伪造)。 */
|
|
29
|
+
export interface KnowledgeHostContext {
|
|
30
|
+
readonly logger: {
|
|
31
|
+
warn(message: string): void;
|
|
32
|
+
};
|
|
33
|
+
readonly storageDomain: {
|
|
34
|
+
open(spec: typeof knowledgeDomainSpec): Promise<KnowledgeStorageDomain>;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** 自检单条断言。 */
|
|
38
|
+
export interface SelfCheckItem {
|
|
39
|
+
readonly code: string;
|
|
40
|
+
readonly ok: boolean;
|
|
41
|
+
/** `error` 阻止使用;`warn` 可用但结果可能不准;`info` 仅提示。 */
|
|
42
|
+
readonly level: 'error' | 'warn' | 'info';
|
|
43
|
+
readonly message: string;
|
|
44
|
+
}
|
|
45
|
+
/** 自检汇总。 */
|
|
46
|
+
export interface SelfCheckResult {
|
|
47
|
+
readonly ok: boolean;
|
|
48
|
+
readonly level: 'ok' | 'warn' | 'error';
|
|
49
|
+
readonly items: readonly SelfCheckItem[];
|
|
50
|
+
readonly baseCount: number;
|
|
51
|
+
readonly checkedAt: string;
|
|
52
|
+
}
|
|
53
|
+
/** 设置页可见的配置投影(**不含 `apiKey` 明文**)。 */
|
|
54
|
+
export interface KnowledgeConfigView {
|
|
55
|
+
readonly baseUrl: string;
|
|
56
|
+
readonly tenantId: string;
|
|
57
|
+
readonly hasApiKey: boolean;
|
|
58
|
+
/** 凭据来源,便于用户判断"我填的 Key 到底生效没有"。 */
|
|
59
|
+
readonly apiKeySource: 'settings' | 'env' | 'none';
|
|
60
|
+
readonly defaultBaseIds: readonly string[];
|
|
61
|
+
readonly maxResults: number;
|
|
62
|
+
readonly maxChunkChars: number;
|
|
63
|
+
readonly agentId: string;
|
|
64
|
+
readonly chatModelId: string;
|
|
65
|
+
}
|
|
66
|
+
/** 文档正文(阅读器/工具共用)。 */
|
|
67
|
+
export interface KnowledgeDocumentContent {
|
|
68
|
+
readonly knowledgeId: string;
|
|
69
|
+
readonly title: string;
|
|
70
|
+
readonly chunkTotal: number;
|
|
71
|
+
readonly page: number;
|
|
72
|
+
readonly pageSize: number;
|
|
73
|
+
readonly truncated: boolean;
|
|
74
|
+
readonly chunks: readonly {
|
|
75
|
+
readonly index: number | null;
|
|
76
|
+
readonly type: string;
|
|
77
|
+
readonly content: string;
|
|
78
|
+
readonly truncated: boolean;
|
|
79
|
+
}[];
|
|
80
|
+
}
|
|
81
|
+
/** 设置页提交的补丁:只接受 schema 里的字段,全部可选。 */
|
|
82
|
+
export type KnowledgeConfigPatch = Partial<KnowledgeConfig>;
|
|
83
|
+
/** 把用户补丁合并进当前配置,并校验(非法输入抛 `bad-request`)。 */
|
|
84
|
+
export declare function mergeConfig(current: KnowledgeConfig, patch: KnowledgeConfigPatch): KnowledgeConfig;
|
|
85
|
+
/** 把片段截断到 `maxChunkChars`,并显式报告是否截断(不静默丢内容)。 */
|
|
86
|
+
export declare function clipContent(content: string, limit: number): {
|
|
87
|
+
text: string;
|
|
88
|
+
truncated: boolean;
|
|
89
|
+
};
|
|
90
|
+
/** 知识库服务。 */
|
|
91
|
+
export declare class KnowledgeService {
|
|
92
|
+
private readonly ctx;
|
|
93
|
+
private domain;
|
|
94
|
+
private stored;
|
|
95
|
+
private bundleDefault;
|
|
96
|
+
private cached;
|
|
97
|
+
private constructor();
|
|
98
|
+
/** 打开存储域并读回已保存的设置。 */
|
|
99
|
+
static open(ctx: KnowledgeHostContext, bundleDefault?: KnowledgeConfigPatch): Promise<KnowledgeService>;
|
|
100
|
+
dispose(): Promise<void>;
|
|
101
|
+
/** 有效凭据来源(存储域 > 环境变量 > 默认空)。 */
|
|
102
|
+
apiKeySource(): KnowledgeConfigView['apiKeySource'];
|
|
103
|
+
/** 有效配置(含明文 Key,仅 Host 内部使用)。 */
|
|
104
|
+
effectiveConfig(): KnowledgeConfig;
|
|
105
|
+
/** 设置页视图:剥掉凭据明文。 */
|
|
106
|
+
getConfigView(): KnowledgeConfigView;
|
|
107
|
+
/**
|
|
108
|
+
* 保存设置补丁。
|
|
109
|
+
*
|
|
110
|
+
* 空字符串 = 显式清空;`undefined` = 保持原值。写盘后立刻丢弃缓存的客户端。
|
|
111
|
+
*/
|
|
112
|
+
saveConfig(patch: KnowledgeConfigPatch): Promise<KnowledgeConfigView>;
|
|
113
|
+
/** 出网客户端(按有效配置缓存)。 */
|
|
114
|
+
client(): WeKnoraClient;
|
|
115
|
+
/** 需要凭据的调用统一先过这道门;没配 Key 时给出可执行的提示。 */
|
|
116
|
+
private requireCredentials;
|
|
117
|
+
private requireDomain;
|
|
118
|
+
listBases(signal?: AbortSignal): Promise<readonly KnowledgeBaseSummary[]>;
|
|
119
|
+
listDocs(baseId: string, signal?: AbortSignal): Promise<readonly KnowledgeDocSummary[]>;
|
|
120
|
+
search(query: string, scope?: {
|
|
121
|
+
readonly baseIds?: readonly string[] | undefined;
|
|
122
|
+
readonly knowledgeIds?: readonly string[] | undefined;
|
|
123
|
+
}, signal?: AbortSignal): Promise<readonly KnowledgeHit[]>;
|
|
124
|
+
/** 读一篇文档:分块 + 标题,按 `chunkIndex` 顺序拼装并分页。 */
|
|
125
|
+
readDocument(knowledgeId: string, options?: {
|
|
126
|
+
readonly page?: number | undefined;
|
|
127
|
+
readonly pageSize?: number | undefined;
|
|
128
|
+
}, signal?: AbortSignal): Promise<KnowledgeDocumentContent>;
|
|
129
|
+
/** 问答:固定走 `/knowledge-chat` + 自建 Agent(内置 agent 缺 model_id,必失败)。 */
|
|
130
|
+
ask(query: string, scope?: {
|
|
131
|
+
readonly baseIds?: readonly string[];
|
|
132
|
+
}, signal?: AbortSignal): Promise<KnowledgeAnswer>;
|
|
133
|
+
/**
|
|
134
|
+
* 自检:连接 + 检索健康 + 问答可用性。
|
|
135
|
+
*
|
|
136
|
+
* 检索健康探针用「第一个可见知识库的第一篇文档标题」当查询词——它一定命中自己,
|
|
137
|
+
* 因此「命中 0 条」只可能是配置问题(未绑重排 / 模型 id 失效),不是数据问题。
|
|
138
|
+
*/
|
|
139
|
+
selfCheck(signal?: AbortSignal): Promise<SelfCheckResult>;
|
|
140
|
+
}
|
|
141
|
+
/** 单测/自检用:注入自定义出网的临时客户端工厂。 */
|
|
142
|
+
export declare function clientForTest(config: KnowledgeConfig, transport: WeKnoraTransport): WeKnoraClient;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 知识库工具名(**单一事实源**)。
|
|
3
|
+
*
|
|
4
|
+
* Host 半边用它注册工具,Web 半边用它认领 `tool.call.toolview` 的 keyed 座位——
|
|
5
|
+
* 两边各写一份就会漂移,而 keyed 座位的漂移是**静默**的(拼错不抛错,只是卡片永远不渲染)。
|
|
6
|
+
*
|
|
7
|
+
* 本文件零依赖:client bundle 是自包含的,共享模块不能引任何东西进来。
|
|
8
|
+
*/
|
|
9
|
+
/** 4 个只读工具(顺序即注册顺序)。 */
|
|
10
|
+
export declare const KNOWLEDGE_TOOL_NAMES: readonly ["knowledge_list_bases", "knowledge_search", "knowledge_read_document", "knowledge_ask"];
|
|
11
|
+
export type KnowledgeToolName = (typeof KNOWLEDGE_TOOL_NAMES)[number];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { KNOWLEDGE_TOOL_NAMES } from './tool-names.js';
|
|
2
|
+
import type { KnowledgeService } from './service.js';
|
|
3
|
+
export { KNOWLEDGE_TOOL_NAMES };
|
|
4
|
+
/** 注册工具所需的最小 Agent 结构端口(不 import DSH agent 运行时)。 */
|
|
5
|
+
export type KnowledgeAgent = {
|
|
6
|
+
readonly id: string;
|
|
7
|
+
readonly ctx: {
|
|
8
|
+
readonly tools: {
|
|
9
|
+
register(definition: unknown): () => void;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* 给一个根 Agent 注册知识库工具。
|
|
15
|
+
* @param service - Host 侧知识库服务。
|
|
16
|
+
* @param agent - 目标根 Agent。
|
|
17
|
+
* @returns 注销器(注销全部已注册工具)。
|
|
18
|
+
*/
|
|
19
|
+
export declare function registerKnowledgeTools(service: KnowledgeService, agent: KnowledgeAgent): () => void;
|
|
20
|
+
/** 供测试断言:工具说明里必须出现的行为约束。 */
|
|
21
|
+
export declare const KNOWLEDGE_TOOL_GUIDE_TEXT = "\u77E5\u8BC6\u5E93\u68C0\u7D22\u8FD4\u56DE 0 \u6761\u65F6\uFF0C\u5982\u5B9E\u8BF4\u660E\u60C5\u51B5\uFF0C\u4E0D\u8981\u51ED\u5E38\u8BC6\u4F5C\u7B54\uFF1B\u5F15\u7528\u7ED3\u8BBA\u5FC5\u987B\u9644\u51FA\u5904\u3002";
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `hiwork-knowledge` Host 半边的领域类型、默认值与存储域定义。
|
|
3
|
+
*
|
|
4
|
+
* 不变量:
|
|
5
|
+
* - 存储域名字必须匹配 `/^[a-z][a-z0-9_]*$/`(不能带连字符),改名等于换一套表;
|
|
6
|
+
* - 凭据(`apiKey`)只在 Host 半边流转:落盘在存储域、下发 WeKnora 时只进请求头,
|
|
7
|
+
* **绝不进日志、绝不回传给 Web 半边**(`toConfigView` 只回 `hasApiKey` 布尔);
|
|
8
|
+
* - 默认值与本节常量一一对应,`cordis.patch.yml` 的 config 只能收紧、不能放宽
|
|
9
|
+
* (例如把 `maxChunkChars` 调大仍受 schema 上限约束)。
|
|
10
|
+
*/
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
/** `ctx.storageDomain.open()` 的域标识;必须保持稳定。 */
|
|
13
|
+
export declare const KNOWLEDGE_DOMAIN_NAME = "hiwork_knowledge";
|
|
14
|
+
export declare const KNOWLEDGE_DOMAIN_VERSION = 1;
|
|
15
|
+
/** 设置只有一条记录,键名固定。 */
|
|
16
|
+
export declare const KNOWLEDGE_SETTINGS_KEY = "config";
|
|
17
|
+
/** 默认后端地址:与部署文档里的域名一致(`/api/v1` 会被补齐)。 */
|
|
18
|
+
export declare const DEFAULT_BASE_URL = "https://hiwork-knowledge.hivery.cn/api/v1";
|
|
19
|
+
export declare const DEFAULT_MAX_RESULTS = 8;
|
|
20
|
+
export declare const DEFAULT_MAX_CHUNK_CHARS = 1200;
|
|
21
|
+
export declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
22
|
+
export declare const DEFAULT_CHAT_TIMEOUT_MS = 300000;
|
|
23
|
+
/** 检索结果条数上限(服务端受 `rerank_threshold` 约束,这里只做客户端截断)。 */
|
|
24
|
+
export declare const MAX_RESULTS_CEILING = 50;
|
|
25
|
+
/** 单条片段字符上限。 */
|
|
26
|
+
export declare const MAX_CHUNK_CHARS_CEILING = 8000;
|
|
27
|
+
export declare const MAX_BASE_IDS = 50;
|
|
28
|
+
/**
|
|
29
|
+
* 「重排未生效」的分数判据。
|
|
30
|
+
*
|
|
31
|
+
* 2026-09-18 实测(同一后端、同一知识库):
|
|
32
|
+
* - 租户检索配置绑好 `rerank_model_id` 时,命中分数落在 **0.43–0.59**;
|
|
33
|
+
* - 未绑时管线 `Rerank action=skip reason="empty_model_id"`,分数是 RRF 融合值
|
|
34
|
+
* **0.0164**(且任意查询都返回同一个分块)。
|
|
35
|
+
* 因此「最高分 < 0.05」可作为「检索结果未经重排」的稳定信号,用于自检告警。
|
|
36
|
+
*/
|
|
37
|
+
export declare const RRF_SCORE_CEILING = 0.05;
|
|
38
|
+
/** 自检探针最多检查的知识库数(避免一次自检打穿后端)。 */
|
|
39
|
+
export declare const SELF_CHECK_BASE_LIMIT = 3;
|
|
40
|
+
export declare const knowledgeConfigSchema: z.ZodObject<{
|
|
41
|
+
baseUrl: z.ZodString;
|
|
42
|
+
apiKey: z.ZodString;
|
|
43
|
+
tenantId: z.ZodString;
|
|
44
|
+
defaultBaseIds: z.ZodArray<z.ZodString>;
|
|
45
|
+
maxResults: z.ZodNumber;
|
|
46
|
+
maxChunkChars: z.ZodNumber;
|
|
47
|
+
requestTimeoutMs: z.ZodNumber;
|
|
48
|
+
chatTimeoutMs: z.ZodNumber;
|
|
49
|
+
agentId: z.ZodString;
|
|
50
|
+
chatModelId: z.ZodString;
|
|
51
|
+
}, z.core.$strict>;
|
|
52
|
+
export type KnowledgeConfig = z.infer<typeof knowledgeConfigSchema>;
|
|
53
|
+
/** 默认配置:域名来自部署文档,凭据留空由用户在设置页填。 */
|
|
54
|
+
export declare const DEFAULT_KNOWLEDGE_CONFIG: KnowledgeConfig;
|
|
55
|
+
/**
|
|
56
|
+
* 把用户输入的地址补齐成 `<origin>/api/v1` 形态。
|
|
57
|
+
*
|
|
58
|
+
* 只做补路径与去尾斜杠,不猜测协议(缺协议时由 `fetch` 报错,错误信息里带上原值,
|
|
59
|
+
* 比 Host 静默拼一个 `https://` 更好排查)。
|
|
60
|
+
*/
|
|
61
|
+
export declare function normalizeBaseUrl(raw: string): string;
|
|
62
|
+
/** 凭据/网络类错误的稳定分类;RPC 与工具都把异常映射成这些码。 */
|
|
63
|
+
export type KnowledgeErrorCode = 'misconfigured' | 'unauthorized' | 'not-found' | 'bad-request' | 'unreachable' | 'timeout' | 'cancelled' | 'internal';
|
|
64
|
+
/** Host 侧统一异常:携带稳定 code 与**不含凭据**的 message。 */
|
|
65
|
+
export declare class KnowledgeError extends Error {
|
|
66
|
+
readonly code: KnowledgeErrorCode;
|
|
67
|
+
constructor(code: KnowledgeErrorCode, message: string);
|
|
68
|
+
}
|
|
69
|
+
/** 存储域的一条设置记录(带版本戳,便于将来迁移)。 */
|
|
70
|
+
export declare const knowledgeSettingsRecordSchema: z.ZodObject<{
|
|
71
|
+
version: z.ZodLiteral<1>;
|
|
72
|
+
config: z.ZodObject<{
|
|
73
|
+
baseUrl: z.ZodString;
|
|
74
|
+
apiKey: z.ZodString;
|
|
75
|
+
tenantId: z.ZodString;
|
|
76
|
+
defaultBaseIds: z.ZodArray<z.ZodString>;
|
|
77
|
+
maxResults: z.ZodNumber;
|
|
78
|
+
maxChunkChars: z.ZodNumber;
|
|
79
|
+
requestTimeoutMs: z.ZodNumber;
|
|
80
|
+
chatTimeoutMs: z.ZodNumber;
|
|
81
|
+
agentId: z.ZodString;
|
|
82
|
+
chatModelId: z.ZodString;
|
|
83
|
+
}, z.core.$strict>;
|
|
84
|
+
}, z.core.$strict>;
|
|
85
|
+
export type KnowledgeSettingsRecord = z.infer<typeof knowledgeSettingsRecordSchema>;
|
|
86
|
+
/** `storageDomain.open()` 的域描述。 */
|
|
87
|
+
export declare const knowledgeDomainSpec: {
|
|
88
|
+
readonly name: "hiwork_knowledge";
|
|
89
|
+
readonly version: 1;
|
|
90
|
+
readonly tables: {
|
|
91
|
+
readonly settings: {
|
|
92
|
+
readonly valueSchema: z.ZodObject<{
|
|
93
|
+
version: z.ZodLiteral<1>;
|
|
94
|
+
config: z.ZodObject<{
|
|
95
|
+
baseUrl: z.ZodString;
|
|
96
|
+
apiKey: z.ZodString;
|
|
97
|
+
tenantId: z.ZodString;
|
|
98
|
+
defaultBaseIds: z.ZodArray<z.ZodString>;
|
|
99
|
+
maxResults: z.ZodNumber;
|
|
100
|
+
maxChunkChars: z.ZodNumber;
|
|
101
|
+
requestTimeoutMs: z.ZodNumber;
|
|
102
|
+
chatTimeoutMs: z.ZodNumber;
|
|
103
|
+
agentId: z.ZodString;
|
|
104
|
+
chatModelId: z.ZodString;
|
|
105
|
+
}, z.core.$strict>;
|
|
106
|
+
}, z.core.$strict>;
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WeKnora REST 客户端(Host 半边专用)。
|
|
3
|
+
*
|
|
4
|
+
* 设计要点:
|
|
5
|
+
* - 只实现 M0 需要的只读端点 + 问答(SSE);写操作(上传/删除)在 M2 才开;
|
|
6
|
+
* - 所有出网调用都带超时:`requestTimeoutMs` 给普通请求,`chatTimeoutMs` 给问答;
|
|
7
|
+
* - **实测行为**:后端在出错时**不主动关闭 SSE 流**(连接会一直挂着),
|
|
8
|
+
* 因此问答循环必须在收到 `response_type=error` 时立刻主动 abort;
|
|
9
|
+
* - 解析对形状容错(`data` 既可能是数组也可能是 `{items,total}`),但**不臆造字段**:
|
|
10
|
+
* 缺字段就是 `undefined`,由上层决定如何展示;
|
|
11
|
+
* - 错误一律映射成 `KnowledgeError`,message 里带上 HTTP 状态与 WeKnora 的原始理由,
|
|
12
|
+
* 但**永不回显 API Key**。
|
|
13
|
+
*/
|
|
14
|
+
import { type KnowledgeConfig } from './types.js';
|
|
15
|
+
/** 知识库摘要(列表页用)。 */
|
|
16
|
+
export interface KnowledgeBaseSummary {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly name: string;
|
|
19
|
+
readonly description: string;
|
|
20
|
+
readonly documentCount: number | null;
|
|
21
|
+
readonly chunkCount: number | null;
|
|
22
|
+
readonly embeddingModelId: string;
|
|
23
|
+
readonly summaryModelId: string;
|
|
24
|
+
readonly updatedAt: string;
|
|
25
|
+
}
|
|
26
|
+
/** 文档摘要(知识库详情页用)。 */
|
|
27
|
+
export interface KnowledgeDocSummary {
|
|
28
|
+
readonly id: string;
|
|
29
|
+
readonly title: string;
|
|
30
|
+
readonly fileName: string;
|
|
31
|
+
readonly fileType: string;
|
|
32
|
+
readonly fileSize: number | null;
|
|
33
|
+
/** `pending` / `processing` / `finalizing` / `completed` / `failed` / `cancelled`。 */
|
|
34
|
+
readonly parseStatus: string;
|
|
35
|
+
/** `enabled` / `disabled`。 */
|
|
36
|
+
readonly enableStatus: string;
|
|
37
|
+
/** `none` / `processing` / `completed` / `failed`。 */
|
|
38
|
+
readonly summaryStatus: string;
|
|
39
|
+
readonly folderPath: string;
|
|
40
|
+
readonly createdAt: string;
|
|
41
|
+
}
|
|
42
|
+
/** 一条检索命中。 */
|
|
43
|
+
export interface KnowledgeHit {
|
|
44
|
+
readonly chunkId: string;
|
|
45
|
+
readonly content: string;
|
|
46
|
+
readonly knowledgeId: string;
|
|
47
|
+
readonly chunkIndex: number | null;
|
|
48
|
+
readonly knowledgeTitle: string;
|
|
49
|
+
readonly fileName: string;
|
|
50
|
+
/** 重排后的分数(未绑 rerank 时是 RRF 值,见 `RRF_SCORE_CEILING`)。 */
|
|
51
|
+
readonly score: number | null;
|
|
52
|
+
readonly chunkType: string;
|
|
53
|
+
readonly startAt: number | null;
|
|
54
|
+
readonly endAt: number | null;
|
|
55
|
+
}
|
|
56
|
+
/** 问答引用(来自 SSE 的 `references` 帧)。 */
|
|
57
|
+
export interface KnowledgeReference {
|
|
58
|
+
readonly chunkId: string;
|
|
59
|
+
readonly content: string;
|
|
60
|
+
readonly knowledgeId: string;
|
|
61
|
+
readonly chunkIndex: number | null;
|
|
62
|
+
readonly knowledgeTitle: string;
|
|
63
|
+
readonly chunkType: string;
|
|
64
|
+
readonly startAt: number | null;
|
|
65
|
+
readonly endAt: number | null;
|
|
66
|
+
}
|
|
67
|
+
/** 问答结果(SSE 收敛成一次性结果)。 */
|
|
68
|
+
export interface KnowledgeAnswer {
|
|
69
|
+
readonly answer: string;
|
|
70
|
+
readonly references: readonly KnowledgeReference[];
|
|
71
|
+
readonly sessionId: string;
|
|
72
|
+
/** 命中的工具调用名(如 `knowledge_search`),便于向用户解释耗时。 */
|
|
73
|
+
readonly toolsUsed: readonly string[];
|
|
74
|
+
readonly elapsedMs: number;
|
|
75
|
+
}
|
|
76
|
+
/** 自检结果。 */
|
|
77
|
+
export interface ProbeResult {
|
|
78
|
+
readonly ok: boolean;
|
|
79
|
+
readonly code: string;
|
|
80
|
+
readonly message: string;
|
|
81
|
+
readonly baseCount?: number;
|
|
82
|
+
}
|
|
83
|
+
/** 出网端口;单测直接注入假实现,无需起 HTTP 服务。 */
|
|
84
|
+
export interface WeKnoraTransport {
|
|
85
|
+
(input: string, init: RequestInit): Promise<Response>;
|
|
86
|
+
}
|
|
87
|
+
/** 客户端端口(服务层与工具层都只依赖它)。 */
|
|
88
|
+
export interface WeKnoraClient {
|
|
89
|
+
listBases(signal?: AbortSignal): Promise<readonly KnowledgeBaseSummary[]>;
|
|
90
|
+
listDocs(baseId: string, signal?: AbortSignal): Promise<readonly KnowledgeDocSummary[]>;
|
|
91
|
+
getDoc(knowledgeId: string, signal?: AbortSignal): Promise<KnowledgeDocSummary | null>;
|
|
92
|
+
chunks(knowledgeId: string, signal?: AbortSignal): Promise<readonly KnowledgeHit[]>;
|
|
93
|
+
search(query: string, scope: {
|
|
94
|
+
readonly baseIds?: readonly string[];
|
|
95
|
+
readonly knowledgeIds?: readonly string[];
|
|
96
|
+
}, signal?: AbortSignal): Promise<readonly KnowledgeHit[]>;
|
|
97
|
+
ask(query: string, scope: {
|
|
98
|
+
readonly baseIds?: readonly string[];
|
|
99
|
+
}, signal?: AbortSignal): Promise<KnowledgeAnswer>;
|
|
100
|
+
capabilities(signal?: AbortSignal): Promise<Readonly<Record<string, {
|
|
101
|
+
supported: boolean;
|
|
102
|
+
reason?: string;
|
|
103
|
+
}>>>;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 创建 WeKnora 客户端。
|
|
107
|
+
* @param config - 凭据与超时配置(`baseUrl` 会先归一化)。
|
|
108
|
+
* @param transport - 出网实现;缺省用全局 `fetch`(Node 24 自带)。
|
|
109
|
+
*/
|
|
110
|
+
export declare function createWeKnoraClient(config: KnowledgeConfig, transport?: WeKnoraTransport): WeKnoraClient;
|
|
111
|
+
/** 自检用:不带任何业务查询地探测后端可达性与凭据(`GET /knowledge-bases`)。 */
|
|
112
|
+
export declare function probeConnection(config: KnowledgeConfig, transport?: WeKnoraTransport, signal?: AbortSignal): Promise<ProbeResult>;
|