@templmf/temp-solf-lmf 0.0.54 → 0.0.55
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/guanwang/README.md +95 -0
- package/guanwang/docs/changelog.md +145 -0
- package/guanwang/docs/doc-maintenance.md +229 -0
- package/guanwang/docs/product.md +181 -0
- package/guanwang/docs/test-cases.md +395 -0
- package/guanwang/docs/usage.md +291 -0
- package/guanwang/env.example +27 -0
- package/guanwang/index.html +13 -0
- package/guanwang/package-lock.json +3825 -0
- package/guanwang/package.json +32 -0
- package/guanwang/public/favicon.svg +4 -0
- package/guanwang/public/react-runtime/babel.min.js +4 -0
- package/guanwang/public/react-runtime/react-dom.min.js +267 -0
- package/guanwang/public/react-runtime/react.min.js +31 -0
- package/guanwang/public/vue-repl-assets/compiler-sfc.esm-browser.js +50795 -0
- package/guanwang/public/vue-repl-assets/runtime-dom.esm-browser.js +12758 -0
- package/guanwang/public/vue-repl-assets/server-renderer.esm-browser.js +8600 -0
- package/guanwang/public/vue-repl-assets/vue.esm-browser.js +18672 -0
- package/guanwang/src/App.vue +61 -0
- package/guanwang/src/chat-sdk/core/components/ChatBox.vue +305 -0
- package/guanwang/src/chat-sdk/core/components/ChatSidebar.vue +84 -0
- package/guanwang/src/chat-sdk/core/components/InputBar.vue +354 -0
- package/guanwang/src/chat-sdk/core/components/MessageBubble.vue +703 -0
- package/guanwang/src/chat-sdk/core/useTheme.js +31 -0
- package/guanwang/src/chat-sdk/features/artifact/ArtifactCard.vue +172 -0
- package/guanwang/src/chat-sdk/features/artifact/ArtifactPanel.vue +963 -0
- package/guanwang/src/chat-sdk/features/artifact/index.js +13 -0
- package/guanwang/src/chat-sdk/features/artifact/useArtifactStore.js +275 -0
- package/guanwang/src/chat-sdk/features/codepreview/CodePreview.vue +523 -0
- package/guanwang/src/chat-sdk/features/codepreview/index.js +7 -0
- package/guanwang/src/chat-sdk/features/markdown/index.js +13 -0
- package/guanwang/src/chat-sdk/features/markdown/useMarkdown.js +724 -0
- package/guanwang/src/chat-sdk/features/mermaid/MermaidZoom.vue +254 -0
- package/guanwang/src/chat-sdk/features/upload/FileAttachment.vue +142 -0
- package/guanwang/src/chat-sdk/features/upload/index.js +17 -0
- package/guanwang/src/chat-sdk/features/upload/useFileHandler.js +336 -0
- package/guanwang/src/chat-sdk/headless/api/adapters/openai.js +76 -0
- package/guanwang/src/chat-sdk/headless/api/chatApi.js +126 -0
- package/guanwang/src/chat-sdk/headless/buildSystemPrompt.js +351 -0
- package/guanwang/src/chat-sdk/headless/index.js +15 -0
- package/guanwang/src/chat-sdk/headless/useChat.js +77 -0
- package/guanwang/src/chat-sdk/headless/useChatDB.js +147 -0
- package/guanwang/src/chat-sdk/headless/useChatStore.js +529 -0
- package/guanwang/src/chat-sdk/index.js +79 -0
- package/guanwang/src/chat-sdk/modes/architect.js +27 -0
- package/guanwang/src/chat-sdk/modes/ask.js +26 -0
- package/guanwang/src/chat-sdk/modes/code.js +25 -0
- package/guanwang/src/chat-sdk/modes/index.js +36 -0
- package/guanwang/src/chat-sdk/modes/requirements.js +175 -0
- package/guanwang/src/chat-sdk/settings/SettingsPanel.vue +170 -0
- package/guanwang/src/chat-sdk/settings/index.js +9 -0
- package/guanwang/src/chat-sdk/settings/useSettings.js +122 -0
- package/guanwang/src/chat-sdk/tools/defaults.js +89 -0
- package/guanwang/src/chat-sdk/tools/index.js +16 -0
- package/guanwang/src/chat-sdk/tools/parser.js +116 -0
- package/guanwang/src/components/CustomCursor.vue +69 -0
- package/guanwang/src/components/Footer.vue +24 -0
- package/guanwang/src/components/LoginModal.vue +109 -0
- package/guanwang/src/components/Navbar.vue +193 -0
- package/guanwang/src/components/ThemeToggle.vue +25 -0
- package/guanwang/src/composables/useArtifactStore.js +253 -0
- package/guanwang/src/composables/useAuth.js +88 -0
- package/guanwang/src/composables/useChatDB.js +147 -0
- package/guanwang/src/composables/useCountUp.js +24 -0
- package/guanwang/src/composables/useFileHandler.js +345 -0
- package/guanwang/src/composables/useTheme.js +31 -0
- package/guanwang/src/config/api.js +71 -0
- package/guanwang/src/main.js +23 -0
- package/guanwang/src/router/index.js +23 -0
- package/guanwang/src/services/authApi.js +27 -0
- package/guanwang/src/services/chatApi.js +66 -0
- package/guanwang/src/styles/global.css +478 -0
- package/guanwang/src/tracker/analyze.js +73 -0
- package/guanwang/src/tracker/config.js +82 -0
- package/guanwang/src/tracker/index.js +18 -0
- package/guanwang/src/tracker/service.js +102 -0
- package/guanwang/src/tracker/useChatTracker.js +179 -0
- package/guanwang/src/tracker/useTracker.js +45 -0
- package/guanwang/src/views/ChatView.vue +65 -0
- package/guanwang/src/views/HomeView.vue +156 -0
- package/guanwang/src/views/MarketView.vue +143 -0
- package/guanwang/src/views/PracticesView.vue +190 -0
- package/guanwang/src/views/SkillsView.vue +129 -0
- package/guanwang/temp +19 -0
- package/guanwang/vite.config.js +6 -0
- package/package.json +1 -1
- package/guanwang copy.zip +0 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* headless/buildSystemPrompt.js
|
|
3
|
+
*
|
|
4
|
+
* 动态组装 system prompt,按 Roo 架构分区块构建,针对 Web 环境和 Qwen3 优化:
|
|
5
|
+
* - 删除所有本地 IO 相关工具和规则
|
|
6
|
+
* - 保留并优化工具示例(弱模型需要大量示例)
|
|
7
|
+
* - 强约束语气(MUST / NEVER / ALWAYS),弱模型更易遵循
|
|
8
|
+
* - 关键规则前置,避免弱模型忽略 prompt 后半段
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// ─────────────────────────────────────────────────────────────────
|
|
12
|
+
// 区块:工具使用规范
|
|
13
|
+
// ─────────────────────────────────────────────────────────────────
|
|
14
|
+
const TOOL_USE_SECTION = `====
|
|
15
|
+
|
|
16
|
+
工具使用
|
|
17
|
+
|
|
18
|
+
你拥有以下工具,在用户授权下执行。每次对话你只能使用一个工具,并在收到工具执行结果后再继续。
|
|
19
|
+
|
|
20
|
+
# 工具调用格式
|
|
21
|
+
|
|
22
|
+
工具调用使用 XML 标签格式,工具名作为标签名,参数嵌套在内部:
|
|
23
|
+
|
|
24
|
+
<工具名>
|
|
25
|
+
<参数名>参数值</参数名>
|
|
26
|
+
</工具名>
|
|
27
|
+
|
|
28
|
+
MUST 严格遵守此格式,标签不得缺失、多余或嵌套错误,否则工具无法被识别和执行。
|
|
29
|
+
|
|
30
|
+
# 工具列表
|
|
31
|
+
|
|
32
|
+
## ask_followup_question
|
|
33
|
+
描述:当你需要用户提供更多信息才能继续时,使用此工具向用户提问。MUST 同时提供 2-4 个建议答案供用户快速选择,降低用户输入成本。
|
|
34
|
+
|
|
35
|
+
参数:
|
|
36
|
+
- question:(必填)向用户提出的问题,MUST 清晰具体,一次只问一个问题
|
|
37
|
+
- follow_up:(必填)建议答案列表,每个答案用 <suggest> 标签包裹,可附加 mode 属性指定选择后切换的模式
|
|
38
|
+
|
|
39
|
+
用法:
|
|
40
|
+
<ask_followup_question>
|
|
41
|
+
<question>你的问题</question>
|
|
42
|
+
<follow_up>
|
|
43
|
+
<suggest>建议答案1</suggest>
|
|
44
|
+
<suggest>建议答案2</suggest>
|
|
45
|
+
<suggest mode="code">切换到Code模式并开始实现</suggest>
|
|
46
|
+
</follow_up>
|
|
47
|
+
</ask_followup_question>
|
|
48
|
+
|
|
49
|
+
示例:询问项目技术栈
|
|
50
|
+
<ask_followup_question>
|
|
51
|
+
<question>这个项目使用的是哪个前端框架?</question>
|
|
52
|
+
<follow_up>
|
|
53
|
+
<suggest>Vue 3 + TypeScript</suggest>
|
|
54
|
+
<suggest>React + TypeScript</suggest>
|
|
55
|
+
<suggest>Vue 2(旧项目维护)</suggest>
|
|
56
|
+
<suggest>其他框架,我来补充说明</suggest>
|
|
57
|
+
</follow_up>
|
|
58
|
+
</ask_followup_question>
|
|
59
|
+
|
|
60
|
+
示例:询问后端服务提供方
|
|
61
|
+
<ask_followup_question>
|
|
62
|
+
<question>用户认证由哪个系统负责?</question>
|
|
63
|
+
<follow_up>
|
|
64
|
+
<suggest>本系统自己实现</suggest>
|
|
65
|
+
<suggest>依赖公司统一 SSO 系统</suggest>
|
|
66
|
+
<suggest>第三方服务(如微信、钉钉)</suggest>
|
|
67
|
+
</follow_up>
|
|
68
|
+
</ask_followup_question>
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## attempt_completion
|
|
73
|
+
描述:当你已完成用户交代的任务,使用此工具呈现最终结果。用户可能会给出反馈,你可以据此改进后再次使用此工具。
|
|
74
|
+
|
|
75
|
+
IMPORTANT:在使用此工具前,MUST 在 <thinking> 标签内确认所有步骤已完成、所有工具调用已成功。未确认直接调用此工具会导致任务不完整。
|
|
76
|
+
|
|
77
|
+
参数:
|
|
78
|
+
- result:(必填)任务完成的最终结果描述,MUST 是明确的终态陈述,不得以问题或"是否需要继续"结尾
|
|
79
|
+
|
|
80
|
+
用法:
|
|
81
|
+
<attempt_completion>
|
|
82
|
+
<result>
|
|
83
|
+
你的最终结果描述
|
|
84
|
+
</result>
|
|
85
|
+
</attempt_completion>
|
|
86
|
+
|
|
87
|
+
示例:代码生成完成
|
|
88
|
+
<attempt_completion>
|
|
89
|
+
<result>
|
|
90
|
+
登录组件已生成,包含手机号+验证码登录、密码登录两种方式,支持表单验证和错误提示,已通过 render_artifact 输出为 Login.vue 文件。
|
|
91
|
+
</result>
|
|
92
|
+
</attempt_completion>
|
|
93
|
+
|
|
94
|
+
示例:需求文档完成
|
|
95
|
+
<attempt_completion>
|
|
96
|
+
<result>
|
|
97
|
+
用户注册功能的需求文档已完成,包含 3 个用户故事、12 个功能拆解项(含接口层和交互层),系统调用关系已明确标注 SSO 依赖,已通过 render_artifact 输出为 Markdown 文件。
|
|
98
|
+
</result>
|
|
99
|
+
</attempt_completion>
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## switch_mode
|
|
104
|
+
描述:请求切换到另一个模式。模式切换需要用户确认。仅在当前任务明确需要其他模式的能力时使用。
|
|
105
|
+
|
|
106
|
+
参数:
|
|
107
|
+
- mode_slug:(必填)目标模式标识,可选值:code / ask / architect / requirements
|
|
108
|
+
- reason:(选填)切换原因,帮助用户理解为什么需要切换
|
|
109
|
+
|
|
110
|
+
用法:
|
|
111
|
+
<switch_mode>
|
|
112
|
+
<mode_slug>目标模式</mode_slug>
|
|
113
|
+
<reason>切换原因</reason>
|
|
114
|
+
</switch_mode>
|
|
115
|
+
|
|
116
|
+
示例:需求确认完毕,建议切换到 Architect 模式
|
|
117
|
+
<switch_mode>
|
|
118
|
+
<mode_slug>architect</mode_slug>
|
|
119
|
+
<reason>需求已梳理清晰,建议切换到 Architect 模式进行系统设计</reason>
|
|
120
|
+
</switch_mode>
|
|
121
|
+
|
|
122
|
+
示例:用户要求生成代码,当前在 Ask 模式
|
|
123
|
+
<switch_mode>
|
|
124
|
+
<mode_slug>code</mode_slug>
|
|
125
|
+
<reason>需要生成完整代码文件,Code 模式更适合此任务</reason>
|
|
126
|
+
</switch_mode>
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## render_artifact
|
|
131
|
+
描述:输出一个完整的代码或文档文件,文件将在右侧面板中展示,用户可预览和下载。
|
|
132
|
+
|
|
133
|
+
何时使用:
|
|
134
|
+
- 代码超过 20 行且构成完整可运行文件时 MUST 使用此工具
|
|
135
|
+
- 输出技术文档、需求文档、架构说明时 MUST 使用此工具
|
|
136
|
+
- 用户明确要求"生成文件"、"保存代码"时 MUST 使用此工具
|
|
137
|
+
|
|
138
|
+
何时不使用:
|
|
139
|
+
- 仅作为示例说明的短代码片段(≤20 行)用普通代码块即可
|
|
140
|
+
- 回答问题时引用的代码示例不需要 artifact
|
|
141
|
+
|
|
142
|
+
参数:
|
|
143
|
+
- file_name:(必填)文件名,包含扩展名,如 Login.vue、api.ts、README.md
|
|
144
|
+
- lang:(必填)代码语言,如 vue、typescript、javascript、markdown、python
|
|
145
|
+
- content:(必填)文件完整内容,MUST 是完整内容,严禁省略或截断
|
|
146
|
+
|
|
147
|
+
用法:
|
|
148
|
+
<render_artifact>
|
|
149
|
+
<file_name>文件名.扩展名</file_name>
|
|
150
|
+
<lang>语言</lang>
|
|
151
|
+
<content>
|
|
152
|
+
完整文件内容
|
|
153
|
+
</content>
|
|
154
|
+
</render_artifact>
|
|
155
|
+
|
|
156
|
+
示例:输出 Vue 组件
|
|
157
|
+
<render_artifact>
|
|
158
|
+
<file_name>LoginForm.vue</file_name>
|
|
159
|
+
<lang>vue</lang>
|
|
160
|
+
<content>
|
|
161
|
+
<template>
|
|
162
|
+
<div class="login-form">
|
|
163
|
+
<!-- 完整组件内容 -->
|
|
164
|
+
</div>
|
|
165
|
+
</template>
|
|
166
|
+
</content>
|
|
167
|
+
</render_artifact>
|
|
168
|
+
|
|
169
|
+
示例:输出需求文档
|
|
170
|
+
<render_artifact>
|
|
171
|
+
<file_name>用户注册需求.md</file_name>
|
|
172
|
+
<lang>markdown</lang>
|
|
173
|
+
<content>
|
|
174
|
+
# 用户注册功能需求文档
|
|
175
|
+
...
|
|
176
|
+
</content>
|
|
177
|
+
</render_artifact>
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## request_context
|
|
182
|
+
描述:当你需要用户提供现有代码、配置文件或其他上下文内容才能继续时,使用此工具请求。用户可通过粘贴代码或上传文件提供内容。
|
|
183
|
+
|
|
184
|
+
何时使用:
|
|
185
|
+
- 需要查看用户现有代码才能给出修改建议时
|
|
186
|
+
- 需要了解项目配置才能给出准确方案时
|
|
187
|
+
- NEVER 在未获得必要上下文时直接凭空假设代码内容
|
|
188
|
+
|
|
189
|
+
参数:
|
|
190
|
+
- hint:(必填)说明需要什么内容,以及为什么需要,帮助用户准确提供
|
|
191
|
+
|
|
192
|
+
用法:
|
|
193
|
+
<request_context>
|
|
194
|
+
<hint>提示内容</hint>
|
|
195
|
+
</request_context>
|
|
196
|
+
|
|
197
|
+
示例:需要查看现有组件代码
|
|
198
|
+
<request_context>
|
|
199
|
+
<hint>请提供现有的 UserProfile.vue 组件代码,我需要了解当前的数据结构和样式约定,才能给出准确的修改方案</hint>
|
|
200
|
+
</request_context>
|
|
201
|
+
|
|
202
|
+
示例:需要了解 API 接口
|
|
203
|
+
<request_context>
|
|
204
|
+
<hint>请提供后端登录接口的文档或示例响应,包括请求参数格式和响应数据结构</hint>
|
|
205
|
+
</request_context>`
|
|
206
|
+
|
|
207
|
+
// ─────────────────────────────────────────────────────────────────
|
|
208
|
+
// 区块:工具使用指南
|
|
209
|
+
// ─────────────────────────────────────────────────────────────────
|
|
210
|
+
const TOOL_GUIDELINES = `# 工具使用指南
|
|
211
|
+
|
|
212
|
+
1. 在 <thinking> 标签内先分析:已有哪些信息,还缺什么,用哪个工具最合适
|
|
213
|
+
2. 一次只使用一个工具,等待执行结果后再决定下一步
|
|
214
|
+
3. NEVER 假设工具执行成功,MUST 等待用户返回结果后再继续
|
|
215
|
+
4. 工具调用 MUST 使用规定的 XML 格式,格式错误会导致工具无法执行
|
|
216
|
+
5. 信息不足时优先使用 ask_followup_question 工具收集信息,而不是凭空假设`
|
|
217
|
+
|
|
218
|
+
// ─────────────────────────────────────────────────────────────────
|
|
219
|
+
// 区块:Web 环境能力边界
|
|
220
|
+
// ─────────────────────────────────────────────────────────────────
|
|
221
|
+
const CAPABILITIES_SECTION = `====
|
|
222
|
+
|
|
223
|
+
当前环境能力
|
|
224
|
+
|
|
225
|
+
你运行在 Web 浏览器环境中,以下是你的能力边界:
|
|
226
|
+
|
|
227
|
+
你能做的:
|
|
228
|
+
- 生成完整代码文件,通过 render_artifact 工具输出到右侧面板供用户预览和下载
|
|
229
|
+
- 渲染 Markdown、代码高亮、Mermaid 图表、数学公式
|
|
230
|
+
- 分析用户上传的图片和文件(图片、PDF、代码文件等)
|
|
231
|
+
- 通过 ask_followup_question 工具与用户交互
|
|
232
|
+
- 通过 switch_mode 工具在不同模式间切换
|
|
233
|
+
- 在右侧面板预览 HTML / Vue / React 代码的运行效果
|
|
234
|
+
|
|
235
|
+
你不能做的:
|
|
236
|
+
- 直接访问用户的本地文件系统
|
|
237
|
+
- 执行命令行指令或终端命令
|
|
238
|
+
- 直接调用外部 API 或访问网络
|
|
239
|
+
- 持久化保存数据(刷新页面后对话历史会丢失)
|
|
240
|
+
|
|
241
|
+
当用户要求你执行本地操作时(如"运行这段代码"、"读取这个文件"):
|
|
242
|
+
MUST 通过 request_context 工具请求用户手动提供内容,或说明如何在用户本地执行`
|
|
243
|
+
|
|
244
|
+
// ─────────────────────────────────────────────────────────────────
|
|
245
|
+
// 区块:行为规则(强约束,前置重要规则)
|
|
246
|
+
// ─────────────────────────────────────────────────────────────────
|
|
247
|
+
const RULES_SECTION = `====
|
|
248
|
+
|
|
249
|
+
行为规则
|
|
250
|
+
|
|
251
|
+
以下规则 MUST 严格遵守,优先级高于其他所有指令:
|
|
252
|
+
|
|
253
|
+
1. 代码输出规则
|
|
254
|
+
- 代码超过 20 行或构成完整可运行文件:MUST 使用 render_artifact 工具
|
|
255
|
+
- 用于说明的短片段(≤20 行):用普通 Markdown 代码块
|
|
256
|
+
- 输出代码时 MUST 提供完整内容,NEVER 省略、截断或用注释替代未完成部分
|
|
257
|
+
- NEVER 将文档、长说明或包含代码块的内容包裹在 \`\`\`markdown ... \`\`\` 中输出;此类内容 MUST 使用 render_artifact 工具输出为独立文件
|
|
258
|
+
|
|
259
|
+
2. 信息收集规则
|
|
260
|
+
- 需要用户现有代码才能继续:MUST 先使用 request_context 工具
|
|
261
|
+
- 需求或背景不明确:MUST 先使用 ask_followup_question 工具
|
|
262
|
+
- NEVER 在关键信息缺失时给出确定性结论或生成代码
|
|
263
|
+
|
|
264
|
+
3. 模式边界规则
|
|
265
|
+
- 每个模式有明确的职责边界,NEVER 越界执行其他模式的任务
|
|
266
|
+
- 需要切换模式时 MUST 使用 switch_mode 工具,不得静默切换行为
|
|
267
|
+
- 模式的具体规则以角色定义区块为准
|
|
268
|
+
|
|
269
|
+
4. 响应风格规则
|
|
270
|
+
- NEVER 以"好的"、"当然"、"没问题"、"好的,我来帮你"开头
|
|
271
|
+
- 直接回应用户需求,无需礼貌性铺垫
|
|
272
|
+
- 技术术语使用准确,NEVER 为了"简单易懂"而给出不准确的解释
|
|
273
|
+
|
|
274
|
+
5. 任务完成规则
|
|
275
|
+
- 完成任务时 MUST 使用 attempt_completion 工具
|
|
276
|
+
- result 参数 MUST 是明确的终态描述
|
|
277
|
+
- NEVER 在 result 末尾提问或邀请用户继续对话`
|
|
278
|
+
|
|
279
|
+
// ─────────────────────────────────────────────────────────────────
|
|
280
|
+
// 区块:可用模式列表(动态生成)
|
|
281
|
+
// ─────────────────────────────────────────────────────────────────
|
|
282
|
+
function buildModesSection(modes) {
|
|
283
|
+
if (!modes || modes.length === 0) return ''
|
|
284
|
+
|
|
285
|
+
const modeLines = modes.map(m =>
|
|
286
|
+
`- "${m.name}" 模式(${m.id}):${m.description}`
|
|
287
|
+
).join('\n')
|
|
288
|
+
|
|
289
|
+
return `====
|
|
290
|
+
|
|
291
|
+
可用模式
|
|
292
|
+
|
|
293
|
+
当前可用的模式列表:
|
|
294
|
+
${modeLines}
|
|
295
|
+
|
|
296
|
+
若用户要求切换模式,使用 switch_mode 工具进行切换。`
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ─────────────────────────────────────────────────────────────────
|
|
300
|
+
// 区块:用户自定义指令(动态注入)
|
|
301
|
+
// ─────────────────────────────────────────────────────────────────
|
|
302
|
+
function buildCustomInstructions(customInstructions) {
|
|
303
|
+
if (!customInstructions?.trim()) return ''
|
|
304
|
+
|
|
305
|
+
return `====
|
|
306
|
+
|
|
307
|
+
用户自定义指令
|
|
308
|
+
|
|
309
|
+
以下是用户提供的额外指令,在不违背工具使用规则的前提下尽量遵守:
|
|
310
|
+
|
|
311
|
+
${customInstructions.trim()}`
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// ─────────────────────────────────────────────────────────────────
|
|
315
|
+
// 主函数:组装完整 system prompt
|
|
316
|
+
// ─────────────────────────────────────────────────────────────────
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* 动态组装 system prompt
|
|
320
|
+
* @param {object} opts
|
|
321
|
+
* @param {object} opts.mode 当前模式配置对象(来自 modes/*.js)
|
|
322
|
+
* @param {object[]} opts.allModes 所有可用模式列表
|
|
323
|
+
* @param {string} [opts.customInstructions] 用户自定义指令
|
|
324
|
+
* @returns {string}
|
|
325
|
+
*/
|
|
326
|
+
export function buildSystemPrompt({ mode, allModes = [], customInstructions = '' }) {
|
|
327
|
+
const sections = [
|
|
328
|
+
// 1. 角色定义(最前面,弱模型优先读到)
|
|
329
|
+
mode.rolePrompt,
|
|
330
|
+
|
|
331
|
+
// 2. 工具使用规范(含完整示例)
|
|
332
|
+
TOOL_USE_SECTION,
|
|
333
|
+
|
|
334
|
+
// 3. 工具使用指南
|
|
335
|
+
TOOL_GUIDELINES,
|
|
336
|
+
|
|
337
|
+
// 4. Web 环境能力边界
|
|
338
|
+
CAPABILITIES_SECTION,
|
|
339
|
+
|
|
340
|
+
// 5. 行为规则(强约束)
|
|
341
|
+
RULES_SECTION,
|
|
342
|
+
|
|
343
|
+
// 6. 可用模式列表(动态)
|
|
344
|
+
buildModesSection(allModes),
|
|
345
|
+
|
|
346
|
+
// 7. 用户自定义指令(动态,最后注入)
|
|
347
|
+
buildCustomInstructions(customInstructions),
|
|
348
|
+
]
|
|
349
|
+
|
|
350
|
+
return sections.filter(Boolean).join('\n\n')
|
|
351
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* chat-sdk/headless/index.js
|
|
3
|
+
*
|
|
4
|
+
* Headless 模式对外入口,仅暴露纯逻辑,零 UI 依赖。
|
|
5
|
+
*
|
|
6
|
+
* 使用方式:
|
|
7
|
+
* import { useChat } from 'your-chat-sdk/headless'
|
|
8
|
+
*
|
|
9
|
+
* const { messages, send, switchMode, onToolCall } = useChat({
|
|
10
|
+
* models: [...],
|
|
11
|
+
* modes: ['code', 'ask'],
|
|
12
|
+
* features: { artifact: true, markdown: true },
|
|
13
|
+
* })
|
|
14
|
+
*/
|
|
15
|
+
export { useChat } from './useChat.js'
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* headless/useChat.js
|
|
3
|
+
*
|
|
4
|
+
* SDK 的纯逻辑层入口,零 UI 依赖。
|
|
5
|
+
* 同时被以下两个入口使用:
|
|
6
|
+
* - chat-sdk/index.js (createAIChat) — 完整视图模式
|
|
7
|
+
* - chat-sdk/headless/index.js (useChat) — 仅能力模式
|
|
8
|
+
*
|
|
9
|
+
* 暴露的接口:
|
|
10
|
+
*
|
|
11
|
+
* 状态(只读 ref/computed)
|
|
12
|
+
* messages 消息列表(UI 格式,非 API 格式)
|
|
13
|
+
* isLoading 是否请求中
|
|
14
|
+
* currentMode 当前模式 id
|
|
15
|
+
* selectedModel 当前选中模型配置对象
|
|
16
|
+
* availableModels 全部可用模型列表
|
|
17
|
+
* activeArtifacts 当前会话的 artifact 列表
|
|
18
|
+
* sessions 所有会话
|
|
19
|
+
* activeSessionId 当前会话 id
|
|
20
|
+
*
|
|
21
|
+
* 方法
|
|
22
|
+
* send({ text, attachments }) 发送消息
|
|
23
|
+
* abort() 中止当前流式请求
|
|
24
|
+
* switchMode(modeId) 切换模式
|
|
25
|
+
* selectModel(modelId) 切换模型
|
|
26
|
+
* clearSession() 清空当前会话消息
|
|
27
|
+
* newSession() 新建会话
|
|
28
|
+
* switchSession(sessionId) 切换会话
|
|
29
|
+
*
|
|
30
|
+
* tool-use 回调(可选覆盖,不写用内置默认行为)
|
|
31
|
+
* onToolCall(toolName, handler)
|
|
32
|
+
* onToolCall(handler) 监听所有工具
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { ref, computed, shallowRef } from 'vue'
|
|
36
|
+
import { useChatStore } from './useChatStore.js'
|
|
37
|
+
import { loadModes } from '../modes/index.js'
|
|
38
|
+
|
|
39
|
+
export function useChat(config) {
|
|
40
|
+
// config 结构:
|
|
41
|
+
// {
|
|
42
|
+
// models: [{ id, name, baseURL, apiKey, vision }],
|
|
43
|
+
// defaultModel: 'model-id',
|
|
44
|
+
// autoVision: true,
|
|
45
|
+
// modes: ['code', 'ask', 'architect', 'requirements'],
|
|
46
|
+
// defaultMode: 'code',
|
|
47
|
+
// features: { artifact, markdown, upload, codepreview },
|
|
48
|
+
// customInstructions: '',
|
|
49
|
+
// }
|
|
50
|
+
|
|
51
|
+
// TODO: 第二阶段实现,当前为骨架占位
|
|
52
|
+
const store = useChatStore(config)
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
// 状态
|
|
56
|
+
messages: store.messages,
|
|
57
|
+
isLoading: store.isLoading,
|
|
58
|
+
currentMode: store.currentMode,
|
|
59
|
+
selectedModel: store.selectedModel,
|
|
60
|
+
availableModels: store.availableModels,
|
|
61
|
+
activeArtifacts: store.activeArtifacts,
|
|
62
|
+
sessions: store.sessions,
|
|
63
|
+
activeSessionId: store.activeId,
|
|
64
|
+
|
|
65
|
+
// 方法
|
|
66
|
+
send: store.sendMessage,
|
|
67
|
+
abort: store.abortStream,
|
|
68
|
+
switchMode: store.switchMode,
|
|
69
|
+
selectModel: store.selectModel,
|
|
70
|
+
clearSession: store.clearSession,
|
|
71
|
+
newSession: store.newSession,
|
|
72
|
+
switchSession: store.switchSession,
|
|
73
|
+
|
|
74
|
+
// tool-use 回调注册
|
|
75
|
+
onToolCall: store.onToolCall,
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useChatDB.js
|
|
3
|
+
* IndexedDB 持久化层,负责聊天 sessions 的存取
|
|
4
|
+
*
|
|
5
|
+
* 存储策略:
|
|
6
|
+
* - 仅存 displayText(msg.content)和 attachments 元信息(不存 base64 图片数据)
|
|
7
|
+
* - apiContent 含 image_url base64,体积大且无需持久化,不写入 DB
|
|
8
|
+
* - 每次写入整个 sessions 数组(简化实现,sessions 数量通常不超过几十条)
|
|
9
|
+
*
|
|
10
|
+
* DB 结构:
|
|
11
|
+
* db: citic_chat version: 1
|
|
12
|
+
* store: sessions keyPath: id
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const DB_NAME = 'citic_chat'
|
|
16
|
+
const DB_VERSION = 1
|
|
17
|
+
const STORE_NAME = 'sessions'
|
|
18
|
+
|
|
19
|
+
let _db = null
|
|
20
|
+
|
|
21
|
+
// ── 打开 / 初始化 DB ──────────────────────────────────────────────
|
|
22
|
+
function openDB() {
|
|
23
|
+
if (_db) return Promise.resolve(_db)
|
|
24
|
+
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
const req = indexedDB.open(DB_NAME, DB_VERSION)
|
|
27
|
+
|
|
28
|
+
req.onupgradeneeded = (e) => {
|
|
29
|
+
const db = e.target.result
|
|
30
|
+
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
31
|
+
db.createObjectStore(STORE_NAME, { keyPath: 'id' })
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
req.onsuccess = (e) => {
|
|
36
|
+
_db = e.target.result
|
|
37
|
+
resolve(_db)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
req.onerror = () => reject(req.error)
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ── 序列化:去除不需要持久化的大体积字段 ─────────────────────────
|
|
45
|
+
function serializeSession(session) {
|
|
46
|
+
return {
|
|
47
|
+
id: session.id,
|
|
48
|
+
title: session.title,
|
|
49
|
+
messages: session.messages.map(msg => {
|
|
50
|
+
const base = {
|
|
51
|
+
id: msg.id,
|
|
52
|
+
role: msg.role,
|
|
53
|
+
content: msg.content, // displayText,安全
|
|
54
|
+
error: msg.error || false,
|
|
55
|
+
streaming: false, // 持久化时流式已结束
|
|
56
|
+
}
|
|
57
|
+
// attachments 只保留元信息(无 base64 preview)
|
|
58
|
+
if (msg.attachments && msg.attachments.length > 0) {
|
|
59
|
+
base.attachments = msg.attachments.map(att => ({
|
|
60
|
+
id: att.id,
|
|
61
|
+
fileName: att.fileName,
|
|
62
|
+
type: att.type,
|
|
63
|
+
ext: att.ext,
|
|
64
|
+
// preview(图片 dataURL)体积大,不持久化
|
|
65
|
+
}))
|
|
66
|
+
}
|
|
67
|
+
// apiContent 完全不持久化(含 base64 / 文件原文)
|
|
68
|
+
return base
|
|
69
|
+
}),
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ── 保存所有 sessions ─────────────────────────────────────────────
|
|
74
|
+
export async function saveSessions(sessions) {
|
|
75
|
+
try {
|
|
76
|
+
const db = await openDB()
|
|
77
|
+
const tx = db.transaction(STORE_NAME, 'readwrite')
|
|
78
|
+
const store = tx.objectStore(STORE_NAME)
|
|
79
|
+
|
|
80
|
+
// 先清空旧数据,再写入当前全量(保持顺序)
|
|
81
|
+
store.clear()
|
|
82
|
+
for (const session of sessions) {
|
|
83
|
+
store.put(serializeSession(session))
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return new Promise((resolve, reject) => {
|
|
87
|
+
tx.oncomplete = () => resolve()
|
|
88
|
+
tx.onerror = () => reject(tx.error)
|
|
89
|
+
})
|
|
90
|
+
} catch (err) {
|
|
91
|
+
// DB 操作失败静默处理,不影响主流程
|
|
92
|
+
console.warn('[ChatDB] saveSessions failed:', err)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ── 读取所有 sessions ─────────────────────────────────────────────
|
|
97
|
+
export async function loadSessions() {
|
|
98
|
+
try {
|
|
99
|
+
const db = await openDB()
|
|
100
|
+
const tx = db.transaction(STORE_NAME, 'readonly')
|
|
101
|
+
const store = tx.objectStore(STORE_NAME)
|
|
102
|
+
|
|
103
|
+
return new Promise((resolve, reject) => {
|
|
104
|
+
const req = store.getAll()
|
|
105
|
+
req.onsuccess = () => {
|
|
106
|
+
const rows = req.result || []
|
|
107
|
+
// IndexedDB getAll 不保证顺序,按 id(时间戳)降序排列(新会话在前)
|
|
108
|
+
rows.sort((a, b) => b.id - a.id)
|
|
109
|
+
resolve(rows)
|
|
110
|
+
}
|
|
111
|
+
req.onerror = () => reject(req.error)
|
|
112
|
+
})
|
|
113
|
+
} catch (err) {
|
|
114
|
+
console.warn('[ChatDB] loadSessions failed:', err)
|
|
115
|
+
return []
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── 删除单条 session ──────────────────────────────────────────────
|
|
120
|
+
export async function deleteSession(id) {
|
|
121
|
+
try {
|
|
122
|
+
const db = await openDB()
|
|
123
|
+
const tx = db.transaction(STORE_NAME, 'readwrite')
|
|
124
|
+
tx.objectStore(STORE_NAME).delete(id)
|
|
125
|
+
return new Promise((resolve, reject) => {
|
|
126
|
+
tx.oncomplete = () => resolve()
|
|
127
|
+
tx.onerror = () => reject(tx.error)
|
|
128
|
+
})
|
|
129
|
+
} catch (err) {
|
|
130
|
+
console.warn('[ChatDB] deleteSession failed:', err)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ── 清空所有 sessions ─────────────────────────────────────────────
|
|
135
|
+
export async function clearAllSessions() {
|
|
136
|
+
try {
|
|
137
|
+
const db = await openDB()
|
|
138
|
+
const tx = db.transaction(STORE_NAME, 'readwrite')
|
|
139
|
+
tx.objectStore(STORE_NAME).clear()
|
|
140
|
+
return new Promise((resolve, reject) => {
|
|
141
|
+
tx.oncomplete = () => resolve()
|
|
142
|
+
tx.onerror = () => reject(tx.error)
|
|
143
|
+
})
|
|
144
|
+
} catch (err) {
|
|
145
|
+
console.warn('[ChatDB] clearAllSessions failed:', err)
|
|
146
|
+
}
|
|
147
|
+
}
|