@klarkxy/dsh-zhihu 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/tools.js ADDED
@@ -0,0 +1,494 @@
1
+ import { _ as reportExecuted, a as executeZhihuGlobalSearch, c as normalizeRecallScopes, d as renderZhihuHotList, f as renderZhihuKnowledgeSearch, i as executeZhihuAsk, l as renderZhihuAsk, m as renderZhihuSearch, n as ZHIHU_ASK_MODELS, o as executeZhihuHotList, p as executeZhihuSearch, r as ZHIHU_RECALL_SCOPES, s as executeZhihuKnowledgeSearch, t as ZHIHU_ASK_DEFAULT_MODEL, u as renderZhihuGlobalSearch } from "./operations.js";
2
+ import { ZHIHU_ASK_TOOL_NAME, ZHIHU_GLOBAL_SEARCH_TOOL_NAME, ZHIHU_HOT_LIST_TOOL_NAME, ZHIHU_KNOWLEDGE_SEARCH_TOOL_NAME, ZHIHU_SEARCH_TOOL_NAME } from "./contracts.js";
3
+ import { defineTool } from "@deepseek-ai/dsh-tools";
4
+ //#region src/tool-definitions.ts
5
+ const SEARCH_ITEM_SCHEMA = {
6
+ type: "object",
7
+ additionalProperties: false,
8
+ properties: {
9
+ title: {
10
+ type: "string",
11
+ required: true
12
+ },
13
+ type: {
14
+ type: "string",
15
+ required: true
16
+ },
17
+ url: {
18
+ type: "string",
19
+ required: true
20
+ },
21
+ summary: {
22
+ type: "string",
23
+ required: true
24
+ },
25
+ votes: {
26
+ type: "integer",
27
+ required: true
28
+ },
29
+ comments: {
30
+ type: "integer",
31
+ required: true
32
+ },
33
+ author: {
34
+ type: "string",
35
+ required: true
36
+ },
37
+ authority: {
38
+ type: "string",
39
+ required: true
40
+ },
41
+ editTime: {
42
+ type: "string",
43
+ required: true
44
+ }
45
+ }
46
+ };
47
+ function createZhihuSearchTool(options = {}) {
48
+ const { onExecuted, ...client } = options;
49
+ return defineTool({
50
+ name: ZHIHU_SEARCH_TOOL_NAME,
51
+ description: "调用知乎开放平台站内搜索(GET /api/v1/content/zhihu_search)拉取社区证据;结果仅作社区/读者反馈参考,不构成 canon,也不直接写入项目文件。",
52
+ parameters: {
53
+ query: {
54
+ type: "string",
55
+ required: true,
56
+ description: "搜索词,2-100 字符。"
57
+ },
58
+ count: {
59
+ type: "integer",
60
+ description: `返回条数,1-10,默认 5。`
61
+ }
62
+ },
63
+ output: {
64
+ schema: {
65
+ type: "object",
66
+ additionalProperties: false,
67
+ properties: {
68
+ version: {
69
+ type: "integer",
70
+ required: true
71
+ },
72
+ query: {
73
+ type: "string",
74
+ required: true
75
+ },
76
+ count: {
77
+ type: "integer",
78
+ required: true
79
+ },
80
+ items: {
81
+ type: "array",
82
+ required: true,
83
+ items: SEARCH_ITEM_SCHEMA
84
+ },
85
+ emptyReason: { type: "string" }
86
+ }
87
+ },
88
+ render(_args, value) {
89
+ return [{
90
+ type: "text",
91
+ text: renderZhihuSearch(value)
92
+ }];
93
+ }
94
+ },
95
+ isConcurrencySafe() {
96
+ return true;
97
+ },
98
+ async execute(args, exec) {
99
+ const typed = args;
100
+ try {
101
+ const result = await executeZhihuSearch(typed.query, typed.count ?? 5, {
102
+ ...client,
103
+ signal: exec.signal
104
+ });
105
+ reportExecuted(onExecuted, {
106
+ ok: true,
107
+ results: result.items.length
108
+ });
109
+ return result;
110
+ } catch (error) {
111
+ reportExecuted(onExecuted, {
112
+ ok: false,
113
+ results: 0
114
+ });
115
+ throw error;
116
+ }
117
+ }
118
+ });
119
+ }
120
+ const OTHER_SEARCH_ITEM_SCHEMA = {
121
+ type: "object",
122
+ additionalProperties: false,
123
+ properties: {
124
+ title: {
125
+ type: "string",
126
+ required: true
127
+ },
128
+ type: {
129
+ type: "string",
130
+ required: true
131
+ },
132
+ url: {
133
+ type: "string",
134
+ required: true
135
+ },
136
+ summary: {
137
+ type: "string",
138
+ required: true
139
+ },
140
+ votes: {
141
+ type: "integer",
142
+ required: true
143
+ },
144
+ comments: {
145
+ type: "integer",
146
+ required: true
147
+ },
148
+ author: {
149
+ type: "string",
150
+ required: true
151
+ },
152
+ authority: {
153
+ type: "string",
154
+ required: true
155
+ },
156
+ editTime: {
157
+ type: "string",
158
+ required: true
159
+ }
160
+ }
161
+ };
162
+ /** 包一层计量:成功报 results,失败报 ok:false 再原样抛出。 */
163
+ async function metered(onExecuted, run, results) {
164
+ try {
165
+ const value = await run();
166
+ reportExecuted(onExecuted, {
167
+ ok: true,
168
+ results: results(value)
169
+ });
170
+ return value;
171
+ } catch (error) {
172
+ reportExecuted(onExecuted, {
173
+ ok: false,
174
+ results: 0
175
+ });
176
+ throw error;
177
+ }
178
+ }
179
+ function createZhihuGlobalSearchTool(options = {}) {
180
+ const { onExecuted, ...client } = options;
181
+ return defineTool({
182
+ name: ZHIHU_GLOBAL_SEARCH_TOOL_NAME,
183
+ description: "调用知乎开放平台全网搜索(GET /api/v1/content/global_search)检索站外公开网页资料;结果仅作参考,不构成 canon,也不直接写入项目文件。",
184
+ parameters: {
185
+ query: {
186
+ type: "string",
187
+ required: true,
188
+ description: "搜索词,2-100 字符。"
189
+ },
190
+ count: {
191
+ type: "integer",
192
+ description: `返回条数,1-20,默认 10。`
193
+ },
194
+ searchDb: {
195
+ type: "string",
196
+ description: "检索库:all(默认)、realtime(实时)、static(静态)。"
197
+ }
198
+ },
199
+ output: {
200
+ schema: {
201
+ type: "object",
202
+ additionalProperties: false,
203
+ properties: {
204
+ version: {
205
+ type: "integer",
206
+ required: true
207
+ },
208
+ query: {
209
+ type: "string",
210
+ required: true
211
+ },
212
+ count: {
213
+ type: "integer",
214
+ required: true
215
+ },
216
+ searchDb: {
217
+ type: "string",
218
+ required: true
219
+ },
220
+ items: {
221
+ type: "array",
222
+ required: true,
223
+ items: OTHER_SEARCH_ITEM_SCHEMA
224
+ },
225
+ emptyReason: { type: "string" }
226
+ }
227
+ },
228
+ render(_args, value) {
229
+ return [{
230
+ type: "text",
231
+ text: renderZhihuGlobalSearch(value)
232
+ }];
233
+ }
234
+ },
235
+ isConcurrencySafe() {
236
+ return true;
237
+ },
238
+ async execute(args, exec) {
239
+ const typed = args;
240
+ const searchDb = typed.searchDb === "realtime" || typed.searchDb === "static" ? typed.searchDb : "all";
241
+ return await metered(onExecuted, () => executeZhihuGlobalSearch(typed.query, typed.count ?? 10, searchDb, {
242
+ ...client,
243
+ signal: exec.signal
244
+ }), (result) => result.items.length);
245
+ }
246
+ });
247
+ }
248
+ function createZhihuHotListTool(options = {}) {
249
+ const { onExecuted, ...client } = options;
250
+ return defineTool({
251
+ name: ZHIHU_HOT_LIST_TOOL_NAME,
252
+ description: "拉取知乎热榜(GET /api/v1/content/hot_list)了解当前社区热点;结果仅作题材与热点参考,不构成 canon,也不直接写入项目文件。",
253
+ parameters: { limit: {
254
+ type: "integer",
255
+ description: `返回条数,1-30,默认 10。`
256
+ } },
257
+ output: {
258
+ schema: {
259
+ type: "object",
260
+ additionalProperties: false,
261
+ properties: {
262
+ version: {
263
+ type: "integer",
264
+ required: true
265
+ },
266
+ limit: {
267
+ type: "integer",
268
+ required: true
269
+ },
270
+ items: {
271
+ type: "array",
272
+ required: true,
273
+ items: {
274
+ type: "object",
275
+ additionalProperties: false,
276
+ properties: {
277
+ title: {
278
+ type: "string",
279
+ required: true
280
+ },
281
+ url: {
282
+ type: "string",
283
+ required: true
284
+ },
285
+ summary: {
286
+ type: "string",
287
+ required: true
288
+ }
289
+ }
290
+ }
291
+ }
292
+ }
293
+ },
294
+ render(_args, value) {
295
+ return [{
296
+ type: "text",
297
+ text: renderZhihuHotList(value)
298
+ }];
299
+ }
300
+ },
301
+ isConcurrencySafe() {
302
+ return true;
303
+ },
304
+ async execute(args, exec) {
305
+ const typed = args;
306
+ return await metered(onExecuted, () => executeZhihuHotList(typed.limit ?? 10, {
307
+ ...client,
308
+ signal: exec.signal
309
+ }), (result) => result.items.length);
310
+ }
311
+ });
312
+ }
313
+ function createZhihuAskTool(options = {}) {
314
+ const { onExecuted, ...client } = options;
315
+ return defineTool({
316
+ name: ZHIHU_ASK_TOOL_NAME,
317
+ description: "调用知乎直答(POST /v1/chat/completions,OpenAI 兼容)基于知乎社区内容生成综合回答;适合考据与背景调研。结果仅作参考,不构成 canon,也不直接写入项目文件。",
318
+ parameters: {
319
+ query: {
320
+ type: "string",
321
+ required: true,
322
+ description: "要问的问题。"
323
+ },
324
+ model: {
325
+ type: "string",
326
+ description: "zhida-thinking-1p5(默认,带思考过程)、zhida-fast-1p5(快,仅适合简单事实查询)或 zhida-agent(最慢最强,仅在明确要求时使用)。"
327
+ }
328
+ },
329
+ output: {
330
+ schema: {
331
+ type: "object",
332
+ additionalProperties: false,
333
+ properties: {
334
+ version: {
335
+ type: "integer",
336
+ required: true
337
+ },
338
+ query: {
339
+ type: "string",
340
+ required: true
341
+ },
342
+ model: {
343
+ type: "string",
344
+ required: true
345
+ },
346
+ content: {
347
+ type: "string",
348
+ required: true
349
+ },
350
+ reasoning: {
351
+ type: "string",
352
+ required: true
353
+ }
354
+ }
355
+ },
356
+ render(_args, value) {
357
+ return [{
358
+ type: "text",
359
+ text: renderZhihuAsk(value)
360
+ }];
361
+ }
362
+ },
363
+ isConcurrencySafe() {
364
+ return true;
365
+ },
366
+ async execute(args, exec) {
367
+ const typed = args;
368
+ const model = ZHIHU_ASK_MODELS.includes(typed.model ?? "") ? typed.model : ZHIHU_ASK_DEFAULT_MODEL;
369
+ return await metered(onExecuted, () => executeZhihuAsk(typed.query, model, {
370
+ ...client,
371
+ signal: exec.signal
372
+ }), (result) => result.content ? 1 : 0);
373
+ }
374
+ });
375
+ }
376
+ function createZhihuKnowledgeSearchTool(options = {}) {
377
+ const { onExecuted, ...client } = options;
378
+ return defineTool({
379
+ name: ZHIHU_KNOWLEDGE_SEARCH_TOOL_NAME,
380
+ description: "检索知乎知识库(POST /api/v1/knowledge/search,RAG 片段),默认只查公开库;用户在知乎网页端上传过个人资料后可加 personal/subscription 召回。结果仅作背景参考,不构成 canon,也不直接写入项目文件。",
381
+ parameters: {
382
+ query: {
383
+ type: "string",
384
+ required: true,
385
+ description: "检索词。"
386
+ },
387
+ limit: {
388
+ type: "integer",
389
+ description: `返回条数,1-10,默认 5。`
390
+ },
391
+ recallScopes: {
392
+ type: "array",
393
+ description: "召回范围:public(公开库,默认)、personal(个人库)、subscription(订阅库),可多选。",
394
+ items: {
395
+ type: "string",
396
+ enum: [...ZHIHU_RECALL_SCOPES]
397
+ }
398
+ }
399
+ },
400
+ output: {
401
+ schema: {
402
+ type: "object",
403
+ additionalProperties: false,
404
+ properties: {
405
+ version: {
406
+ type: "integer",
407
+ required: true
408
+ },
409
+ query: {
410
+ type: "string",
411
+ required: true
412
+ },
413
+ limit: {
414
+ type: "integer",
415
+ required: true
416
+ },
417
+ recallScopes: {
418
+ type: "array",
419
+ required: true,
420
+ items: { type: "string" }
421
+ },
422
+ items: {
423
+ type: "array",
424
+ required: true,
425
+ items: {
426
+ type: "object",
427
+ additionalProperties: false,
428
+ properties: {
429
+ docName: {
430
+ type: "string",
431
+ required: true
432
+ },
433
+ originUrl: {
434
+ type: "string",
435
+ required: true
436
+ },
437
+ snippets: {
438
+ type: "array",
439
+ required: true,
440
+ items: { type: "string" }
441
+ }
442
+ }
443
+ }
444
+ }
445
+ }
446
+ },
447
+ render(_args, value) {
448
+ return [{
449
+ type: "text",
450
+ text: renderZhihuKnowledgeSearch(value)
451
+ }];
452
+ }
453
+ },
454
+ isConcurrencySafe() {
455
+ return true;
456
+ },
457
+ async execute(args, exec) {
458
+ const typed = args;
459
+ return await metered(onExecuted, () => executeZhihuKnowledgeSearch(typed.query, typed.limit ?? 5, normalizeRecallScopes(typed.recallScopes), {
460
+ ...client,
461
+ signal: exec.signal
462
+ }), (result) => result.items.length);
463
+ }
464
+ });
465
+ }
466
+ //#endregion
467
+ //#region src/tools.ts
468
+ const name = "dsh-zhihu-tools";
469
+ const inject = ["zhihu", "tools"];
470
+ function apply(ctx) {
471
+ const host = ctx;
472
+ const options = host.zhihu.toolOptions;
473
+ for (const create of [
474
+ createZhihuSearchTool,
475
+ createZhihuGlobalSearchTool,
476
+ createZhihuHotListTool,
477
+ createZhihuAskTool,
478
+ createZhihuKnowledgeSearchTool
479
+ ]) {
480
+ const tool = create(options);
481
+ const execute = tool.execute;
482
+ host.tools.register({
483
+ ...tool,
484
+ execute: (args, exec) => host.zhihu.run((signal) => execute(args, {
485
+ ...exec,
486
+ signal
487
+ }), exec.signal)
488
+ });
489
+ }
490
+ }
491
+ //#endregion
492
+ export { apply, inject, name };
493
+
494
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","names":[],"sources":["../src/tool-definitions.ts","../src/tools.ts"],"sourcesContent":["import { defineTool } from '@deepseek-ai/dsh-tools'\nimport { reportExecuted, type ZhihuClientOptions } from './zhihu-client.ts'\nimport { ZHIHU_SEARCH_TOOL_NAME, ZHIHU_GLOBAL_SEARCH_TOOL_NAME, ZHIHU_HOT_LIST_TOOL_NAME, ZHIHU_ASK_TOOL_NAME, ZHIHU_KNOWLEDGE_SEARCH_TOOL_NAME } from './contracts.ts'\nimport { executeZhihuSearch, renderZhihuSearch, ZHIHU_SEARCH_DEFAULT_COUNT, ZHIHU_SEARCH_MAX_COUNT, type ZhihuSearchResult } from './search-api.ts'\nimport { ZHIHU_GLOBAL_SEARCH_VERSION, ZHIHU_GLOBAL_SEARCH_DEFAULT_COUNT, ZHIHU_GLOBAL_SEARCH_MAX_COUNT, ZHIHU_HOT_LIST_VERSION, ZHIHU_HOT_LIST_DEFAULT_LIMIT, ZHIHU_HOT_LIST_MAX_LIMIT, ZHIHU_ASK_VERSION, ZHIHU_ASK_TIMEOUT_MS, ZHIHU_KNOWLEDGE_SEARCH_VERSION, ZHIHU_KNOWLEDGE_SEARCH_DEFAULT_LIMIT, ZHIHU_KNOWLEDGE_SEARCH_MAX_LIMIT, ZHIHU_ASK_MODELS, ZhihuAskModel, ZHIHU_ASK_DEFAULT_MODEL, ZhihuToolOptions, ZhihuGlobalSearchDb, ZhihuGlobalSearchResult, executeZhihuGlobalSearch, renderZhihuGlobalSearch, ZhihuHotListItem, ZhihuHotListResult, executeZhihuHotList, renderZhihuHotList, ZhihuAskResult, executeZhihuAsk, renderZhihuAsk, ZhihuKnowledgeItem, ZhihuRecallScope, ZHIHU_RECALL_SCOPES, ZhihuKnowledgeSearchResult, normalizeRecallScopes, executeZhihuKnowledgeSearch, renderZhihuKnowledgeSearch } from './operations.ts'\nexport type CreateZhihuSearchToolOptions = ZhihuClientOptions & {\n /** Best-effort metering hook; invoked after every execution, failures included. */\n onExecuted?: (event: { ok: boolean; results: number }) => void\n}\n\nconst SEARCH_ITEM_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: { type: 'string', required: true },\n type: { type: 'string', required: true },\n url: { type: 'string', required: true },\n summary: { type: 'string', required: true },\n votes: { type: 'integer', required: true },\n comments: { type: 'integer', required: true },\n author: { type: 'string', required: true },\n authority: { type: 'string', required: true },\n editTime: { type: 'string', required: true },\n },\n} as const\n\nexport function createZhihuSearchTool(options: CreateZhihuSearchToolOptions = {}) {\n const { onExecuted, ...client } = options\n return defineTool({\n name: ZHIHU_SEARCH_TOOL_NAME,\n description: '调用知乎开放平台站内搜索(GET /api/v1/content/zhihu_search)拉取社区证据;结果仅作社区/读者反馈参考,不构成 canon,也不直接写入项目文件。',\n parameters: {\n query: { type: 'string', required: true, description: '搜索词,2-100 字符。' },\n count: { type: 'integer', description: `返回条数,1-${ZHIHU_SEARCH_MAX_COUNT},默认 ${ZHIHU_SEARCH_DEFAULT_COUNT}。` },\n },\n output: {\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n version: { type: 'integer', required: true },\n query: { type: 'string', required: true },\n count: { type: 'integer', required: true },\n items: {\n type: 'array',\n required: true,\n items: SEARCH_ITEM_SCHEMA,\n },\n emptyReason: { type: 'string' },\n },\n },\n render(_args, value) {\n return [{ type: 'text' as const, text: renderZhihuSearch(value as ZhihuSearchResult) }]\n },\n },\n isConcurrencySafe() { return true },\n async execute(args, exec) {\n const typed = args as { query: string; count?: number }\n try {\n const result = await executeZhihuSearch(typed.query, typed.count ?? ZHIHU_SEARCH_DEFAULT_COUNT, {\n ...client,\n signal: exec.signal,\n })\n reportExecuted(onExecuted, { ok: true, results: result.items.length })\n return result\n } catch (error) {\n reportExecuted(onExecuted, { ok: false, results: 0 })\n throw error\n }\n },\n })\n}\n\nconst OTHER_SEARCH_ITEM_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: { type: 'string', required: true },\n type: { type: 'string', required: true },\n url: { type: 'string', required: true },\n summary: { type: 'string', required: true },\n votes: { type: 'integer', required: true },\n comments: { type: 'integer', required: true },\n author: { type: 'string', required: true },\n authority: { type: 'string', required: true },\n editTime: { type: 'string', required: true },\n },\n} as const\n\n/** 包一层计量:成功报 results,失败报 ok:false 再原样抛出。 */\nasync function metered<T>(onExecuted: ZhihuToolOptions['onExecuted'], run: () => Promise<T>, results: (value: T) => number): Promise<T> {\n try {\n const value = await run()\n reportExecuted(onExecuted, { ok: true, results: results(value) })\n return value\n } catch (error) {\n reportExecuted(onExecuted, { ok: false, results: 0 })\n throw error\n }\n}\n\nexport function createZhihuGlobalSearchTool(options: ZhihuToolOptions = {}) {\n const { onExecuted, ...client } = options\n return defineTool({\n name: ZHIHU_GLOBAL_SEARCH_TOOL_NAME,\n description: '调用知乎开放平台全网搜索(GET /api/v1/content/global_search)检索站外公开网页资料;结果仅作参考,不构成 canon,也不直接写入项目文件。',\n parameters: {\n query: { type: 'string', required: true, description: '搜索词,2-100 字符。' },\n count: { type: 'integer', description: `返回条数,1-${ZHIHU_GLOBAL_SEARCH_MAX_COUNT},默认 ${ZHIHU_GLOBAL_SEARCH_DEFAULT_COUNT}。` },\n searchDb: { type: 'string', description: '检索库:all(默认)、realtime(实时)、static(静态)。' },\n },\n output: {\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n version: { type: 'integer', required: true },\n query: { type: 'string', required: true },\n count: { type: 'integer', required: true },\n searchDb: { type: 'string', required: true },\n items: { type: 'array', required: true, items: OTHER_SEARCH_ITEM_SCHEMA },\n emptyReason: { type: 'string' },\n },\n },\n render(_args, value) {\n return [{ type: 'text' as const, text: renderZhihuGlobalSearch(value as ZhihuGlobalSearchResult) }]\n },\n },\n isConcurrencySafe() { return true },\n async execute(args, exec) {\n const typed = args as { query: string; count?: number; searchDb?: string }\n const searchDb: ZhihuGlobalSearchDb = typed.searchDb === 'realtime' || typed.searchDb === 'static' ? typed.searchDb : 'all'\n return await metered(onExecuted, () => executeZhihuGlobalSearch(\n typed.query,\n typed.count ?? ZHIHU_GLOBAL_SEARCH_DEFAULT_COUNT,\n searchDb,\n { ...client, signal: exec.signal },\n ), (result) => result.items.length)\n },\n })\n}\n\nexport function createZhihuHotListTool(options: ZhihuToolOptions = {}) {\n const { onExecuted, ...client } = options\n return defineTool({\n name: ZHIHU_HOT_LIST_TOOL_NAME,\n description: '拉取知乎热榜(GET /api/v1/content/hot_list)了解当前社区热点;结果仅作题材与热点参考,不构成 canon,也不直接写入项目文件。',\n parameters: {\n limit: { type: 'integer', description: `返回条数,1-${ZHIHU_HOT_LIST_MAX_LIMIT},默认 ${ZHIHU_HOT_LIST_DEFAULT_LIMIT}。` },\n },\n output: {\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n version: { type: 'integer', required: true },\n limit: { type: 'integer', required: true },\n items: {\n type: 'array',\n required: true,\n items: {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: { type: 'string', required: true },\n url: { type: 'string', required: true },\n summary: { type: 'string', required: true },\n },\n },\n },\n },\n },\n render(_args, value) {\n return [{ type: 'text' as const, text: renderZhihuHotList(value as ZhihuHotListResult) }]\n },\n },\n isConcurrencySafe() { return true },\n async execute(args, exec) {\n const typed = args as { limit?: number }\n return await metered(onExecuted, () => executeZhihuHotList(\n typed.limit ?? ZHIHU_HOT_LIST_DEFAULT_LIMIT,\n { ...client, signal: exec.signal },\n ), (result) => result.items.length)\n },\n })\n}\n\nexport function createZhihuAskTool(options: ZhihuToolOptions = {}) {\n const { onExecuted, ...client } = options\n return defineTool({\n name: ZHIHU_ASK_TOOL_NAME,\n description: '调用知乎直答(POST /v1/chat/completions,OpenAI 兼容)基于知乎社区内容生成综合回答;适合考据与背景调研。结果仅作参考,不构成 canon,也不直接写入项目文件。',\n parameters: {\n query: { type: 'string', required: true, description: '要问的问题。' },\n model: { type: 'string', description: 'zhida-thinking-1p5(默认,带思考过程)、zhida-fast-1p5(快,仅适合简单事实查询)或 zhida-agent(最慢最强,仅在明确要求时使用)。' },\n },\n output: {\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n version: { type: 'integer', required: true },\n query: { type: 'string', required: true },\n model: { type: 'string', required: true },\n content: { type: 'string', required: true },\n reasoning: { type: 'string', required: true },\n },\n },\n render(_args, value) {\n return [{ type: 'text' as const, text: renderZhihuAsk(value as ZhihuAskResult) }]\n },\n },\n isConcurrencySafe() { return true },\n async execute(args, exec) {\n const typed = args as { query: string; model?: string }\n const model: ZhihuAskModel = (ZHIHU_ASK_MODELS as readonly string[]).includes(typed.model ?? '')\n ? (typed.model as ZhihuAskModel)\n : ZHIHU_ASK_DEFAULT_MODEL\n return await metered(onExecuted, () => executeZhihuAsk(\n typed.query,\n model,\n { ...client, signal: exec.signal },\n ), (result) => (result.content ? 1 : 0))\n },\n })\n}\n\nexport function createZhihuKnowledgeSearchTool(options: ZhihuToolOptions = {}) {\n const { onExecuted, ...client } = options\n return defineTool({\n name: ZHIHU_KNOWLEDGE_SEARCH_TOOL_NAME,\n description: '检索知乎知识库(POST /api/v1/knowledge/search,RAG 片段),默认只查公开库;用户在知乎网页端上传过个人资料后可加 personal/subscription 召回。结果仅作背景参考,不构成 canon,也不直接写入项目文件。',\n parameters: {\n query: { type: 'string', required: true, description: '检索词。' },\n limit: { type: 'integer', description: `返回条数,1-${ZHIHU_KNOWLEDGE_SEARCH_MAX_LIMIT},默认 ${ZHIHU_KNOWLEDGE_SEARCH_DEFAULT_LIMIT}。` },\n recallScopes: {\n type: 'array',\n description: '召回范围:public(公开库,默认)、personal(个人库)、subscription(订阅库),可多选。',\n items: { type: 'string', enum: [...ZHIHU_RECALL_SCOPES] },\n },\n },\n output: {\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n version: { type: 'integer', required: true },\n query: { type: 'string', required: true },\n limit: { type: 'integer', required: true },\n recallScopes: { type: 'array', required: true, items: { type: 'string' } },\n items: {\n type: 'array',\n required: true,\n items: {\n type: 'object',\n additionalProperties: false,\n properties: {\n docName: { type: 'string', required: true },\n originUrl: { type: 'string', required: true },\n snippets: { type: 'array', required: true, items: { type: 'string' } },\n },\n },\n },\n },\n },\n render(_args, value) {\n return [{ type: 'text' as const, text: renderZhihuKnowledgeSearch(value as ZhihuKnowledgeSearchResult) }]\n },\n },\n isConcurrencySafe() { return true },\n async execute(args, exec) {\n const typed = args as { query: string; limit?: number; recallScopes?: unknown }\n return await metered(onExecuted, () => executeZhihuKnowledgeSearch(\n typed.query,\n typed.limit ?? ZHIHU_KNOWLEDGE_SEARCH_DEFAULT_LIMIT,\n normalizeRecallScopes(typed.recallScopes),\n { ...client, signal: exec.signal },\n ), (result) => result.items.length)\n },\n })\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport type { ZhihuService } from './index.ts'\nimport { createZhihuSearchTool, createZhihuGlobalSearchTool, createZhihuHotListTool, createZhihuAskTool, createZhihuKnowledgeSearchTool } from './tool-definitions.ts'\nexport const name = 'dsh-zhihu-tools'\nexport const inject = ['zhihu', 'tools'] as const\nexport function apply(ctx: Context): void {\n const host = ctx as Context & { zhihu: ZhihuService; tools: { register: (tool: unknown) => unknown } }\n const options = host.zhihu.toolOptions\n for (const create of [createZhihuSearchTool, createZhihuGlobalSearchTool, createZhihuHotListTool, createZhihuAskTool, createZhihuKnowledgeSearchTool]) {\n const tool = create(options)\n const execute = tool.execute as unknown as (args: unknown, exec: { signal: AbortSignal }) => Promise<unknown>\n host.tools.register({ ...tool, execute: (args: unknown, exec: { signal: AbortSignal }) =>\n host.zhihu.run(signal => execute(args, { ...exec, signal }), exec.signal) })\n }\n}\n"],"mappings":"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"}
package/lib/usage.d.ts ADDED
@@ -0,0 +1,54 @@
1
+ import { z } from "zod";
2
+ //#region src/usage.d.ts
3
+ declare class UsageInputError extends Error {
4
+ readonly code: string;
5
+ constructor(message: string, code: string);
6
+ }
7
+ declare function dayKey(now?: Date): string;
8
+ declare function resolveDays(value: unknown): number;
9
+ declare function recentDayKeys(count: number, now?: Date): string[];
10
+ /** Daily counters for zhihu tool executions (search/global search/hot list/ask/knowledge). `results` accumulates returned items on successful calls. */
11
+ type ZhihuDailyUsage = {
12
+ date: string;
13
+ calls: number;
14
+ failures: number;
15
+ results: number;
16
+ };
17
+ /** Metering payload emitted by novel tool hosts after each zhihu_search execution. */
18
+ type ZhihuSearchEvent = {
19
+ ok: boolean;
20
+ results?: number;
21
+ };
22
+ declare const dailyRowSchema: z.ZodObject<{
23
+ date: z.ZodString;
24
+ calls: z.ZodNumber;
25
+ failures: z.ZodNumber;
26
+ results: z.ZodNumber;
27
+ }, z.core.$strip>;
28
+ declare const zhihuUsageDomainSpec: {
29
+ name: string;
30
+ version: number;
31
+ tables: {
32
+ daily: import("@deepseek-ai/dsh-storage-domain").DomainTableSpec<string, {
33
+ date: string;
34
+ calls: number;
35
+ failures: number;
36
+ results: number;
37
+ }>;
38
+ };
39
+ };
40
+ /** Fold one zhihu_search execution into a daily row. */
41
+ declare function mergeZhihuUsage(day: ZhihuDailyUsage | undefined, event: ZhihuSearchEvent): ZhihuDailyUsage;
42
+ interface ZhihuUsageTableLike {
43
+ get(key: string): z.infer<typeof dailyRowSchema> | undefined;
44
+ put(key: string, value: z.infer<typeof dailyRowSchema>): Promise<void>;
45
+ }
46
+ interface ZhihuUsageRecorder {
47
+ record(event: ZhihuSearchEvent): Promise<void>;
48
+ read(days: number): Promise<ZhihuDailyUsage[]>;
49
+ }
50
+ /** Read-modify-write adapter over a storage-domain table, mirroring `createUsageRecorder`. */
51
+ declare function createZhihuUsageRecorder(table: ZhihuUsageTableLike): ZhihuUsageRecorder;
52
+ //#endregion
53
+ export { UsageInputError, ZhihuDailyUsage, ZhihuSearchEvent, ZhihuUsageRecorder, ZhihuUsageTableLike, createZhihuUsageRecorder, dayKey, mergeZhihuUsage, recentDayKeys, resolveDays, zhihuUsageDomainSpec };
54
+ //# sourceMappingURL=usage.d.ts.map
package/lib/usage.js ADDED
@@ -0,0 +1,96 @@
1
+ import { defineDomain, domainTable } from "@deepseek-ai/dsh-storage-domain";
2
+ import { z } from "zod";
3
+ //#region src/usage.ts
4
+ var UsageInputError = class extends Error {
5
+ code;
6
+ constructor(message, code) {
7
+ super(message);
8
+ this.code = code;
9
+ this.name = "UsageInputError";
10
+ }
11
+ };
12
+ function dayKey(now = /* @__PURE__ */ new Date()) {
13
+ return [
14
+ now.getFullYear(),
15
+ String(now.getMonth() + 1).padStart(2, "0"),
16
+ String(now.getDate()).padStart(2, "0")
17
+ ].join("-");
18
+ }
19
+ function resolveDays(value) {
20
+ const days = typeof value === "number" && Number.isFinite(value) ? Math.trunc(value) : 30;
21
+ if (days < 1) throw new UsageInputError("days must be at least 1", "DAYS_OUT_OF_RANGE");
22
+ if (days > 90) throw new UsageInputError("days must be no more than 90", "DAYS_OUT_OF_RANGE");
23
+ return days;
24
+ }
25
+ function recentDayKeys(count, now = /* @__PURE__ */ new Date()) {
26
+ const days = [];
27
+ const base = new Date(now.getFullYear(), now.getMonth(), now.getDate());
28
+ for (let offset = count - 1; offset >= 0; offset -= 1) {
29
+ const cursor = new Date(base);
30
+ cursor.setDate(base.getDate() - offset);
31
+ days.push(dayKey(cursor));
32
+ }
33
+ return days;
34
+ }
35
+ const dailyRowSchema = z.object({
36
+ date: z.string(),
37
+ calls: z.number().int().nonnegative(),
38
+ failures: z.number().int().nonnegative(),
39
+ results: z.number().int().nonnegative()
40
+ });
41
+ const zhihuUsageDomainSpec = defineDomain({
42
+ name: "dsh_editor_zhihu_usage",
43
+ version: 1,
44
+ tables: { daily: domainTable(dailyRowSchema) }
45
+ });
46
+ function int(value) {
47
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : 0;
48
+ }
49
+ function emptyDay(date) {
50
+ return {
51
+ date,
52
+ calls: 0,
53
+ failures: 0,
54
+ results: 0
55
+ };
56
+ }
57
+ /** Fold one zhihu_search execution into a daily row. */
58
+ function mergeZhihuUsage(day, event) {
59
+ const next = day ? { ...day } : emptyDay("");
60
+ next.calls += 1;
61
+ if (event.ok) next.results += int(event.results);
62
+ else next.failures += 1;
63
+ return next;
64
+ }
65
+ /** Read-modify-write adapter over a storage-domain table, mirroring `createUsageRecorder`. */
66
+ function createZhihuUsageRecorder(table) {
67
+ return {
68
+ async record(event) {
69
+ const date = dayKey();
70
+ let stored;
71
+ try {
72
+ stored = table.get(date);
73
+ } catch {
74
+ stored = void 0;
75
+ }
76
+ const merged = mergeZhihuUsage(stored, event);
77
+ await table.put(date, {
78
+ ...merged,
79
+ date
80
+ });
81
+ },
82
+ async read(days) {
83
+ const keys = recentDayKeys(resolveDays(days));
84
+ const byDate = /* @__PURE__ */ new Map();
85
+ for (const key of keys) {
86
+ const row = table.get(key);
87
+ if (row) byDate.set(row.date, row);
88
+ }
89
+ return keys.map((date) => byDate.get(date) ?? emptyDay(date));
90
+ }
91
+ };
92
+ }
93
+ //#endregion
94
+ export { UsageInputError, createZhihuUsageRecorder, dayKey, mergeZhihuUsage, recentDayKeys, resolveDays, zhihuUsageDomainSpec };
95
+
96
+ //# sourceMappingURL=usage.js.map