@yfwu2020/dsh-selection-explain 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/README.md +306 -0
- package/cordis.patch.yml +31 -0
- package/lib/client.js +4904 -0
- package/lib/index.js +2292 -0
- package/lib/index.js.map +1 -0
- package/lib/types/index.d.ts +251 -0
- package/package.json +50 -0
- package/scripts/build.sh +102 -0
- package/scripts/dump-prompt.mjs +143 -0
- package/scripts/test-client.mjs +2486 -0
- package/scripts/test-filters.mjs +229 -0
- package/scripts/test-guard.mjs +48 -0
- package/scripts/test-transcript.mjs +78 -0
- package/skills/web-design/SKILL.md +74 -0
- package/src/client/index.js +4904 -0
- package/src/index.ts +2560 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 打印一次划词解读实际发给模型的提示词(system + user)与各段尺寸。
|
|
3
|
+
*
|
|
4
|
+
* 用法:
|
|
5
|
+
* node scripts/dump-prompt.mjs <sessionId> "<选中文字>" ["<上下文片段>"]
|
|
6
|
+
* node scripts/dump-prompt.mjs --no-session "<选中文字>" # 不带会话背景
|
|
7
|
+
* node scripts/dump-prompt.mjs --all # 一次打出五种模式的系统提示词
|
|
8
|
+
* node scripts/dump-prompt.mjs --no-session "x" --stage=detail --kind=code --question="为什么?"
|
|
9
|
+
* SEL_ORIGIN=http://127.0.0.1:3080 node scripts/dump-prompt.mjs ...
|
|
10
|
+
*
|
|
11
|
+
* 原理:host 支持诊断开关 debug:true —— SSE 的 start 事件里会带上 system / user 原文。
|
|
12
|
+
*/
|
|
13
|
+
const args = process.argv.slice(2)
|
|
14
|
+
const ORIGIN = process.env.SEL_ORIGIN || 'http://127.0.0.1:3080'
|
|
15
|
+
|
|
16
|
+
const noSession = args[0] === '--no-session'
|
|
17
|
+
const sessionId = noSession ? '' : args[0] || ''
|
|
18
|
+
const text = (noSession ? args[1] : args[1]) || 'the migration ran long'
|
|
19
|
+
const context =
|
|
20
|
+
(noSession ? args[2] : args[2]) || 'PM:这周能发布吗?\nDev:【the migration ran long】,得顺延到周三。\nPM:那就周三,先冻结功能。'
|
|
21
|
+
|
|
22
|
+
const FULL = args.includes('--full') // --full:等完整回答,附带打印 token 用量与缓存命中
|
|
23
|
+
const STAGE = (args.find((a) => a.startsWith('--stage=')) || '').slice('--stage='.length)
|
|
24
|
+
const KIND = (args.find((a) => a.startsWith('--kind=')) || '').slice('--kind='.length)
|
|
25
|
+
const QUESTION = (args.find((a) => a.startsWith('--question=')) || '').slice('--question='.length)
|
|
26
|
+
const WEB = args.includes('--web') // --web:按"网页模式"发(host 会注入设计规范)
|
|
27
|
+
|
|
28
|
+
/** 发一次请求,只取 start 事件里的 system / user(debug:true 才有)。 */
|
|
29
|
+
async function fetchPrompt(payload) {
|
|
30
|
+
const response = await fetch(`${ORIGIN}/selection-explain/api/analyze`, {
|
|
31
|
+
method: 'POST',
|
|
32
|
+
headers: { 'content-type': 'application/json' },
|
|
33
|
+
body: JSON.stringify({ label: '对话消息', ...(sessionId ? { sessionId } : {}), debug: true, ...payload }),
|
|
34
|
+
})
|
|
35
|
+
const reader = response.body.getReader()
|
|
36
|
+
const decoder = new TextDecoder()
|
|
37
|
+
let buffer = ''
|
|
38
|
+
for (;;) {
|
|
39
|
+
const { done, value } = await reader.read()
|
|
40
|
+
if (done) break
|
|
41
|
+
buffer += decoder.decode(value, { stream: true })
|
|
42
|
+
let index
|
|
43
|
+
while ((index = buffer.indexOf('\n\n')) >= 0) {
|
|
44
|
+
const block = buffer.slice(0, index)
|
|
45
|
+
buffer = buffer.slice(index + 2)
|
|
46
|
+
const line = block.trim()
|
|
47
|
+
if (!line.startsWith('data:')) continue
|
|
48
|
+
let message
|
|
49
|
+
try {
|
|
50
|
+
message = JSON.parse(line.slice(5).trim())
|
|
51
|
+
} catch {
|
|
52
|
+
continue
|
|
53
|
+
}
|
|
54
|
+
if (message.type === 'start') {
|
|
55
|
+
// 关掉流:我们只要提示词,不要它生成完(省时省钱)
|
|
56
|
+
try {
|
|
57
|
+
await reader.cancel()
|
|
58
|
+
} catch (error) {
|
|
59
|
+
/* noop */
|
|
60
|
+
}
|
|
61
|
+
return message
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return null
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// --all:把四种模式(翻译/解读、代码注释、详解、追问)的系统提示词一次性打出来
|
|
69
|
+
if (args.includes('--all')) {
|
|
70
|
+
const modes = [
|
|
71
|
+
{ title: '① 翻译(选中含英文)', payload: { text: 'the migration ran long', context, stage: 'translation' } },
|
|
72
|
+
{ title: '② 解读(选中纯中文)', payload: { text: '先对齐一下,我们再闭环', context: '评审会上他说"先对齐一下,我们再闭环"。', stage: 'translation' } },
|
|
73
|
+
{ title: '③ 注释(选中代码)', payload: { text: 'const a = 1\nfunction f() { return a }', context: '代码片段。', kind: 'code', stage: 'translation' } },
|
|
74
|
+
{ title: '④ 详解(点展开详解)', payload: { text: 'the migration ran long', context, stage: 'detail' } },
|
|
75
|
+
{ title: '⑤ 追问(小窗对话)', payload: { text: 'the migration ran long', context, question: '为什么会这样?', history: [{ role: 'assistant', text: '## 翻译\n迁移跑久了。' }] } },
|
|
76
|
+
{ title: '⑥ 追问 + 网页模式(注入 skills/web-design/SKILL.md)', payload: { text: 'the migration ran long', context, question: '为什么?给我一页对比', webAnswer: true, history: [{ role: 'assistant', text: '## 翻译\n迁移跑久了。' }] } },
|
|
77
|
+
]
|
|
78
|
+
for (const mode of modes) {
|
|
79
|
+
const start = await fetchPrompt(mode.payload)
|
|
80
|
+
console.log(`\n${'='.repeat(78)}\n### ${mode.title}\n${'='.repeat(78)}`)
|
|
81
|
+
if (!start) {
|
|
82
|
+
console.log('(没拿到 start 事件)')
|
|
83
|
+
continue
|
|
84
|
+
}
|
|
85
|
+
console.log(`[system ${start.debug?.sizes?.system ?? '?'} 字 / user ${start.debug?.sizes?.user ?? '?'} 字] tools=${JSON.stringify(start.tools || [])} effort=${start.effort}`)
|
|
86
|
+
console.log('\n----- SYSTEM -----\n' + (start.debug?.system || '(未返回)'))
|
|
87
|
+
console.log('\n----- USER(第一段的开头)-----\n' + String(start.debug?.user || '').slice(0, 600))
|
|
88
|
+
}
|
|
89
|
+
process.exit(0)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const response = await fetch(`${ORIGIN}/selection-explain/api/analyze`, {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
headers: { 'content-type': 'application/json' },
|
|
95
|
+
body: JSON.stringify({
|
|
96
|
+
text,
|
|
97
|
+
context,
|
|
98
|
+
label: '对话消息',
|
|
99
|
+
...(sessionId ? { sessionId } : {}),
|
|
100
|
+
...(STAGE ? { stage: STAGE } : {}),
|
|
101
|
+
...(KIND ? { kind: KIND } : {}),
|
|
102
|
+
...(QUESTION ? { question: QUESTION, history: [{ role: 'assistant', text: '(上一轮)' }] } : {}),
|
|
103
|
+
...(WEB ? { webAnswer: true } : {}),
|
|
104
|
+
debug: true,
|
|
105
|
+
}),
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
const reader = response.body.getReader()
|
|
109
|
+
const decoder = new TextDecoder()
|
|
110
|
+
let buffer = ''
|
|
111
|
+
let printed = false
|
|
112
|
+
while (!printed) {
|
|
113
|
+
const { done, value } = await reader.read()
|
|
114
|
+
if (done) break
|
|
115
|
+
buffer += decoder.decode(value, { stream: true })
|
|
116
|
+
let index
|
|
117
|
+
while ((index = buffer.indexOf('\n\n')) >= 0) {
|
|
118
|
+
const block = buffer.slice(0, index)
|
|
119
|
+
buffer = buffer.slice(index + 2)
|
|
120
|
+
const line = block.trim()
|
|
121
|
+
if (!line.startsWith('data:')) continue
|
|
122
|
+
let message
|
|
123
|
+
try {
|
|
124
|
+
message = JSON.parse(line.slice(5).trim())
|
|
125
|
+
} catch {
|
|
126
|
+
continue
|
|
127
|
+
}
|
|
128
|
+
if (message.type !== 'start') continue
|
|
129
|
+
const sizes = message.debug?.sizes || {}
|
|
130
|
+
console.log('=== 尺寸(字符)===')
|
|
131
|
+
console.log(`system ${sizes.system} | user ${sizes.user} | 局部片段 ${message.context.chars} | 会话背景 ${message.context.session ? message.context.session.chars : 0}${message.context.session ? '(标记命中=' + message.context.session.marked + ')' : ''}`)
|
|
132
|
+
console.log('\n=== SYSTEM ===\n' + (message.debug?.system || '(未返回)'))
|
|
133
|
+
console.log('\n=== USER(实际注入的上下文)===\n' + (message.debug?.user || '(未返回)'))
|
|
134
|
+
printed = true
|
|
135
|
+
if (!FULL) break
|
|
136
|
+
}
|
|
137
|
+
if (FULL) {
|
|
138
|
+
const line = buffer.trim()
|
|
139
|
+
void line
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
reader.cancel().catch(() => {})
|
|
143
|
+
if (!printed) console.error('未取到 start 事件(服务是否在跑?地址:' + ORIGIN + ')')
|