dialog-survey 1.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/CHANGELOG.md +95 -0
- package/README.md +242 -0
- package/VERSION +1 -0
- package/dist/api/admin-templates.d.ts +15 -0
- package/dist/api/admin-templates.d.ts.map +1 -0
- package/dist/api/admin-templates.js +611 -0
- package/dist/api/admin-templates.js.map +1 -0
- package/dist/api/analysis.d.ts +8 -0
- package/dist/api/analysis.d.ts.map +1 -0
- package/dist/api/analysis.js +106 -0
- package/dist/api/analysis.js.map +1 -0
- package/dist/api/health.d.ts +6 -0
- package/dist/api/health.d.ts.map +1 -0
- package/dist/api/health.js +116 -0
- package/dist/api/health.js.map +1 -0
- package/dist/api/plans.d.ts +3 -0
- package/dist/api/plans.d.ts.map +1 -0
- package/dist/api/plans.js +184 -0
- package/dist/api/plans.js.map +1 -0
- package/dist/api/templates.d.ts +9 -0
- package/dist/api/templates.d.ts.map +1 -0
- package/dist/api/templates.js +211 -0
- package/dist/api/templates.js.map +1 -0
- package/dist/core/graph.d.ts +7 -0
- package/dist/core/graph.d.ts.map +1 -0
- package/dist/core/graph.js +31 -0
- package/dist/core/graph.js.map +1 -0
- package/dist/core/nodes/analyzing.d.ts +3 -0
- package/dist/core/nodes/analyzing.d.ts.map +1 -0
- package/dist/core/nodes/analyzing.js +38 -0
- package/dist/core/nodes/analyzing.js.map +1 -0
- package/dist/core/nodes/completed.d.ts +3 -0
- package/dist/core/nodes/completed.d.ts.map +1 -0
- package/dist/core/nodes/completed.js +8 -0
- package/dist/core/nodes/completed.js.map +1 -0
- package/dist/core/nodes/interviewing.d.ts +13 -0
- package/dist/core/nodes/interviewing.d.ts.map +1 -0
- package/dist/core/nodes/interviewing.js +130 -0
- package/dist/core/nodes/interviewing.js.map +1 -0
- package/dist/core/nodes/planning.d.ts +15 -0
- package/dist/core/nodes/planning.d.ts.map +1 -0
- package/dist/core/nodes/planning.js +40 -0
- package/dist/core/nodes/planning.js.map +1 -0
- package/dist/core/types/index.d.ts +69 -0
- package/dist/core/types/index.d.ts.map +1 -0
- package/dist/core/types/index.js +49 -0
- package/dist/core/types/index.js.map +1 -0
- package/dist/domains/interview/entities/interview.entity.d.ts +21 -0
- package/dist/domains/interview/entities/interview.entity.d.ts.map +1 -0
- package/dist/domains/interview/entities/interview.entity.js +20 -0
- package/dist/domains/interview/entities/interview.entity.js.map +1 -0
- package/dist/domains/interview/entities/message.entity.d.ts +15 -0
- package/dist/domains/interview/entities/message.entity.d.ts.map +1 -0
- package/dist/domains/interview/entities/message.entity.js +11 -0
- package/dist/domains/interview/entities/message.entity.js.map +1 -0
- package/dist/domains/interview/entities/response.entity.d.ts +17 -0
- package/dist/domains/interview/entities/response.entity.d.ts.map +1 -0
- package/dist/domains/interview/entities/response.entity.js +12 -0
- package/dist/domains/interview/entities/response.entity.js.map +1 -0
- package/dist/domains/interview/services/interview.domain.d.ts +8 -0
- package/dist/domains/interview/services/interview.domain.d.ts.map +1 -0
- package/dist/domains/interview/services/interview.domain.js +30 -0
- package/dist/domains/interview/services/interview.domain.js.map +1 -0
- package/dist/integrations/dingtalk/client.d.ts +32 -0
- package/dist/integrations/dingtalk/client.d.ts.map +1 -0
- package/dist/integrations/dingtalk/client.js +89 -0
- package/dist/integrations/dingtalk/client.js.map +1 -0
- package/dist/integrations/dingtalk/message-sender.d.ts +24 -0
- package/dist/integrations/dingtalk/message-sender.d.ts.map +1 -0
- package/dist/integrations/dingtalk/message-sender.js +81 -0
- package/dist/integrations/dingtalk/message-sender.js.map +1 -0
- package/dist/integrations/dingtalk/middleware.d.ts +4 -0
- package/dist/integrations/dingtalk/middleware.d.ts.map +1 -0
- package/dist/integrations/dingtalk/middleware.js +29 -0
- package/dist/integrations/dingtalk/middleware.js.map +1 -0
- package/dist/integrations/dingtalk/stream-client.d.ts +112 -0
- package/dist/integrations/dingtalk/stream-client.d.ts.map +1 -0
- package/dist/integrations/dingtalk/stream-client.js +305 -0
- package/dist/integrations/dingtalk/stream-client.js.map +1 -0
- package/dist/integrations/dingtalk/token-manager.d.ts +22 -0
- package/dist/integrations/dingtalk/token-manager.d.ts.map +1 -0
- package/dist/integrations/dingtalk/token-manager.js +109 -0
- package/dist/integrations/dingtalk/token-manager.js.map +1 -0
- package/dist/integrations/llm/alibaba.d.ts +10 -0
- package/dist/integrations/llm/alibaba.d.ts.map +1 -0
- package/dist/integrations/llm/alibaba.js +60 -0
- package/dist/integrations/llm/alibaba.js.map +1 -0
- package/dist/integrations/llm/base.d.ts +28 -0
- package/dist/integrations/llm/base.d.ts.map +1 -0
- package/dist/integrations/llm/base.js +2 -0
- package/dist/integrations/llm/base.js.map +1 -0
- package/dist/integrations/llm/volcengine.d.ts +12 -0
- package/dist/integrations/llm/volcengine.d.ts.map +1 -0
- package/dist/integrations/llm/volcengine.js +80 -0
- package/dist/integrations/llm/volcengine.js.map +1 -0
- package/dist/middleware/admin-auth.d.ts +3 -0
- package/dist/middleware/admin-auth.d.ts.map +1 -0
- package/dist/middleware/admin-auth.js +37 -0
- package/dist/middleware/admin-auth.js.map +1 -0
- package/dist/middleware/api-auth.d.ts +10 -0
- package/dist/middleware/api-auth.d.ts.map +1 -0
- package/dist/middleware/api-auth.js +30 -0
- package/dist/middleware/api-auth.js.map +1 -0
- package/dist/repositories/interview-state-mapper.d.ts +28 -0
- package/dist/repositories/interview-state-mapper.d.ts.map +1 -0
- package/dist/repositories/interview-state-mapper.js +32 -0
- package/dist/repositories/interview-state-mapper.js.map +1 -0
- package/dist/repositories/interview-state.repository.d.ts +34 -0
- package/dist/repositories/interview-state.repository.d.ts.map +1 -0
- package/dist/repositories/interview-state.repository.js +220 -0
- package/dist/repositories/interview-state.repository.js.map +1 -0
- package/dist/repositories/interview.repository.d.ts +29 -0
- package/dist/repositories/interview.repository.d.ts.map +1 -0
- package/dist/repositories/interview.repository.js +36 -0
- package/dist/repositories/interview.repository.js.map +1 -0
- package/dist/repositories/message.repository.d.ts +17 -0
- package/dist/repositories/message.repository.d.ts.map +1 -0
- package/dist/repositories/message.repository.js +37 -0
- package/dist/repositories/message.repository.js.map +1 -0
- package/dist/repositories/template.repository.d.ts +117 -0
- package/dist/repositories/template.repository.d.ts.map +1 -0
- package/dist/repositories/template.repository.js +280 -0
- package/dist/repositories/template.repository.js.map +1 -0
- package/dist/schemas/dimensions.d.ts +61 -0
- package/dist/schemas/dimensions.d.ts.map +1 -0
- package/dist/schemas/dimensions.js +44 -0
- package/dist/schemas/dimensions.js.map +1 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +217 -0
- package/dist/server.js.map +1 -0
- package/dist/services/analysis.service.d.ts +81 -0
- package/dist/services/analysis.service.d.ts.map +1 -0
- package/dist/services/analysis.service.js +229 -0
- package/dist/services/analysis.service.js.map +1 -0
- package/dist/services/analytics.service.d.ts +44 -0
- package/dist/services/analytics.service.d.ts.map +1 -0
- package/dist/services/analytics.service.js +222 -0
- package/dist/services/analytics.service.js.map +1 -0
- package/dist/services/asr.service.d.ts +9 -0
- package/dist/services/asr.service.d.ts.map +1 -0
- package/dist/services/asr.service.js +41 -0
- package/dist/services/asr.service.js.map +1 -0
- package/dist/services/audit-cleanup.service.d.ts +9 -0
- package/dist/services/audit-cleanup.service.d.ts.map +1 -0
- package/dist/services/audit-cleanup.service.js +27 -0
- package/dist/services/audit-cleanup.service.js.map +1 -0
- package/dist/services/batch-aggregation.service.d.ts +34 -0
- package/dist/services/batch-aggregation.service.d.ts.map +1 -0
- package/dist/services/batch-aggregation.service.js +91 -0
- package/dist/services/batch-aggregation.service.js.map +1 -0
- package/dist/services/conversation-engine.d.ts +16 -0
- package/dist/services/conversation-engine.d.ts.map +1 -0
- package/dist/services/conversation-engine.js +49 -0
- package/dist/services/conversation-engine.js.map +1 -0
- package/dist/services/dead-letter.service.d.ts +12 -0
- package/dist/services/dead-letter.service.d.ts.map +1 -0
- package/dist/services/dead-letter.service.js +24 -0
- package/dist/services/dead-letter.service.js.map +1 -0
- package/dist/services/followup.service.d.ts +18 -0
- package/dist/services/followup.service.d.ts.map +1 -0
- package/dist/services/followup.service.js +146 -0
- package/dist/services/followup.service.js.map +1 -0
- package/dist/services/interview-plan-base.service.d.ts +226 -0
- package/dist/services/interview-plan-base.service.d.ts.map +1 -0
- package/dist/services/interview-plan-base.service.js +217 -0
- package/dist/services/interview-plan-base.service.js.map +1 -0
- package/dist/services/interview-plan-members.service.d.ts +45 -0
- package/dist/services/interview-plan-members.service.d.ts.map +1 -0
- package/dist/services/interview-plan-members.service.js +237 -0
- package/dist/services/interview-plan-members.service.js.map +1 -0
- package/dist/services/interview-plan-send.service.d.ts +27 -0
- package/dist/services/interview-plan-send.service.d.ts.map +1 -0
- package/dist/services/interview-plan-send.service.js +250 -0
- package/dist/services/interview-plan-send.service.js.map +1 -0
- package/dist/services/interview-plan.service.d.ts +8 -0
- package/dist/services/interview-plan.service.d.ts.map +1 -0
- package/dist/services/interview-plan.service.js +8 -0
- package/dist/services/interview-plan.service.js.map +1 -0
- package/dist/services/llm-clustering.d.ts +16 -0
- package/dist/services/llm-clustering.d.ts.map +1 -0
- package/dist/services/llm-clustering.js +91 -0
- package/dist/services/llm-clustering.js.map +1 -0
- package/dist/services/prompt.service.d.ts +13 -0
- package/dist/services/prompt.service.d.ts.map +1 -0
- package/dist/services/prompt.service.js +151 -0
- package/dist/services/prompt.service.js.map +1 -0
- package/dist/services/report.service.d.ts +28 -0
- package/dist/services/report.service.d.ts.map +1 -0
- package/dist/services/report.service.js +169 -0
- package/dist/services/report.service.js.map +1 -0
- package/dist/services/stream-message-utils.d.ts +31 -0
- package/dist/services/stream-message-utils.d.ts.map +1 -0
- package/dist/services/stream-message-utils.js +80 -0
- package/dist/services/stream-message-utils.js.map +1 -0
- package/dist/services/stream-message.service.d.ts +29 -0
- package/dist/services/stream-message.service.d.ts.map +1 -0
- package/dist/services/stream-message.service.js +184 -0
- package/dist/services/stream-message.service.js.map +1 -0
- package/dist/services/template-dimension.service.d.ts +21 -0
- package/dist/services/template-dimension.service.d.ts.map +1 -0
- package/dist/services/template-dimension.service.js +9 -0
- package/dist/services/template-dimension.service.js.map +1 -0
- package/dist/utils/db.d.ts +10 -0
- package/dist/utils/db.d.ts.map +1 -0
- package/dist/utils/db.js +21 -0
- package/dist/utils/db.js.map +1 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +21 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/markdown.d.ts +2 -0
- package/dist/utils/markdown.d.ts.map +1 -0
- package/dist/utils/markdown.js +39 -0
- package/dist/utils/markdown.js.map +1 -0
- package/dist/utils/pii-anonymizer.d.ts +2 -0
- package/dist/utils/pii-anonymizer.d.ts.map +1 -0
- package/dist/utils/pii-anonymizer.js +13 -0
- package/dist/utils/pii-anonymizer.js.map +1 -0
- package/dist/utils/retry.d.ts +8 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +28 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/security.d.ts +26 -0
- package/dist/utils/security.d.ts.map +1 -0
- package/dist/utils/security.js +99 -0
- package/dist/utils/security.js.map +1 -0
- package/package.json +80 -0
- package/prisma/schema.prisma +228 -0
- package/public/css/admin.css +73 -0
- package/public/js/alpine.min.js +2670 -0
- package/public/js/htmx.min.js +3422 -0
- package/scripts/cli.mjs +794 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
export async function clusterDimensionQuotes(quotes, dimension, llm) {
|
|
2
|
+
if (!quotes.length)
|
|
3
|
+
return [];
|
|
4
|
+
const prompt = `You are a qualitative research analyst.
|
|
5
|
+
Cluster the following ${quotes.length} user quotes about "${dimension}" into 3-8 sub-themes.
|
|
6
|
+
|
|
7
|
+
**Quotes**:
|
|
8
|
+
${quotes.map((q, i) => `${i + 1}. "${q}"`).join('\n')}
|
|
9
|
+
|
|
10
|
+
**Task**:
|
|
11
|
+
1. Group similar quotes into 3-8 sub-themes
|
|
12
|
+
2. For each sub-theme: provide a name, count, overall sentiment (positive/negative/neutral), and 1-2 representative quotes
|
|
13
|
+
|
|
14
|
+
**Return JSON only**:
|
|
15
|
+
[{"subTopicName":"...","mentionCount":3,"overallSentiment":"positive|negative|neutral","representativeQuotes":["..."]}]`;
|
|
16
|
+
try {
|
|
17
|
+
const response = await llm.chat({
|
|
18
|
+
model: process.env.LLM_MODEL || process.env.VOLCENGINE_MODEL || 'deepseek-v3.2',
|
|
19
|
+
messages: [{ role: 'user', content: prompt }],
|
|
20
|
+
max_tokens: 2000,
|
|
21
|
+
temperature: 0.1,
|
|
22
|
+
});
|
|
23
|
+
const parsed = parseSubThemes(response.content);
|
|
24
|
+
return parsed;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export async function discoverEmergentIssues(allEmergents, llm) {
|
|
31
|
+
if (!allEmergents.length)
|
|
32
|
+
return [];
|
|
33
|
+
const prompt = `You are a qualitative research analyst.
|
|
34
|
+
From the following user-emergent issue tags across multiple interviews, identify the top 5 most important issues.
|
|
35
|
+
|
|
36
|
+
**All emergent tags**:
|
|
37
|
+
${allEmergents.join('\n')}
|
|
38
|
+
|
|
39
|
+
**Task**:
|
|
40
|
+
1. Identify up to 5 most important issues
|
|
41
|
+
2. For each: provide name, estimated mention count, urgency (high/medium/low), and up to 3 representative examples
|
|
42
|
+
|
|
43
|
+
**Return JSON only**:
|
|
44
|
+
[{"name":"...","mentionCount":15,"urgency":"high|medium|low","representativeQuotes":["..."]}]`;
|
|
45
|
+
try {
|
|
46
|
+
const response = await llm.chat({
|
|
47
|
+
model: process.env.LLM_MODEL || process.env.VOLCENGINE_MODEL || 'deepseek-v3.2',
|
|
48
|
+
messages: [{ role: 'user', content: prompt }],
|
|
49
|
+
max_tokens: 1500,
|
|
50
|
+
temperature: 0.1,
|
|
51
|
+
});
|
|
52
|
+
const parsed = parseEmergentIssues(response.content);
|
|
53
|
+
return parsed;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function parseSubThemes(content) {
|
|
60
|
+
try {
|
|
61
|
+
const cleaned = content
|
|
62
|
+
.replace(/```json\n?/g, '')
|
|
63
|
+
.replace(/```\n?/g, '')
|
|
64
|
+
.trim();
|
|
65
|
+
const parsed = JSON.parse(cleaned);
|
|
66
|
+
if (Array.isArray(parsed) && parsed.length >= 3 && parsed.length <= 8) {
|
|
67
|
+
return parsed;
|
|
68
|
+
}
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function parseEmergentIssues(content) {
|
|
76
|
+
try {
|
|
77
|
+
const cleaned = content
|
|
78
|
+
.replace(/```json\n?/g, '')
|
|
79
|
+
.replace(/```\n?/g, '')
|
|
80
|
+
.trim();
|
|
81
|
+
const parsed = JSON.parse(cleaned);
|
|
82
|
+
if (Array.isArray(parsed)) {
|
|
83
|
+
return parsed.slice(0, 5);
|
|
84
|
+
}
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=llm-clustering.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-clustering.js","sourceRoot":"","sources":["../../src/services/llm-clustering.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface PromptTemplate {
|
|
2
|
+
name: string;
|
|
3
|
+
template: string;
|
|
4
|
+
variables: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare class PromptService {
|
|
7
|
+
private templates;
|
|
8
|
+
constructor(templates?: Record<string, PromptTemplate>);
|
|
9
|
+
render(templateName: string, variables: Record<string, string>): string;
|
|
10
|
+
getTemplate(name: string): PromptTemplate | undefined;
|
|
11
|
+
}
|
|
12
|
+
export declare const promptService: PromptService;
|
|
13
|
+
//# sourceMappingURL=prompt.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.service.d.ts","sourceRoot":"","sources":["../../src/services/prompt.service.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAuID,qBAAa,aAAa;IACxB,OAAO,CAAC,SAAS,CAA8B;gBAEnC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC;IAItD,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM;IAgBvE,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;CAGtD;AAED,eAAO,MAAM,aAAa,eAAsB,CAAC"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
const TEMPLATES = {
|
|
2
|
+
generateQuestion: {
|
|
3
|
+
name: 'generateQuestion',
|
|
4
|
+
template: `你是访谈主持人。根据以下访谈上下文,生成下一个问题。
|
|
5
|
+
|
|
6
|
+
访谈主题: {{topic}}
|
|
7
|
+
当前问题: {{currentQuestion}}
|
|
8
|
+
用户回答: {{userAnswer}}
|
|
9
|
+
|
|
10
|
+
请生成下一个问题,要求简洁、具体。`,
|
|
11
|
+
variables: ['topic', 'currentQuestion', 'userAnswer'],
|
|
12
|
+
},
|
|
13
|
+
generateReport: {
|
|
14
|
+
name: 'generateReport',
|
|
15
|
+
template: `根据以下访谈内容,生成访谈报告。
|
|
16
|
+
|
|
17
|
+
访谈主题: {{topic}}
|
|
18
|
+
访谈问题与回答:
|
|
19
|
+
{{qaPairs}}
|
|
20
|
+
|
|
21
|
+
请生成包含以下内容的报告:
|
|
22
|
+
1. 关键发现
|
|
23
|
+
2. 情感分析
|
|
24
|
+
3. 行动建议
|
|
25
|
+
|
|
26
|
+
使用中文输出。`,
|
|
27
|
+
variables: ['topic', 'qaPairs'],
|
|
28
|
+
},
|
|
29
|
+
generateSmartResponse: {
|
|
30
|
+
name: 'generateSmartResponse',
|
|
31
|
+
template: `你是一位温暖、专业的访谈主持人。你的目标是让被访者感到舒适、愿意深入分享真实经验。
|
|
32
|
+
|
|
33
|
+
**重要原则**:
|
|
34
|
+
- 不要机械地问问题,要像朋友聊天一样自然
|
|
35
|
+
- 用户情绪变化时,先回应情绪,再考虑访谈进度
|
|
36
|
+
- 宁可少问一个问题,也不要让用户感到被强迫
|
|
37
|
+
- **绝对不要在回应中包含或暗示下一个问题**。你只需要对用户的当前回答做出温暖回应,下一个问题会由系统自动追加。如果你重复了下一个问题,用户会感到困惑。
|
|
38
|
+
|
|
39
|
+
**深度追问要求**:
|
|
40
|
+
当用户分享了问题或痛点时,不要停留在表面的感谢或简单追问。要深入挖掘:
|
|
41
|
+
- 鼓励用户描述**具体场景**而非抽象概念
|
|
42
|
+
- 追问用户**亲身经历**的细节("当时是什么情况?""您是怎么应对的?")
|
|
43
|
+
- 引导用户提出**自己的解决建议**("如果让您来改进,您会怎么做?")
|
|
44
|
+
- 探寻用户的**真实想法**而非官方回答("抛开公司政策不说,您个人觉得…")
|
|
45
|
+
|
|
46
|
+
目标是获取一线的真实痛点和建议,这些对于后续改进非常有价值。
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
**对话历史**:
|
|
51
|
+
{{conversationHistory}}
|
|
52
|
+
|
|
53
|
+
**当前问题**: {{currentQuestion}}
|
|
54
|
+
**已追问次数**: {{followupCount}}/{{maxFollowups}}
|
|
55
|
+
**用户回答**: {{userAnswer}}
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
分析用户回答后,选择策略并生成回应。
|
|
60
|
+
|
|
61
|
+
**策略选择**(选择最匹配的一项):
|
|
62
|
+
1. **认真回答** - 用户认真回答了问题 → 肯定并深入挖掘细节 → 追问具体场景、个人体验、解决建议
|
|
63
|
+
2. **敷衍回答** - 用户敷衍(如"嗯嗯"、"不知道")→ 简短回应 → 换角度激发兴趣
|
|
64
|
+
3. **困惑不理解** - 用户不理解问题或访谈目的 → 解释目的 → 温柔引导
|
|
65
|
+
4. **拒绝不想答** - 用户拒绝回答此问题 → 表示理解 → 提议换话题或跳过
|
|
66
|
+
5. **不满情绪激动** - 用户表达不满/愤怒 → 真诚道歉共情 → 尊重用户选择
|
|
67
|
+
6. **要求结束** - 用户想结束访谈 → 确认结束 → 真诚感谢受访者的时间和分享,表达祝福(如"非常感谢您抽时间参与这次访谈,您的分享对我们很有价值,祝您工作顺利!")
|
|
68
|
+
7. **要求跳过** - 用户想跳过此题 → 同意跳过 → 直接进入下一题
|
|
69
|
+
8. **质疑测试边界** - 用户质疑访谈者能力或测试边界 → 温和重申角色 → 保持专业
|
|
70
|
+
9. **转移话题** - 用户转移话题但不想结束 → 温和引导回主题 或 接受转移
|
|
71
|
+
|
|
72
|
+
**行动决策**:
|
|
73
|
+
- NEXT: 用户回答充分,应该进入下一个问题
|
|
74
|
+
- FOLLOWUP: 需要追问细节(注意:已追问次数不能超过上限)。追问时应提出一个具体的新问题,引导用户深入分享经验和建议。
|
|
75
|
+
- END: 用户明确想结束访谈
|
|
76
|
+
- STAY: 其他情况,继续当前话题
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
**输出格式**(必须严格按JSON格式返回,不要输出其他内容):
|
|
81
|
+
|
|
82
|
+
{
|
|
83
|
+
"thinking": "简短分析用户意图和情绪(10-20字)",
|
|
84
|
+
"strategy": "选择的策略编号(1-9)",
|
|
85
|
+
"action": "NEXT 或 FOLLOWUP 或 END 或 STAY",
|
|
86
|
+
"response": "你的回应内容(50-100字,温暖自然。如果是NEXT,只需肯定用户的分享,不要包含下一个问题。)"
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
**示例输出**:
|
|
90
|
+
{
|
|
91
|
+
"thinking": "用户质疑访谈目的,情绪困惑",
|
|
92
|
+
"strategy": "3",
|
|
93
|
+
"action": "STAY",
|
|
94
|
+
"response": "理解您的疑惑。这个访谈是想了解您的工作经历,帮助我们更好地理解您的专业背景。如果您不太想聊这个,我们可以换个话题?"
|
|
95
|
+
}`,
|
|
96
|
+
variables: [
|
|
97
|
+
'conversationHistory',
|
|
98
|
+
'currentQuestion',
|
|
99
|
+
'followupCount',
|
|
100
|
+
'maxFollowups',
|
|
101
|
+
'userAnswer',
|
|
102
|
+
'userName',
|
|
103
|
+
'lastQuestionFlag',
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
analyzeWithDimensions: {
|
|
107
|
+
name: 'analyzeWithDimensions',
|
|
108
|
+
template: `你是一个定性研究分析师。根据以下访谈对话内容,按预设维度进行分类标注。
|
|
109
|
+
|
|
110
|
+
**预设维度**:
|
|
111
|
+
{{dimensions}}
|
|
112
|
+
|
|
113
|
+
每个维度包含:id、label、description、keywords
|
|
114
|
+
|
|
115
|
+
**访谈内容**:
|
|
116
|
+
{{qaPairs}}
|
|
117
|
+
|
|
118
|
+
**任务**:
|
|
119
|
+
1. 对每个维度,判断受访者是否提及
|
|
120
|
+
2. 如果提及,判断情感倾向(positive/negative/neutral)
|
|
121
|
+
3. 提取1-2条最具代表性的原文引用
|
|
122
|
+
4. 如果发现预设维度之外的重要问题,提取为emergentTag
|
|
123
|
+
5. 给出整体满意度评分(1-5,5为最满意)
|
|
124
|
+
|
|
125
|
+
**返回JSON**:
|
|
126
|
+
{"dimensionTags":[{"dimensionId":"...","label":"...","sentiment":"positive|negative|neutral","quotes":["..."]}],"emergentTags":["..."],"interviewerRating":3}`,
|
|
127
|
+
variables: ['dimensions', 'qaPairs'],
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
export class PromptService {
|
|
131
|
+
templates;
|
|
132
|
+
constructor(templates) {
|
|
133
|
+
this.templates = new Map(Object.entries(templates || TEMPLATES));
|
|
134
|
+
}
|
|
135
|
+
render(templateName, variables) {
|
|
136
|
+
const template = this.templates.get(templateName);
|
|
137
|
+
if (!template) {
|
|
138
|
+
throw new Error(`Template not found: ${templateName}`);
|
|
139
|
+
}
|
|
140
|
+
let result = template.template;
|
|
141
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
142
|
+
result = result.replace(new RegExp(`{{${key}}}`, 'g'), value.replace(/\{\{/g, '{{').replace(/\}\}/g, '}}'));
|
|
143
|
+
}
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
getTemplate(name) {
|
|
147
|
+
return this.templates.get(name);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
export const promptService = new PromptService();
|
|
151
|
+
//# sourceMappingURL=prompt.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.service.js","sourceRoot":"","sources":["../../src/services/prompt.service.ts"],"names":[],"mappings":"AAMA,MAAM,SAAS,GAAmC;IAChD,gBAAgB,EAAE;QAChB,IAAI,EAAE,kBAAkB;QACxB,QAAQ,EAAE;;;;;;kBAMI;QACd,SAAS,EAAE,CAAC,OAAO,EAAE,iBAAiB,EAAE,YAAY,CAAC;KACtD;IAED,cAAc,EAAE;QACd,IAAI,EAAE,gBAAgB;QACtB,QAAQ,EAAE;;;;;;;;;;;QAWN;QACJ,SAAS,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC;KAChC;IAED,qBAAqB,EAAE;QACrB,IAAI,EAAE,uBAAuB;QAC7B,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgEZ;QACE,SAAS,EAAE;YACT,qBAAqB;YACrB,iBAAiB;YACjB,eAAe;YACf,cAAc;YACd,YAAY;YACZ,UAAU;YACV,kBAAkB;SACnB;KACF;IAED,qBAAqB,EAAE;QACrB,IAAI,EAAE,uBAAuB;QAC7B,QAAQ,EAAE;;;;;;;;;;;;;;;;;;8JAkBgJ;QAC1J,SAAS,EAAE,CAAC,YAAY,EAAE,SAAS,CAAC;KACrC;CACF,CAAC;AAEF,MAAM,OAAO,aAAa;IAChB,SAAS,CAA8B;IAE/C,YAAY,SAA0C;QACpD,IAAI,CAAC,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,IAAI,SAAS,CAAC,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,CAAC,YAAoB,EAAE,SAAiC;QAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAClD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,uBAAuB,YAAY,EAAE,CAAC,CAAC;QACzD,CAAC;QAED,IAAI,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC;QAC/B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YACrD,MAAM,GAAG,MAAM,CAAC,OAAO,CACrB,IAAI,MAAM,CAAC,KAAK,GAAG,IAAI,EAAE,GAAG,CAAC,EAC7B,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CACpD,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,WAAW,CAAC,IAAY;QACtB,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;CACF;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { VolcengineLLM } from '../integrations/llm/volcengine.js';
|
|
2
|
+
export interface Report {
|
|
3
|
+
interviewId: string;
|
|
4
|
+
content: string;
|
|
5
|
+
keyFindings: string[];
|
|
6
|
+
sentiment: string;
|
|
7
|
+
recommendations: string[];
|
|
8
|
+
generatedAt: Date;
|
|
9
|
+
}
|
|
10
|
+
export interface ReportWithDimensions extends Report {
|
|
11
|
+
dimensionTags?: Array<{
|
|
12
|
+
dimensionId: string;
|
|
13
|
+
label: string;
|
|
14
|
+
sentiment: 'positive' | 'negative' | 'neutral';
|
|
15
|
+
quotes: string[];
|
|
16
|
+
}>;
|
|
17
|
+
emergentTags?: string[];
|
|
18
|
+
interviewerRating?: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function generateReport(interviewId: string, topic: string, qaPairs: Array<{
|
|
21
|
+
question: string;
|
|
22
|
+
answer: string;
|
|
23
|
+
}>): Promise<Report>;
|
|
24
|
+
export declare function generateReportWithDimensions(interviewId: string, _topic: string, qaPairs: Array<{
|
|
25
|
+
question: string;
|
|
26
|
+
answer: string;
|
|
27
|
+
}>, dimensionsJson: string | null, llm: VolcengineLLM): Promise<ReportWithDimensions>;
|
|
28
|
+
//# sourceMappingURL=report.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.service.d.ts","sourceRoot":"","sources":["../../src/services/report.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAKjF,MAAM,WAAW,MAAM;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,EAAE,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,oBAAqB,SAAQ,MAAM;IAClD,aAAa,CAAC,EAAE,KAAK,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;QAC/C,MAAM,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC,CAAC;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,GACnD,OAAO,CAAC,MAAM,CAAC,CAuCjB;AA6FD,wBAAsB,4BAA4B,CAChD,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EACpD,cAAc,EAAE,MAAM,GAAG,IAAI,EAC7B,GAAG,EAAE,aAAa,GACjB,OAAO,CAAC,oBAAoB,CAAC,CA6C/B"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { DEFAULT_MODEL, VolcengineLLM } from '../integrations/llm/volcengine.js';
|
|
4
|
+
import { info } from '../utils/logger.js';
|
|
5
|
+
import { withRetry } from '../utils/retry.js';
|
|
6
|
+
import { promptService } from './prompt.service.js';
|
|
7
|
+
export async function generateReport(interviewId, topic, qaPairs) {
|
|
8
|
+
info('Generating report', { interviewId, qaCount: qaPairs.length });
|
|
9
|
+
const qaText = qaPairs.map((qa) => `Q: ${qa.question}\nA: ${qa.answer}`).join('\n\n');
|
|
10
|
+
const prompt = promptService.render('generateReport', {
|
|
11
|
+
topic,
|
|
12
|
+
qaPairs: qaText,
|
|
13
|
+
});
|
|
14
|
+
const llm = VolcengineLLM.fromEnv();
|
|
15
|
+
try {
|
|
16
|
+
const response = await withRetry(() => llm.chat({
|
|
17
|
+
model: DEFAULT_MODEL,
|
|
18
|
+
messages: [{ role: 'user', content: prompt }],
|
|
19
|
+
max_tokens: 3000,
|
|
20
|
+
}));
|
|
21
|
+
const content = response.content;
|
|
22
|
+
const parsed = parseReportContent(content);
|
|
23
|
+
const report = {
|
|
24
|
+
interviewId,
|
|
25
|
+
content,
|
|
26
|
+
keyFindings: parsed.keyFindings,
|
|
27
|
+
sentiment: parsed.sentiment,
|
|
28
|
+
recommendations: parsed.recommendations,
|
|
29
|
+
generatedAt: new Date(),
|
|
30
|
+
};
|
|
31
|
+
await saveReport(report);
|
|
32
|
+
return report;
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
const errorMsg = e instanceof Error ? e.message : 'Unknown error';
|
|
36
|
+
info('Report generation failed, using fallback', { error: errorMsg });
|
|
37
|
+
return createFallbackReport(interviewId, topic, qaPairs);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function parseReportContent(content) {
|
|
41
|
+
const keyFindings = [];
|
|
42
|
+
let sentiment = 'neutral';
|
|
43
|
+
const recommendations = [];
|
|
44
|
+
// Support both "### 1. 关键发现" (Markdown header) and "关键发现:" formats
|
|
45
|
+
const findingMatch = content.match(/[##\s]*关键发现[::]?\s*\n?([\s\S]*?)(?=情感分析|行动建议|### .*建议|$)/i) ||
|
|
46
|
+
content.match(/关键发现[\s::]*([\s\S]*?)(?=情感分析|行动建议|$)/i);
|
|
47
|
+
if (findingMatch) {
|
|
48
|
+
// Split by newlines or markdown list markers, filter out empty/heading lines
|
|
49
|
+
const findings = findingMatch[1]
|
|
50
|
+
.split(/[\n]+/)
|
|
51
|
+
.map((f) => f
|
|
52
|
+
.replace(/^[-*•]\s*/, '')
|
|
53
|
+
.replace(/^\d+\.\s*/, '')
|
|
54
|
+
.trim())
|
|
55
|
+
.filter((f) => f.length > 0);
|
|
56
|
+
keyFindings.push(...findings.slice(0, 5));
|
|
57
|
+
}
|
|
58
|
+
// Support both "### 2. 情感分析" and "情感分析:" formats
|
|
59
|
+
const sentimentMatch = content.match(/情感分析[::]\s*([\u4e00-\u9fa5]+)/i) ||
|
|
60
|
+
content.match(/情感分析[\s\S]*?(积极|正面|negative|positive|消极|负面|negative|neutral|中立|neutral)/i);
|
|
61
|
+
if (sentimentMatch) {
|
|
62
|
+
const raw = sentimentMatch[1].trim().toLowerCase();
|
|
63
|
+
if (raw.includes('积极') || raw.includes('正面') || raw === 'positive') {
|
|
64
|
+
sentiment = 'positive';
|
|
65
|
+
}
|
|
66
|
+
else if (raw.includes('消极') || raw.includes('负面') || raw === 'negative') {
|
|
67
|
+
sentiment = 'negative';
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
sentiment = 'neutral';
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Support both "### 3. 行动建议" and "行动建议:" formats
|
|
74
|
+
const recMatch = content.match(/行动建议[::]?\s*\n?([\s\S]*?)(?=总结|$)/i) ||
|
|
75
|
+
content.match(/行动建议[\s\S]*?([\s\S]*)/i);
|
|
76
|
+
if (recMatch) {
|
|
77
|
+
const recs = recMatch[1]
|
|
78
|
+
.split(/[\n]+/)
|
|
79
|
+
.map((r) => r
|
|
80
|
+
.replace(/^[-*•]\s*/, '')
|
|
81
|
+
.replace(/^\d+\.\s*/, '')
|
|
82
|
+
.trim())
|
|
83
|
+
.filter((r) => r.length > 0);
|
|
84
|
+
recommendations.push(...recs.slice(0, 5));
|
|
85
|
+
}
|
|
86
|
+
return { keyFindings, sentiment, recommendations };
|
|
87
|
+
}
|
|
88
|
+
function createFallbackReport(interviewId, topic, qaPairs) {
|
|
89
|
+
return {
|
|
90
|
+
interviewId,
|
|
91
|
+
content: `# 访谈报告\n\n主题: ${topic}\n\n问答记录:\n${qaPairs.map((qa) => `Q: ${qa.question}\nA: ${qa.answer}`).join('\n\n')}`,
|
|
92
|
+
keyFindings: qaPairs.map((qa) => qa.answer.substring(0, 50)),
|
|
93
|
+
sentiment: 'neutral',
|
|
94
|
+
recommendations: ['建议进行更深入的后续访谈'],
|
|
95
|
+
generatedAt: new Date(),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
async function saveReport(report) {
|
|
99
|
+
const reportsDir = process.env.REPORTS_DIR || './reports';
|
|
100
|
+
const filename = `${report.interviewId}_${Date.now()}.md`;
|
|
101
|
+
const filepath = path.join(reportsDir, filename);
|
|
102
|
+
fs.mkdirSync(reportsDir, { recursive: true });
|
|
103
|
+
fs.writeFileSync(filepath, report.content);
|
|
104
|
+
info('Report saved', { filepath });
|
|
105
|
+
return filepath;
|
|
106
|
+
}
|
|
107
|
+
export async function generateReportWithDimensions(interviewId, _topic, qaPairs, dimensionsJson, llm) {
|
|
108
|
+
const qaText = qaPairs.map((qa) => `Q: ${qa.question}\nA: ${qa.answer}`).join('\n\n');
|
|
109
|
+
const prompt = promptService.render('analyzeWithDimensions', {
|
|
110
|
+
dimensions: dimensionsJson || '[No preset dimensions]',
|
|
111
|
+
qaPairs: qaText,
|
|
112
|
+
});
|
|
113
|
+
try {
|
|
114
|
+
const response = await withRetry(() => llm.chat({
|
|
115
|
+
model: DEFAULT_MODEL,
|
|
116
|
+
messages: [{ role: 'user', content: prompt }],
|
|
117
|
+
max_tokens: 2000,
|
|
118
|
+
temperature: 0.1,
|
|
119
|
+
}));
|
|
120
|
+
const parsed = parseDimensionAnalysis(response.content);
|
|
121
|
+
return {
|
|
122
|
+
interviewId,
|
|
123
|
+
content: response.content,
|
|
124
|
+
keyFindings: [],
|
|
125
|
+
sentiment: '',
|
|
126
|
+
recommendations: [],
|
|
127
|
+
generatedAt: new Date(),
|
|
128
|
+
...parsed,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
catch (e) {
|
|
132
|
+
info('Dimension analysis failed', {
|
|
133
|
+
interviewId,
|
|
134
|
+
error: e.message,
|
|
135
|
+
});
|
|
136
|
+
return {
|
|
137
|
+
interviewId,
|
|
138
|
+
content: '',
|
|
139
|
+
keyFindings: [],
|
|
140
|
+
sentiment: '',
|
|
141
|
+
recommendations: [],
|
|
142
|
+
generatedAt: new Date(),
|
|
143
|
+
dimensionTags: [],
|
|
144
|
+
emergentTags: [],
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function parseDimensionAnalysis(content) {
|
|
149
|
+
try {
|
|
150
|
+
const cleaned = content
|
|
151
|
+
.replace(/```json\n?/g, '')
|
|
152
|
+
.replace(/```\n?/g, '')
|
|
153
|
+
.trim();
|
|
154
|
+
const jsonRaw = JSON.parse(cleaned);
|
|
155
|
+
if (typeof jsonRaw !== 'object' || jsonRaw === null) {
|
|
156
|
+
return { dimensionTags: [], emergentTags: [] };
|
|
157
|
+
}
|
|
158
|
+
const jsonStr = jsonRaw;
|
|
159
|
+
return {
|
|
160
|
+
dimensionTags: Array.isArray(jsonStr.dimensionTags) ? jsonStr.dimensionTags : [],
|
|
161
|
+
emergentTags: Array.isArray(jsonStr.emergentTags) ? jsonStr.emergentTags : [],
|
|
162
|
+
interviewerRating: typeof jsonStr.interviewerRating === 'number' ? jsonStr.interviewerRating : undefined,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return { dimensionTags: [], emergentTags: [] };
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=report.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.service.js","sourceRoot":"","sources":["../../src/services/report.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface StreamMessage {
|
|
2
|
+
specVersion: string;
|
|
3
|
+
type: string;
|
|
4
|
+
headers: {
|
|
5
|
+
topic: string;
|
|
6
|
+
messageId: string;
|
|
7
|
+
time: string;
|
|
8
|
+
};
|
|
9
|
+
data: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ParsedStreamMessage {
|
|
12
|
+
userId: string;
|
|
13
|
+
content: string;
|
|
14
|
+
sessionWebhook: string;
|
|
15
|
+
messageId: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ProcessResult {
|
|
18
|
+
success: boolean;
|
|
19
|
+
response?: string;
|
|
20
|
+
error?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function parseStreamMessage(message: StreamMessage): ParsedStreamMessage | null;
|
|
23
|
+
/** Allowed DingTalk hostnames for SSRF protection */
|
|
24
|
+
export declare const ALLOWED_WEBHOOK_HOSTS: Set<string>;
|
|
25
|
+
/**
|
|
26
|
+
* Validates that a webhook URL is from an allowed DingTalk hostname.
|
|
27
|
+
* Protects against SSRF attacks via malicious URLs (e.g., file:///, http://169.254169.254/, etc.)
|
|
28
|
+
*/
|
|
29
|
+
export declare function isAllowedWebhookUrl(url: string): boolean;
|
|
30
|
+
export declare function sendReply(sessionWebhook: string, content: string): Promise<boolean>;
|
|
31
|
+
//# sourceMappingURL=stream-message-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-message-utils.d.ts","sourceRoot":"","sources":["../../src/services/stream-message-utils.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,mBAAmB,GAAG,IAAI,CAiBrF;AAED,qDAAqD;AACrD,eAAO,MAAM,qBAAqB,aAIhC,CAAC;AAEH;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOxD;AAED,wBAAsB,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA4CzF"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { error, info } from '../utils/logger.js';
|
|
2
|
+
export function parseStreamMessage(message) {
|
|
3
|
+
try {
|
|
4
|
+
const data = JSON.parse(message.data);
|
|
5
|
+
const content = data.text?.content || data.content || '';
|
|
6
|
+
return {
|
|
7
|
+
userId: data.senderStaffId || '',
|
|
8
|
+
content: content,
|
|
9
|
+
sessionWebhook: data.sessionWebhook || '',
|
|
10
|
+
messageId: message.headers.messageId,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
catch (e) {
|
|
14
|
+
error('Failed to parse stream message', {
|
|
15
|
+
error: e instanceof Error ? e.message : String(e),
|
|
16
|
+
});
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** Allowed DingTalk hostnames for SSRF protection */
|
|
21
|
+
export const ALLOWED_WEBHOOK_HOSTS = new Set([
|
|
22
|
+
'oapi.dingtalk.com',
|
|
23
|
+
'api.dingtalk.com',
|
|
24
|
+
'router.dingtalk.com',
|
|
25
|
+
]);
|
|
26
|
+
/**
|
|
27
|
+
* Validates that a webhook URL is from an allowed DingTalk hostname.
|
|
28
|
+
* Protects against SSRF attacks via malicious URLs (e.g., file:///, http://169.254169.254/, etc.)
|
|
29
|
+
*/
|
|
30
|
+
export function isAllowedWebhookUrl(url) {
|
|
31
|
+
try {
|
|
32
|
+
const parsed = new URL(url);
|
|
33
|
+
return ALLOWED_WEBHOOK_HOSTS.has(parsed.hostname);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export async function sendReply(sessionWebhook, content) {
|
|
40
|
+
if (!sessionWebhook) {
|
|
41
|
+
error('No sessionWebhook provided');
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
info('Sending reply (exported)', {
|
|
45
|
+
webhook: sessionWebhook,
|
|
46
|
+
contentLength: content.length,
|
|
47
|
+
});
|
|
48
|
+
try {
|
|
49
|
+
const body = {
|
|
50
|
+
msgtype: 'text',
|
|
51
|
+
text: { content },
|
|
52
|
+
};
|
|
53
|
+
const response = await fetch(sessionWebhook, {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: { 'Content-Type': 'application/json' },
|
|
56
|
+
body: JSON.stringify(body),
|
|
57
|
+
});
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
const responseText = await response.text();
|
|
60
|
+
error('Failed to send reply', {
|
|
61
|
+
status: response.status,
|
|
62
|
+
statusText: response.statusText,
|
|
63
|
+
webhook: sessionWebhook,
|
|
64
|
+
responseText: responseText.substring(0, 200),
|
|
65
|
+
});
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
info('Reply sent', {
|
|
69
|
+
webhook: sessionWebhook,
|
|
70
|
+
contentLength: content.length,
|
|
71
|
+
});
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
const errMsg = e instanceof Error ? e.message : String(e);
|
|
76
|
+
error('Failed to send reply', { error: errMsg });
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=stream-message-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-message-utils.js","sourceRoot":"","sources":["../../src/services/stream-message-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { PrismaClient } from '@prisma/client';
|
|
2
|
+
import { InterviewStateRepository } from '../repositories/interview-state.repository.js';
|
|
3
|
+
import { parseStreamMessage } from './stream-message-utils.js';
|
|
4
|
+
export type { StreamMessage, ParsedStreamMessage, ProcessResult } from './stream-message-utils.js';
|
|
5
|
+
export declare class StreamMessageService {
|
|
6
|
+
private repo;
|
|
7
|
+
constructor(repo?: InterviewStateRepository);
|
|
8
|
+
parseStreamMessage: typeof parseStreamMessage;
|
|
9
|
+
sendReply(sessionWebhook: string, content: string): Promise<boolean>;
|
|
10
|
+
processStreamMessage(message: {
|
|
11
|
+
data: string;
|
|
12
|
+
headers: {
|
|
13
|
+
messageId: string;
|
|
14
|
+
};
|
|
15
|
+
}, retryCount?: number, prisma?: PrismaClient): Promise<{
|
|
16
|
+
success: boolean;
|
|
17
|
+
response?: string;
|
|
18
|
+
error?: string;
|
|
19
|
+
}>;
|
|
20
|
+
private processStreamMessageWithState;
|
|
21
|
+
private resolveDefaultTemplateId;
|
|
22
|
+
}
|
|
23
|
+
export { parseStreamMessage, sendReply, isAllowedWebhookUrl } from './stream-message-utils.js';
|
|
24
|
+
export declare function processStreamMessage(message: import('./stream-message-utils.js').StreamMessage, prisma?: PrismaClient): Promise<{
|
|
25
|
+
success: boolean;
|
|
26
|
+
response?: string;
|
|
27
|
+
error?: string;
|
|
28
|
+
}>;
|
|
29
|
+
//# sourceMappingURL=stream-message.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-message.service.d.ts","sourceRoot":"","sources":["../../src/services/stream-message.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAI9C,OAAO,EAAE,wBAAwB,EAAE,MAAM,+CAA+C,CAAC;AAGzF,OAAO,EAEL,kBAAkB,EAEnB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAInG,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,IAAI,CAA2B;gBAE3B,IAAI,CAAC,EAAE,wBAAwB;IAI3C,kBAAkB,4BAAsB;IAElC,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQpE,oBAAoB,CACxB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE;YAAE,SAAS,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,EACzD,UAAU,SAAI,EACd,MAAM,CAAC,EAAE,YAAY,GACpB,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;YA6IrD,6BAA6B;YAiC7B,wBAAwB;CAOvC;AAGD,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAE/F,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,OAAO,2BAA2B,EAAE,aAAa,EAC1D,MAAM,CAAC,EAAE,YAAY,GACpB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAIlE"}
|