sg-paisou 0.2.5 → 0.2.7

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 CHANGED
@@ -1,2 +1,712 @@
1
- # SG-paisou-web
1
+ # SG Paisou Web - AI 聊天 SDK
2
2
 
3
+ 一个功能完整的 Vue 3 AI 聊天组件 SDK,专为教育场景设计,支持数学公式渲染、流式消息、语音交互、思考动画等高级功能。
4
+
5
+ ## 📋 目录
6
+
7
+ - [项目简介](#项目简介)
8
+ - [功能特性](#功能特性)
9
+ - [快速开始](#快速开始)
10
+ - [安装](#安装)
11
+ - [基础使用](#基础使用)
12
+ - [配置说明](#配置说明)
13
+ - [API 文档](#api-文档)
14
+ - [组件说明](#组件说明)
15
+ - [高级功能](#高级功能)
16
+ - [开发指南](#开发指南)
17
+ - [常见问题](#常见问题)
18
+
19
+ ## 🎯 项目简介
20
+
21
+ SG Paisou Web 是一个基于 Vue 3 的 AI 聊天 SDK,主要用于教育场景中的智能问答和题目讲解。该 SDK 提供了完整的聊天界面、流式消息处理、数学公式渲染、语音交互、思考动画等功能,可以快速集成到现有项目中。
22
+
23
+ ### 核心特点
24
+
25
+ - 🎨 **高度可定制**:支持自定义样式、图标、文本等
26
+ - 📱 **响应式设计**:完美适配移动端和桌面端
27
+ - 🧮 **数学公式支持**:基于 MathJax 的 LaTeX 公式渲染
28
+ - 🎤 **语音交互**:支持语音输入和 TTS 语音播放
29
+ - 💬 **流式消息**:实时流式消息接收和显示
30
+ - 🎭 **思考动画**:Thinkie 思考动画,提升用户体验
31
+ - 📊 **数据埋点**:完整的数据统计和埋点支持
32
+
33
+ ## ✨ 功能特性
34
+
35
+ ### 1. 聊天功能
36
+
37
+ - ✅ 实时流式消息接收和显示
38
+ - ✅ 消息类型:AI 消息、用户消息
39
+ - ✅ 消息状态:发送中、成功、失败
40
+ - ✅ 网络错误处理和重试机制
41
+ - ✅ 消息长按反馈功能
42
+ - ✅ 消息滚动到底部
43
+
44
+ ### 2. 数学公式渲染
45
+
46
+ - ✅ 支持 LaTeX 数学公式(`$...$` 和 `$$...$$`)
47
+ - ✅ 支持行内公式和块级公式
48
+ - ✅ 自动处理货币符号(如 `$45`)
49
+ - ✅ 支持特殊字符转义(如 `\%`、`\$`)
50
+ - ✅ 基于 MathJax 的高质量渲染
51
+
52
+ ### 3. 思考动画(Thinkie is thinking...)
53
+
54
+ - ✅ 7 组随机思考文案
55
+ - ✅ 打字机效果逐行显示
56
+ - ✅ 涟漪动画效果
57
+ - ✅ 与流式消息同步处理
58
+ - ✅ 平滑的收起动画
59
+
60
+ ### 4. 语音功能
61
+
62
+ - ✅ 语音输入(语音转文字)
63
+ - ✅ TTS 语音播放
64
+ - ✅ 音量控制(静音/取消静音)
65
+ - ✅ 录音倒计时
66
+ - ✅ 语音处理状态提示
67
+
68
+ ### 5. 反馈功能
69
+
70
+ - ✅ 消息反馈(点赞/踩)
71
+ - ✅ 整体反馈
72
+ - ✅ 反馈弹窗
73
+ - ✅ 反馈状态持久化
74
+
75
+ ### 6. 历史记录
76
+
77
+ - ✅ 历史消息加载
78
+ - ✅ 历史会话恢复
79
+ - ✅ 7 天限制处理
80
+ - ✅ 历史记录状态管理
81
+
82
+ ### 7. 特殊功能
83
+
84
+ - ✅ 特殊题目处理
85
+ - ✅ 追问功能
86
+ - ✅ 继续讲解功能
87
+ - ✅ 再次讲解功能
88
+ - ✅ 快捷回复
89
+ - ✅ 内容安全检测
90
+
91
+ ## 🚀 快速开始
92
+
93
+ ### 安装
94
+
95
+ ```bash
96
+ # 使用 npm
97
+ npm install @genie/sg-paisou-sdk
98
+
99
+ # 使用 pnpm
100
+ pnpm add @genie/sg-paisou-sdk
101
+
102
+ # 使用 yarn
103
+ yarn add @genie/sg-paisou-sdk
104
+ ```
105
+
106
+ ### 基础使用
107
+
108
+ ```vue
109
+ <template>
110
+ <AIChatComponent
111
+ :questionInfo="questionInfo"
112
+ :config="chatConfig"
113
+ @messageSend="handleMessageSend"
114
+ @feedback="handleFeedback"
115
+ />
116
+ </template>
117
+
118
+ <script setup>
119
+ import { AIChatComponent } from '@genie/sg-paisou-sdk'
120
+ import '@genie/sg-paisou-sdk/style.css'
121
+
122
+ const questionInfo = {
123
+ questionId: '123',
124
+ sessionId: 'session_123',
125
+ content: '问题内容',
126
+ imagePath: 'https://example.com/image.jpg'
127
+ }
128
+
129
+ const chatConfig = {
130
+ aiInfo: {
131
+ name: 'Thinkie',
132
+ title: 'AI Tutor'
133
+ }
134
+ }
135
+
136
+ const handleMessageSend = (eventType, eventData) => {
137
+ console.log('Message sent:', eventType, eventData)
138
+ }
139
+
140
+ const handleFeedback = (feedbackId) => {
141
+ console.log('Feedback:', feedbackId)
142
+ }
143
+ </script>
144
+ ```
145
+
146
+ ## ⚙️ 配置说明
147
+
148
+ ### 完整配置示例
149
+
150
+ ```typescript
151
+ import { IAIChatConfig } from '@genie/sg-paisou-sdk'
152
+
153
+ const config: IAIChatConfig = {
154
+ // AI 头像配置
155
+ avatar: {
156
+ src: 'https://example.com/avatar.png',
157
+ alt: 'AI Avatar',
158
+ width: '40px',
159
+ height: '40px',
160
+ borderRadius: '50%'
161
+ },
162
+
163
+ // 用户头像配置
164
+ userAvatar: {
165
+ src: 'https://example.com/user-avatar.png',
166
+ alt: 'User Avatar',
167
+ width: '40px',
168
+ height: '40px',
169
+ borderRadius: '50%'
170
+ },
171
+
172
+ // AI 信息配置
173
+ aiInfo: {
174
+ name: 'Thinkie',
175
+ title: 'AI Tutor',
176
+ nameStyle: {
177
+ color: '#222222',
178
+ fontSize: '16px',
179
+ fontWeight: '600'
180
+ },
181
+ titleStyle: {
182
+ color: '#666666',
183
+ fontSize: '14px'
184
+ }
185
+ },
186
+
187
+ // 静音按钮配置
188
+ muteButton: {
189
+ soundOnIcon: 'https://example.com/sound-on.png',
190
+ soundOffIcon: 'https://example.com/sound-off.png',
191
+ width: '24px',
192
+ height: '24px',
193
+ onClick: (isSoundOn) => {
194
+ console.log('Sound toggled:', isSoundOn)
195
+ }
196
+ },
197
+
198
+ // 输入框配置
199
+ input: {
200
+ placeholder: 'Type your message...',
201
+ sendIcon: 'https://example.com/send-icon.png',
202
+ sendIconWidth: '24px',
203
+ sendIconHeight: '24px',
204
+ borderRadius: '20px',
205
+ padding: '8px 16px',
206
+ onSend: (message) => {
207
+ console.log('Message sent:', message)
208
+ }
209
+ },
210
+
211
+ // 样式配置
212
+ styles: {
213
+ container: {
214
+ background: '#ffffff',
215
+ borderRadius: '12px'
216
+ },
217
+ header: {
218
+ padding: '16px',
219
+ borderBottom: '1px solid #e5e5e5'
220
+ },
221
+ chatContent: {
222
+ height: '500px',
223
+ padding: '20px'
224
+ },
225
+ messages: {
226
+ aiMessage: {
227
+ background: '#f5f5f5',
228
+ color: '#222222',
229
+ borderRadius: '12px',
230
+ padding: '12px 16px'
231
+ },
232
+ userMessage: {
233
+ background: '#007AFF',
234
+ color: '#ffffff',
235
+ borderRadius: '12px',
236
+ padding: '12px 16px'
237
+ },
238
+ messageGap: '16px'
239
+ },
240
+ inputContainer: {
241
+ padding: '16px',
242
+ background: '#ffffff'
243
+ }
244
+ },
245
+
246
+ // 反馈按钮配置
247
+ feedback: {
248
+ buttons: [
249
+ {
250
+ id: 'helpful',
251
+ text: 'Helpful',
252
+ icon: 'https://example.com/helpful-icon.png'
253
+ },
254
+ {
255
+ id: 'not-helpful',
256
+ text: 'Not Helpful',
257
+ icon: 'https://example.com/not-helpful-icon.png'
258
+ }
259
+ ],
260
+ containerStyle: {
261
+ padding: '12px 0',
262
+ gap: '12px'
263
+ },
264
+ show: true
265
+ },
266
+
267
+ // 按钮配置
268
+ buttons: {
269
+ startExplaining: {
270
+ text: 'Start Explaining',
271
+ className: 'btn-primary',
272
+ style: {
273
+ background: 'linear-gradient(91.95deg, #FFCB00 0%, #FFAB03 100%)',
274
+ color: '#FFFFFF'
275
+ }
276
+ },
277
+ continue: {
278
+ text: 'Continue',
279
+ className: 'btn-continue',
280
+ style: {
281
+ background: 'transparent',
282
+ color: '#007AFF'
283
+ }
284
+ },
285
+ snapAnother: {
286
+ text: 'Snap Another',
287
+ icon: 'https://example.com/camera-icon.png',
288
+ className: 'btn-snap',
289
+ style: {
290
+ background: '#f5f5f5',
291
+ color: '#222222'
292
+ }
293
+ }
294
+ },
295
+
296
+ // 文本配置(国际化)
297
+ texts: {
298
+ notMyQuestion: "This isn't my question",
299
+ startExplaining: 'Start Explaining',
300
+ continue: 'Continue',
301
+ wasThisHelpful: 'Was this helpful?',
302
+ snapAnother: 'Snap Another',
303
+ explainAgain: 'Explain Again'
304
+ },
305
+
306
+ // 自定义 CSS 类名
307
+ customClasses: {
308
+ container: 'my-chat-container',
309
+ header: 'my-chat-header',
310
+ chatContent: 'my-chat-content',
311
+ inputContainer: 'my-input-container'
312
+ }
313
+ }
314
+ ```
315
+
316
+ ## 📚 API 文档
317
+
318
+ ### Props
319
+
320
+ #### `questionInfo` (Object, 可选)
321
+
322
+ 问题信息对象,包含以下字段:
323
+
324
+ ```typescript
325
+ interface QuestionInfo {
326
+ questionId?: string // 问题ID
327
+ question_id?: string // 问题ID(兼容字段)
328
+ sessionId?: string // 会话ID
329
+ session_id?: string // 会话ID(兼容字段)
330
+ content?: string // 问题内容
331
+ imagePath?: string // 问题图片路径
332
+ image_path?: string // 问题图片路径(兼容字段)
333
+ question_type?: number // 问题类型:0-不可解,1-特殊题,2-普通题
334
+ pkgName?: string // 包名:'SGpaisou' 或其他
335
+ historyType?: boolean // 是否为历史记录
336
+ isOverSevenDays?: number // 是否超过7天:0-否,1-是
337
+ favorite?: boolean // 是否收藏
338
+ vote?: 'upvote' | 'downvote' | null // 反馈状态
339
+ avatar?: string // 用户头像
340
+ // ... 其他字段
341
+ }
342
+ ```
343
+
344
+ #### `config` (IAIChatConfig, 可选)
345
+
346
+ 聊天组件配置对象,详见 [配置说明](#配置说明)。
347
+
348
+ ### Events
349
+
350
+ #### `messageSend`
351
+
352
+ 消息发送事件,包含以下事件类型:
353
+
354
+ ```typescript
355
+ // 事件类型
356
+ type MessageEventType =
357
+ | 'voice' // 语音事件:'start' | 'stop'
358
+ | 'dataCollection' // 数据埋点
359
+ | 'sessionUpdated' // 会话更新
360
+ | 'questionData' // 问题数据
361
+ | 'historyLoading' // 历史加载
362
+ | 'shouldShowRecording' // 是否显示录音
363
+ | 'explainAgain' // 再次讲解
364
+
365
+ // 使用示例
366
+ @messageSend="handleMessageSend"
367
+
368
+ const handleMessageSend = (eventType: string, eventData: any) => {
369
+ switch (eventType) {
370
+ case 'voice':
371
+ if (eventData === 'start') {
372
+ // 开始录音
373
+ } else if (eventData === 'stop') {
374
+ // 停止录音
375
+ }
376
+ break
377
+ case 'dataCollection':
378
+ // 处理埋点数据
379
+ console.log('Tracking:', eventData)
380
+ break
381
+ case 'sessionUpdated':
382
+ // 处理会话更新
383
+ console.log('Session updated:', eventData)
384
+ break
385
+ }
386
+ }
387
+ ```
388
+
389
+ #### `feedback`
390
+
391
+ 反馈事件:
392
+
393
+ ```typescript
394
+ @feedback="handleFeedback"
395
+
396
+ const handleFeedback = (feedbackId: string) => {
397
+ console.log('Feedback ID:', feedbackId)
398
+ }
399
+ ```
400
+
401
+ #### `muteToggle`
402
+
403
+ 静音切换事件:
404
+
405
+ ```typescript
406
+ @muteToggle="handleMuteToggle"
407
+
408
+ const handleMuteToggle = (isMuted: boolean) => {
409
+ console.log('Muted:', isMuted)
410
+ }
411
+ ```
412
+
413
+ #### `snap-another`
414
+
415
+ 拍照另一个事件:
416
+
417
+ ```typescript
418
+ @snap-another="handleSnapAnother"
419
+
420
+ const handleSnapAnother = () => {
421
+ console.log('Snap another question')
422
+ }
423
+ ```
424
+
425
+ #### `session-updated`
426
+
427
+ 会话更新事件:
428
+
429
+ ```typescript
430
+ @session-updated="handleSessionUpdated"
431
+
432
+ const handleSessionUpdated = (data: any) => {
433
+ console.log('Session updated:', data)
434
+ }
435
+ ```
436
+
437
+ #### `tracking`
438
+
439
+ 数据埋点事件:
440
+
441
+ ```typescript
442
+ @tracking="handleTracking"
443
+
444
+ const handleTracking = (data: any) => {
445
+ console.log('Tracking data:', data)
446
+ // 发送到数据统计平台
447
+ }
448
+ ```
449
+
450
+ #### `question-data-updated`
451
+
452
+ 问题数据更新事件:
453
+
454
+ ```typescript
455
+ @question-data-updated="handleQuestionDataUpdated"
456
+
457
+ const handleQuestionDataUpdated = (data: {
458
+ questionText: string
459
+ questionImage: string
460
+ questionId: string
461
+ }) => {
462
+ console.log('Question data updated:', data)
463
+ }
464
+ ```
465
+
466
+ ### Methods (通过 ref 调用)
467
+
468
+ ```vue
469
+ <template>
470
+ <AIChatComponent ref="aiChatRef" />
471
+ </template>
472
+
473
+ <script setup>
474
+ import { ref } from 'vue'
475
+
476
+ const aiChatRef = ref()
477
+
478
+ // 添加消息
479
+ aiChatRef.value.addMessage({
480
+ id: 'msg_1',
481
+ type: 'ai',
482
+ content: 'Hello!',
483
+ timestamp: Date.now()
484
+ })
485
+
486
+ // 添加 AI 消息
487
+ aiChatRef.value.addAIMessage('This is an AI message')
488
+
489
+ // 添加用户消息
490
+ aiChatRef.value.addUserMessage('This is a user message')
491
+
492
+ // 清空消息
493
+ aiChatRef.value.clearMessages()
494
+
495
+ // 滚动到底部
496
+ aiChatRef.value.scrollToBottom()
497
+
498
+ // 处理语音数据
499
+ aiChatRef.value.handleVoiceData({
500
+ content: '语音转文字结果'
501
+ })
502
+
503
+ // 取消录音
504
+ aiChatRef.value.cancelVoiceRecord()
505
+
506
+ // 开始讲解
507
+ aiChatRef.value.handleStartExplaining('message_id')
508
+
509
+ // 静音控制
510
+ aiChatRef.value.handleMuteToggle(true) // 静音
511
+ aiChatRef.value.handleMuteToggle(false) // 取消静音
512
+
513
+ // 清理 TTS 资源
514
+ aiChatRef.value.clearTTSResources()
515
+
516
+ // 销毁 TTS 播放器
517
+ aiChatRef.value.destroyTTS()
518
+ </script>
519
+ ```
520
+
521
+ ## 🧩 组件说明
522
+
523
+ ### AIChatComponent
524
+
525
+ 主要的聊天组件,包含以下子组件:
526
+
527
+ - **Loading**: 加载状态组件
528
+ - **FeedbackButtons**: 反馈按钮组件
529
+ - **SpecialQuestions**: 特殊题目组件
530
+ - **Dialogs**: 对话框组件
531
+ - **ImagePreview**: 图片预览组件
532
+
533
+ ### Composables
534
+
535
+ SDK 使用多个 Composables 来组织代码:
536
+
537
+ - `useMessageHandler`: 消息管理
538
+ - `useStreamHandler`: 流式消息处理
539
+ - `useFeedbackHandler`: 反馈处理
540
+ - `useHistoryHandler`: 历史记录处理
541
+ - `useTypewriter`: 打字机效果
542
+ - `useQuestionFlow`: 问题流程处理
543
+ - `useMessageCleanup`: 消息清理
544
+ - `useThinkingFlow`: 思考动画处理
545
+ - `useKeyboardHandler`: 键盘处理
546
+
547
+ ## 🎨 高级功能
548
+
549
+ ### 1. 思考动画(Thinkie is thinking...)
550
+
551
+ 当用户点击 "Start Explaining" 或 "Explain Again" 时,会显示 Thinkie 思考动画:
552
+
553
+ ```typescript
554
+ // 思考动画会自动启动,包含以下特性:
555
+ // - 7 组随机思考文案
556
+ // - 打字机效果逐行显示
557
+ // - 涟漪动画效果
558
+ // - 与流式消息同步处理
559
+ // - 平滑的收起动画
560
+ ```
561
+
562
+ ### 2. 数学公式渲染
563
+
564
+ 支持多种数学公式格式:
565
+
566
+ ```markdown
567
+ # 行内公式
568
+ 这是行内公式:$x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}$
569
+
570
+ # 块级公式
571
+ $$
572
+ \int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}
573
+ $$
574
+
575
+ # 货币符号(自动识别)
576
+ 价格是 $45,不是数学公式
577
+
578
+ # 数学公式中的货币
579
+ $$\\$ {23}$$ 会被渲染为 $23
580
+ ```
581
+
582
+ ### 3. 流式消息处理
583
+
584
+ 支持实时流式消息接收:
585
+
586
+ ```typescript
587
+ // 流式消息会自动处理,包括:
588
+ // - 实时内容更新
589
+ // - TTS 语音播放
590
+ // - 错误处理和重试
591
+ // - 网络断开检测
592
+ ```
593
+
594
+ ### 4. 内容安全检测
595
+
596
+ 发送消息前会自动进行内容安全检测:
597
+
598
+ ```typescript
599
+ // 如果内容不安全,会:
600
+ // - 删除用户消息
601
+ // - 显示安全警告弹窗
602
+ // - 阻止消息发送
603
+ ```
604
+
605
+ ## 🛠️ 开发指南
606
+
607
+ ### 本地开发
608
+
609
+ ```bash
610
+ # 安装依赖
611
+ pnpm install
612
+
613
+ # 启动开发服务器
614
+ pnpm dev
615
+
616
+ # 构建库
617
+ pnpm build:lib
618
+
619
+ # 构建类型定义
620
+ pnpm build:types
621
+ ```
622
+
623
+ ### 项目结构
624
+
625
+ ```
626
+ src/
627
+ ├── components/
628
+ │ └── AIChatSDK/ # AI 聊天 SDK 核心代码
629
+ │ ├── AIChatComponent.vue # 主组件
630
+ │ ├── component/ # 子组件
631
+ │ ├── composables/ # 组合式函数
632
+ │ ├── utils/ # 工具函数
633
+ │ ├── types.ts # 类型定义
634
+ │ └── style.scss # 样式文件
635
+ ├── utils/
636
+ │ ├── render.ts # 内容渲染(数学公式、Markdown)
637
+ │ ├── request.ts # API 请求
638
+ │ ├── tts.ts # TTS 语音播放
639
+ │ ├── useSSE.ts # SSE 流式消息
640
+ │ └── bridge.ts # 客户端桥接
641
+ ├── views/
642
+ │ └── QuestionChatPage/ # 问题聊天页面
643
+ └── store/
644
+ └── useAppStore.ts # 状态管理
645
+ ```
646
+
647
+ ### 类型定义
648
+
649
+ 所有类型定义都在 `src/components/AIChatSDK/types.ts` 中,主要类型包括:
650
+
651
+ - `IAIChatConfig`: 完整配置类型
652
+ - `IMessage`: 消息类型
653
+ - `IAvatarConfig`: 头像配置
654
+ - `IAIInfoConfig`: AI 信息配置
655
+ - `IInputConfig`: 输入框配置
656
+ - `IStyleConfig`: 样式配置
657
+ - 等等...
658
+
659
+ ## ❓ 常见问题
660
+
661
+ ### 1. 如何自定义样式?
662
+
663
+ 可以通过 `config.styles` 配置自定义样式,或者使用 `customClasses` 添加自定义 CSS 类名。
664
+
665
+ ### 2. 如何处理数学公式?
666
+
667
+ 数学公式会自动渲染,支持 `$...$`(行内)和 `$$...$$`(块级)格式。货币符号(如 `$45`)会自动识别并保留。
668
+
669
+ ### 3. 如何集成语音功能?
670
+
671
+ 语音功能需要客户端支持,通过 `bridge.ts` 与客户端通信。确保客户端实现了相应的桥接方法。
672
+
673
+ ### 4. 如何自定义思考文案?
674
+
675
+ 思考文案在 `src/components/AIChatSDK/composables/useThinkingFlow.ts` 中的 `THINKING_OPTIONS` 数组中定义,可以修改或添加新的文案组。
676
+
677
+ ### 5. 如何处理网络错误?
678
+
679
+ 网络错误会自动处理,显示错误消息和重试按钮。可以通过 `retryStreamMessage` 方法手动重试。
680
+
681
+ ### 6. 如何自定义埋点?
682
+
683
+ 埋点通过 `@tracking` 事件发送,可以在父组件中接收并发送到数据统计平台。
684
+
685
+ ## 📝 更新日志
686
+
687
+ ### v0.0.1
688
+
689
+ - ✅ 初始版本发布
690
+ - ✅ 基础聊天功能
691
+ - ✅ 流式消息处理
692
+ - ✅ 数学公式渲染
693
+ - ✅ 思考动画功能
694
+ - ✅ 语音交互支持
695
+ - ✅ 反馈功能
696
+ - ✅ 历史记录支持
697
+
698
+ ## 📄 许可证
699
+
700
+ ISC
701
+
702
+ ## 👥 作者
703
+
704
+ - **wuxiaoqiang** - v_wuxiaoqiang@tal.com
705
+
706
+ ## 🤝 贡献
707
+
708
+ 欢迎提交 Issue 和 Pull Request!
709
+
710
+ ---
711
+
712
+ **注意**:本项目需要 Vue 3.4.0+ 和相应的客户端桥接支持。