@chatman-media/kb 1.3.1 → 1.4.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/dist/answer-types.d.ts +9 -0
- package/dist/answer-types.d.ts.map +1 -1
- package/dist/answer.d.ts.map +1 -1
- package/dist/extract-user-facts.test.d.ts +2 -0
- package/dist/extract-user-facts.test.d.ts.map +1 -0
- package/dist/fact-checker.test.d.ts +2 -0
- package/dist/fact-checker.test.d.ts.map +1 -0
- package/dist/grade-skills.test.d.ts +2 -0
- package/dist/grade-skills.test.d.ts.map +1 -0
- package/dist/index.js +9 -5
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.test.d.ts +2 -0
- package/dist/prompt.test.d.ts.map +1 -0
- package/dist/reflect.test.d.ts +2 -0
- package/dist/reflect.test.d.ts.map +1 -0
- package/dist/rewrite-query.test.d.ts +2 -0
- package/dist/rewrite-query.test.d.ts.map +1 -0
- package/dist/styles.d.ts +9 -0
- package/dist/styles.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/answer-types.ts +6 -0
- package/src/answer.ts +2 -0
- package/src/extract-user-facts.test.ts +44 -0
- package/src/fact-checker.test.ts +66 -0
- package/src/grade-skills.test.ts +39 -0
- package/src/prompt.test.ts +44 -0
- package/src/prompt.ts +8 -4
- package/src/reflect.test.ts +44 -0
- package/src/rewrite-query.test.ts +56 -0
- package/src/styles.ts +6 -0
package/dist/answer-types.d.ts
CHANGED
|
@@ -54,6 +54,15 @@ export interface AnswerInput {
|
|
|
54
54
|
* uses a calm FAQ-support block instead — answering questions without selling.
|
|
55
55
|
*/
|
|
56
56
|
supportPhase?: "docs" | "submitted";
|
|
57
|
+
/**
|
|
58
|
+
* Per-stage override (Phase 2 C-2): the lead's current funnel-stage goal/
|
|
59
|
+
* guidance from stage_definitions. Takes precedence over the Style's
|
|
60
|
+
* per-sales-stage config when composing the prompt.
|
|
61
|
+
*/
|
|
62
|
+
stageOverride?: {
|
|
63
|
+
goal: string;
|
|
64
|
+
guidance?: string;
|
|
65
|
+
};
|
|
57
66
|
/**
|
|
58
67
|
* Called after every `answerWithRag` or `answerWithRagStream` call with the
|
|
59
68
|
* final telemetry. Useful for logging, metrics, or A/B experiment recording
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"answer-types.d.ts","sourceRoot":"","sources":["../src/answer-types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"answer-types.d.ts","sourceRoot":"","sources":["../src/answer-types.ts"],"names":[],"mappings":"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"}
|
package/dist/answer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../src/answer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,YAAY,EAGjB,KAAK,OAAO,EACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AA8BzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,iBAAiB,EACjB,KAAK,OAAO,GACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,EACtB,0BAA0B,EAC1B,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EACL,iBAAiB,EACjB,4BAA4B,EAC5B,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAI5B,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,oEAAoE;IACpE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CAgG/E;
|
|
1
|
+
{"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../src/answer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,YAAY,EAGjB,KAAK,OAAO,EACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AA8BzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,iBAAiB,EACjB,KAAK,OAAO,GACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,EACtB,0BAA0B,EAC1B,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EACL,iBAAiB,EACjB,4BAA4B,EAC5B,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAI5B,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,oEAAoE;IACpE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CAgG/E;AA4OD;;;;;;;;GAQG;AACH,wBAAuB,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,aAAa,CAAC,MAAM,CAAC,CA2HpF;AAED,wBAAsB,aAAa,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EACxD,KAAK,EAAE,WAAW,GAAG;IAAE,YAAY,EAAE,CAAC,CAAA;CAAE,GACvC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrC,wBAAsB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;AAiG/E;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CAAC,KAAK,EAAE;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;CACzB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8BlB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-user-facts.test.d.ts","sourceRoot":"","sources":["../src/extract-user-facts.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fact-checker.test.d.ts","sourceRoot":"","sources":["../src/fact-checker.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grade-skills.test.d.ts","sourceRoot":"","sources":["../src/grade-skills.test.ts"],"names":[],"mappings":""}
|
package/dist/index.js
CHANGED
|
@@ -45100,9 +45100,11 @@ ${triggerLine}${h.body}`;
|
|
|
45100
45100
|
const skillsBlock = skillsForStage.length ? `\u041F\u0420\u0418\u0401\u041C\u042B (\u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 \u0443\u043C\u0435\u0441\u0442\u043D\u044B\u0435, \u043D\u0435 \u0432\u0441\u0435 \u0441\u0440\u0430\u0437\u0443 \u2014 \u0432\u044B\u0431\u0438\u0440\u0430\u0439 \u043F\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0443):
|
|
45101
45101
|
` + skillsForStage.map((s) => `- ${s.displayName} \u2014 ${s.promptFragment}`).join(`
|
|
45102
45102
|
`) : "";
|
|
45103
|
-
const
|
|
45104
|
-
|
|
45105
|
-
\u041A\u0410\
|
|
45103
|
+
const effGoal = options.stageOverride?.goal ?? stageCfg?.goal;
|
|
45104
|
+
const effGuidance = options.stageOverride?.guidance ?? stageCfg?.guidance;
|
|
45105
|
+
const stageBlock = effGoal ? `\u0422\u0415\u041A\u0423\u0429\u0418\u0419 \u042D\u0422\u0410\u041F: ${stage.toUpperCase()}.
|
|
45106
|
+
` + `\u0426\u0415\u041B\u042C \u042D\u0422\u0410\u041F\u0410: ${effGoal}.` + (effGuidance ? `
|
|
45107
|
+
\u041A\u0410\u041A: ${effGuidance}` : "") + (stageCfg?.groundingRequired ? `
|
|
45106
45108
|
GROUNDING: \u043D\u0430 \u044D\u0442\u043E\u043C \u044D\u0442\u0430\u043F\u0435 \u0432\u0441\u0435 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u044B\u0435 \u0444\u0430\u043A\u0442\u044B (\u0446\u0438\u0444\u0440\u044B, \u0441\u0443\u043C\u043C\u044B, \u0441\u0440\u043E\u043A\u0438) \u0431\u0435\u0440\u0438 \u0422\u041E\u041B\u042C\u041A\u041E \u0438\u0437 \u0441\u0435\u043A\u0446\u0438\u0438 KB CONTEXT \u043D\u0438\u0436\u0435. \u0415\u0441\u043B\u0438 \u0435\u0451 \u043D\u0435\u0442 \u0438\u043B\u0438 \u043D\u0443\u0436\u043D\u043E\u0433\u043E \u0444\u0430\u043A\u0442\u0430 \u0432 \u043D\u0435\u0439 \u043D\u0435\u0442 \u2014 \u043D\u0435 \u0432\u044B\u0434\u0443\u043C\u044B\u0432\u0430\u0439, \u0441\u043A\u0430\u0436\u0438 \u0447\u0442\u043E \u0443\u0442\u043E\u0447\u043D\u0438\u0448\u044C.` : "") : `\u0422\u0415\u041A\u0423\u0429\u0418\u0419 \u042D\u0422\u0410\u041F: ${stage}. (\u0421\u043F\u0435\u0446\u0438\u0444\u0438\u0447\u0435\u0441\u043A\u0438\u0445 \u043F\u0440\u0430\u0432\u0438\u043B \u0434\u043B\u044F \u044D\u0442\u0430\u043F\u0430 \u043D\u0435\u0442 \u2014 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 \u043E\u0431\u0449\u0438\u0439 \u0441\u0442\u0438\u043B\u044C.)`;
|
|
45107
45109
|
const minorRule = guardrails.noMinors ? "- \u0415\u0441\u043B\u0438 prospect <18 \u043B\u0435\u0442 \u2014 \u0432\u0435\u0436\u043B\u0438\u0432\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0438 \u0434\u0438\u0430\u043B\u043E\u0433." : "";
|
|
45108
45110
|
const topicsRule = guardrails.forbiddenTopics.length ? `- \u0417\u0430\u043F\u0440\u0435\u0449\u0451\u043D\u043D\u044B\u0435 \u0442\u0435\u043C\u044B: ${guardrails.forbiddenTopics.join(", ")}.` : "";
|
|
@@ -59905,7 +59907,8 @@ ${kbContextStr}` : vacBlock : kbContextStr;
|
|
|
59905
59907
|
...input.conversationSummary ? { conversationSummary: input.conversationSummary } : {},
|
|
59906
59908
|
...input.skills && input.skills.length > 0 ? { skills: input.skills } : {},
|
|
59907
59909
|
...input.directorHooks && input.directorHooks.length > 0 ? { directorHooks: input.directorHooks } : {},
|
|
59908
|
-
...input.supportPhase ? { supportPhase: input.supportPhase } : {}
|
|
59910
|
+
...input.supportPhase ? { supportPhase: input.supportPhase } : {},
|
|
59911
|
+
...input.stageOverride ? { stageOverride: input.stageOverride } : {}
|
|
59909
59912
|
});
|
|
59910
59913
|
temperature = input.style.model.temperature;
|
|
59911
59914
|
} else {
|
|
@@ -60092,7 +60095,8 @@ ${kbContextStr}` : vacBlock : kbContextStr;
|
|
|
60092
60095
|
...input.conversationSummary ? { conversationSummary: input.conversationSummary } : {},
|
|
60093
60096
|
...input.skills && input.skills.length > 0 ? { skills: input.skills } : {},
|
|
60094
60097
|
...input.directorHooks && input.directorHooks.length > 0 ? { directorHooks: input.directorHooks } : {},
|
|
60095
|
-
...input.supportPhase ? { supportPhase: input.supportPhase } : {}
|
|
60098
|
+
...input.supportPhase ? { supportPhase: input.supportPhase } : {},
|
|
60099
|
+
...input.stageOverride ? { stageOverride: input.stageOverride } : {}
|
|
60096
60100
|
});
|
|
60097
60101
|
temperature = input.style.model.temperature;
|
|
60098
60102
|
} else {
|
package/dist/prompt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAQ,KAAK,EAAE,MAAM,aAAa,CAAC;AA6C5E;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,WAAW,EAClB,mBAAmB,GAAE,MAAM,GAAG,IAAW,EACzC,OAAO,GAAE,cAAmB,GAC3B,MAAM,
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAQ,KAAK,EAAE,MAAM,aAAa,CAAC;AA6C5E;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,WAAW,EAClB,mBAAmB,GAAE,MAAM,GAAG,IAAW,EACzC,OAAO,GAAE,cAAmB,GAC3B,MAAM,CAyIR"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.test.d.ts","sourceRoot":"","sources":["../src/prompt.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reflect.test.d.ts","sourceRoot":"","sources":["../src/reflect.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rewrite-query.test.d.ts","sourceRoot":"","sources":["../src/rewrite-query.test.ts"],"names":[],"mappings":""}
|
package/dist/styles.d.ts
CHANGED
|
@@ -182,5 +182,14 @@ export interface ComposeOptions {
|
|
|
182
182
|
* block instead. `docs` = collecting their documents, `submitted` = filed.
|
|
183
183
|
*/
|
|
184
184
|
supportPhase?: "docs" | "submitted";
|
|
185
|
+
/**
|
|
186
|
+
* Per-stage override (Phase 2): goal/guidance from the lead's current funnel
|
|
187
|
+
* stage_definition. Takes precedence over `style.stages[stage]` for the stage
|
|
188
|
+
* block. Lets AI-built funnels carry per-stage instructions.
|
|
189
|
+
*/
|
|
190
|
+
stageOverride?: {
|
|
191
|
+
goal: string;
|
|
192
|
+
guidance?: string;
|
|
193
|
+
};
|
|
185
194
|
}
|
|
186
195
|
//# sourceMappingURL=styles.d.ts.map
|
package/dist/styles.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,aAAa,+DAAgE,CAAC;AAC3F,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,eAAO,MAAM,gBAAgB,2DAA4D,CAAC;AAC1F,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/D,eAAO,MAAM,UAAU,2FAOb,CAAC;AACX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD,eAAO,MAAM,UAAU;;;;;;;;;;iBAGrB,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C,eAAO,MAAM,iBAAiB;;;;;iBAK5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,aAAa;;;;;;;;iBAKxB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEzD,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsCtB,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhD;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,MAAM,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IACnC;;;;OAIG;IACH,aAAa,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACjD;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,aAAa,+DAAgE,CAAC;AAC3F,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,eAAO,MAAM,gBAAgB,2DAA4D,CAAC;AAC1F,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/D,eAAO,MAAM,UAAU,2FAOb,CAAC;AACX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD,eAAO,MAAM,UAAU;;;;;;;;;;iBAGrB,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C,eAAO,MAAM,iBAAiB;;;;;iBAK5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,aAAa;;;;;;;;iBAKxB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEzD,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsCtB,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhD;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,MAAM,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IACnC;;;;OAIG;IACH,aAAa,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACjD;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IACpC;;;;OAIG;IACH,aAAa,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACrD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatman-media/kb",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Tenant-scoped Knowledge Base: hybrid retrieval (pgvector + BM25), ingest, answer pipeline, persona/skill composition. LLM I/O живёт в @chatman-media/llm-router.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"url": "https://github.com/chatman-media/lead-engine/issues"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@chatman-media/llm-router": "1.1.
|
|
59
|
+
"@chatman-media/llm-router": "1.1.1",
|
|
60
60
|
"unpdf": "^1.6.2",
|
|
61
61
|
"zod": "^4.4.1"
|
|
62
62
|
},
|
package/src/answer-types.ts
CHANGED
|
@@ -57,6 +57,12 @@ export interface AnswerInput {
|
|
|
57
57
|
* uses a calm FAQ-support block instead — answering questions without selling.
|
|
58
58
|
*/
|
|
59
59
|
supportPhase?: "docs" | "submitted";
|
|
60
|
+
/**
|
|
61
|
+
* Per-stage override (Phase 2 C-2): the lead's current funnel-stage goal/
|
|
62
|
+
* guidance from stage_definitions. Takes precedence over the Style's
|
|
63
|
+
* per-sales-stage config when composing the prompt.
|
|
64
|
+
*/
|
|
65
|
+
stageOverride?: { goal: string; guidance?: string };
|
|
60
66
|
/**
|
|
61
67
|
* Called after every `answerWithRag` or `answerWithRagStream` call with the
|
|
62
68
|
* final telemetry. Useful for logging, metrics, or A/B experiment recording
|
package/src/answer.ts
CHANGED
|
@@ -234,6 +234,7 @@ async function answerFromHits(opts: {
|
|
|
234
234
|
? { directorHooks: input.directorHooks }
|
|
235
235
|
: {}),
|
|
236
236
|
...(input.supportPhase ? { supportPhase: input.supportPhase } : {}),
|
|
237
|
+
...(input.stageOverride ? { stageOverride: input.stageOverride } : {}),
|
|
237
238
|
});
|
|
238
239
|
temperature = input.style.model.temperature;
|
|
239
240
|
} else {
|
|
@@ -503,6 +504,7 @@ export async function* answerWithRagStream(input: AnswerInput): AsyncIterable<st
|
|
|
503
504
|
? { directorHooks: input.directorHooks }
|
|
504
505
|
: {}),
|
|
505
506
|
...(input.supportPhase ? { supportPhase: input.supportPhase } : {}),
|
|
507
|
+
...(input.stageOverride ? { stageOverride: input.stageOverride } : {}),
|
|
506
508
|
});
|
|
507
509
|
temperature = input.style.model.temperature;
|
|
508
510
|
} else {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ChatClient, ChatMessage } from "@chatman-media/llm-router";
|
|
2
|
+
import { describe, expect, it } from "bun:test";
|
|
3
|
+
import { extractUserFacts, parseFactsFromLlmOutput } from "./extract-user-facts.ts";
|
|
4
|
+
|
|
5
|
+
const chat = (text: string): ChatClient => ({ complete: async () => text }) as unknown as ChatClient;
|
|
6
|
+
const chatThrows = (): ChatClient =>
|
|
7
|
+
({ complete: async () => { throw new Error("x"); } }) as unknown as ChatClient;
|
|
8
|
+
|
|
9
|
+
describe("parseFactsFromLlmOutput", () => {
|
|
10
|
+
it("нет {} → {}", () => {
|
|
11
|
+
expect(parseFactsFromLlmOutput("no json")).toEqual({});
|
|
12
|
+
});
|
|
13
|
+
it("битый JSON → {}", () => {
|
|
14
|
+
expect(parseFactsFromLlmOutput("{broken")).toEqual({});
|
|
15
|
+
});
|
|
16
|
+
it("массив → {}", () => {
|
|
17
|
+
expect(parseFactsFromLlmOutput("[1,2]")).toEqual({});
|
|
18
|
+
});
|
|
19
|
+
it("валидный объект + coercion числовых значений", () => {
|
|
20
|
+
expect(parseFactsFromLlmOutput('{"name":"Аня","age":23}')).toEqual({ name: "Аня", age: "23" });
|
|
21
|
+
});
|
|
22
|
+
it("пустые / слишком длинные значения и длинные ключи отбрасываются", () => {
|
|
23
|
+
const longVal = "x".repeat(300);
|
|
24
|
+
const longKey = "k".repeat(50);
|
|
25
|
+
const out = parseFactsFromLlmOutput(`{"city":" ","name":"Аня","big":"${longVal}","${longKey}":"v"}`);
|
|
26
|
+
expect(out).toEqual({ name: "Аня" });
|
|
27
|
+
});
|
|
28
|
+
it("code-fence/think снимаются", () => {
|
|
29
|
+
expect(parseFactsFromLlmOutput('```json\n{"name":"Ира"}\n```')).toEqual({ name: "Ира" });
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("extractUserFacts", () => {
|
|
34
|
+
const msgs: ChatMessage[] = [{ role: "user", content: "я Аня, мне 23" }];
|
|
35
|
+
it("нет user-сообщений → {} без LLM", async () => {
|
|
36
|
+
expect(await extractUserFacts({ messages: [{ role: "assistant", content: "hi" }], chat: chatThrows() })).toEqual({});
|
|
37
|
+
});
|
|
38
|
+
it("парсит факты из LLM", async () => {
|
|
39
|
+
expect(await extractUserFacts({ messages: msgs, chat: chat('{"name":"Аня","age":"23"}') })).toEqual({ name: "Аня", age: "23" });
|
|
40
|
+
});
|
|
41
|
+
it("LLM упал → {}", async () => {
|
|
42
|
+
expect(await extractUserFacts({ messages: msgs, chat: chatThrows() })).toEqual({});
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ChatClient } from "@chatman-media/llm-router";
|
|
2
|
+
import { afterEach, describe, expect, it } from "bun:test";
|
|
3
|
+
import { checkFacts, parseFactCheckResult } from "./fact-checker.ts";
|
|
4
|
+
|
|
5
|
+
const chat = (text: string): ChatClient => ({ complete: async () => text }) as unknown as ChatClient;
|
|
6
|
+
const chatThrows = (): ChatClient =>
|
|
7
|
+
({ complete: async () => { throw new Error("down"); } }) as unknown as ChatClient;
|
|
8
|
+
|
|
9
|
+
const savedEnv = process.env.RAG_FACT_CHECKER_FAIL_OPEN;
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
if (savedEnv === undefined) delete process.env.RAG_FACT_CHECKER_FAIL_OPEN;
|
|
12
|
+
else process.env.RAG_FACT_CHECKER_FAIL_OPEN = savedEnv;
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe("parseFactCheckResult", () => {
|
|
16
|
+
it("чистый JSON", () => {
|
|
17
|
+
expect(parseFactCheckResult('{"grounded":false,"vacancyOk":true,"reason":"x"}')).toEqual({
|
|
18
|
+
grounded: false,
|
|
19
|
+
vacancyOk: true,
|
|
20
|
+
reason: "x",
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
it("code-fence + think убираются", () => {
|
|
24
|
+
expect(parseFactCheckResult('<think>..</think>```json\n{"grounded":true,"vacancyOk":false}\n```')).toMatchObject({
|
|
25
|
+
grounded: true,
|
|
26
|
+
vacancyOk: false,
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
it("нет {} → OK (fail-open парсинга)", () => {
|
|
30
|
+
expect(parseFactCheckResult("no json")).toEqual({ grounded: true, vacancyOk: true });
|
|
31
|
+
});
|
|
32
|
+
it("битый JSON → OK", () => {
|
|
33
|
+
expect(parseFactCheckResult("{broken")).toEqual({ grounded: true, vacancyOk: true });
|
|
34
|
+
});
|
|
35
|
+
it("non-bool поля → дефолт true", () => {
|
|
36
|
+
expect(parseFactCheckResult('{"grounded":"yes"}')).toEqual({ grounded: true, vacancyOk: true });
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe("checkFacts", () => {
|
|
41
|
+
const base = { question: "q", answer: "Зарплата 1500$", context: "контекст 1500$" };
|
|
42
|
+
it("пустой ответ → OK без вызова LLM", async () => {
|
|
43
|
+
expect(await checkFacts({ ...base, answer: " ", chat: chatThrows() })).toEqual({ grounded: true, vacancyOk: true });
|
|
44
|
+
});
|
|
45
|
+
it("нет контекста и вакансий → OK", async () => {
|
|
46
|
+
expect(await checkFacts({ ...base, context: "", chat: chatThrows() })).toEqual({ grounded: true, vacancyOk: true });
|
|
47
|
+
});
|
|
48
|
+
it("есть контекст → парсит вердикт LLM", async () => {
|
|
49
|
+
const r = await checkFacts({ ...base, chat: chat('{"grounded":false,"vacancyOk":true,"reason":"выдумал"}') });
|
|
50
|
+
expect(r.grounded).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
it("vacancies-ветка промпта", async () => {
|
|
53
|
+
const r = await checkFacts({ ...base, vacanciesBlock: "Вакансия: 2000$", chat: chat('{"grounded":true,"vacancyOk":false,"reason":"не совпало"}') });
|
|
54
|
+
expect(r.vacancyOk).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
it("LLM упал → fail-closed по умолчанию", async () => {
|
|
57
|
+
delete process.env.RAG_FACT_CHECKER_FAIL_OPEN;
|
|
58
|
+
const r = await checkFacts({ ...base, chat: chatThrows() });
|
|
59
|
+
expect(r.grounded).toBe(false);
|
|
60
|
+
expect(r.reason).toContain("checker_error");
|
|
61
|
+
});
|
|
62
|
+
it("LLM упал + FAIL_OPEN=1 → пропускаем", async () => {
|
|
63
|
+
process.env.RAG_FACT_CHECKER_FAIL_OPEN = "1";
|
|
64
|
+
expect(await checkFacts({ ...base, chat: chatThrows() })).toEqual({ grounded: true, vacancyOk: true });
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ChatClient } from "@chatman-media/llm-router";
|
|
2
|
+
import { describe, expect, it } from "bun:test";
|
|
3
|
+
import { gradeSkills, parseSlugList } from "./grade-skills.ts";
|
|
4
|
+
|
|
5
|
+
const chat = (text: string): ChatClient => ({ complete: async () => text }) as unknown as ChatClient;
|
|
6
|
+
const chatThrows = (): ChatClient =>
|
|
7
|
+
({ complete: async () => { throw new Error("x"); } }) as unknown as ChatClient;
|
|
8
|
+
const ALLOWED = ["mirroring", "scarcity-spots-left", "tactical-empathy"] as const;
|
|
9
|
+
|
|
10
|
+
describe("parseSlugList", () => {
|
|
11
|
+
it("пусто → []", () => {
|
|
12
|
+
expect(parseSlugList("", ALLOWED)).toEqual([]);
|
|
13
|
+
});
|
|
14
|
+
it("JSON-массив, фильтр по allowed", () => {
|
|
15
|
+
expect(parseSlugList('["mirroring","unknown-skill","tactical-empathy"]', ALLOWED)).toEqual(["mirroring", "tactical-empathy"]);
|
|
16
|
+
});
|
|
17
|
+
it("code-fenced JSON", () => {
|
|
18
|
+
expect(parseSlugList('```json\n["scarcity-spots-left"]\n```', ALLOWED)).toEqual(["scarcity-spots-left"]);
|
|
19
|
+
});
|
|
20
|
+
it("comma/newline текст → fallback-парсинг", () => {
|
|
21
|
+
expect(parseSlugList("mirroring, tactical-empathy", ALLOWED)).toEqual(["mirroring", "tactical-empathy"]);
|
|
22
|
+
});
|
|
23
|
+
it("всё неразрешённое → []", () => {
|
|
24
|
+
expect(parseSlugList('["foo","bar"]', ALLOWED)).toEqual([]);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("gradeSkills", () => {
|
|
29
|
+
const base = { question: "q", reply: "r", availableSlugs: ALLOWED };
|
|
30
|
+
it("пустой availableSlugs → [] без LLM", async () => {
|
|
31
|
+
expect(await gradeSkills({ ...base, availableSlugs: [], chat: chatThrows() })).toEqual([]);
|
|
32
|
+
});
|
|
33
|
+
it("парсит и фильтрует ответ LLM", async () => {
|
|
34
|
+
expect(await gradeSkills({ ...base, chat: chat('["mirroring","x"]') })).toEqual(["mirroring"]);
|
|
35
|
+
});
|
|
36
|
+
it("LLM упал → [] (failure-soft)", async () => {
|
|
37
|
+
expect(await gradeSkills({ ...base, chat: chatThrows() })).toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Unit tests for kb composeSystemPrompt — the prompt builder used by the reply
|
|
2
|
+
// pipeline (answer.ts). Focus: Phase 2 C-2 `stageOverride` precedence (the
|
|
3
|
+
// lead's funnel-stage goal/guidance wins over the Style's per-sales-stage cfg).
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it } from "bun:test";
|
|
6
|
+
import { composeSystemPrompt } from "./prompt.ts";
|
|
7
|
+
import type { Style } from "./styles.ts";
|
|
8
|
+
|
|
9
|
+
const baseStyle: Style = {
|
|
10
|
+
slug: "t",
|
|
11
|
+
displayName: "T",
|
|
12
|
+
persona: { name: "Алекс", role: "human" },
|
|
13
|
+
voice: { tone: "friendly", language: "ru", forbid: [] },
|
|
14
|
+
framework: "SPIN",
|
|
15
|
+
hooks: [],
|
|
16
|
+
stages: { qualify: { goal: "STYLE_GOAL", groundingRequired: false } },
|
|
17
|
+
fewShot: [],
|
|
18
|
+
guardrails: { noMinors: true, botDisclosureOnDirectQuestion: true, forbiddenTopics: [] },
|
|
19
|
+
model: { id: "x", temperature: 0.5, maxTokens: 100 },
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
describe("composeSystemPrompt — stageOverride (Phase 2 C-2)", () => {
|
|
23
|
+
it("без override → goal берётся из Style", () => {
|
|
24
|
+
const p = composeSystemPrompt(baseStyle, "qualify");
|
|
25
|
+
expect(p).toContain("STYLE_GOAL");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("stageOverride имеет приоритет над Style для goal и guidance", () => {
|
|
29
|
+
const p = composeSystemPrompt(baseStyle, "qualify", null, {
|
|
30
|
+
stageOverride: { goal: "OVERRIDE_GOAL", guidance: "OVERRIDE_GUIDE" },
|
|
31
|
+
});
|
|
32
|
+
expect(p).toContain("OVERRIDE_GOAL");
|
|
33
|
+
expect(p).toContain("OVERRIDE_GUIDE");
|
|
34
|
+
expect(p).not.toContain("STYLE_GOAL");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("stageOverride работает для стадии без конфига в Style", () => {
|
|
38
|
+
// "close" нет в baseStyle.stages → раньше был бы generic-блок; теперь override.
|
|
39
|
+
const p = composeSystemPrompt(baseStyle, "close", null, {
|
|
40
|
+
stageOverride: { goal: "CLOSE_GOAL" },
|
|
41
|
+
});
|
|
42
|
+
expect(p).toContain("CLOSE_GOAL");
|
|
43
|
+
});
|
|
44
|
+
});
|
package/src/prompt.ts
CHANGED
|
@@ -121,11 +121,15 @@ export function composeSystemPrompt(
|
|
|
121
121
|
skillsForStage.map((s) => `- ${s.displayName} — ${s.promptFragment}`).join("\n")
|
|
122
122
|
: "";
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
// Per-stage instructions: the lead's funnel-stage override (Phase 2) takes
|
|
125
|
+
// precedence over the Style's per-sales-stage config; grounding stays from style.
|
|
126
|
+
const effGoal = options.stageOverride?.goal ?? stageCfg?.goal;
|
|
127
|
+
const effGuidance = options.stageOverride?.guidance ?? stageCfg?.guidance;
|
|
128
|
+
const stageBlock = effGoal
|
|
125
129
|
? `ТЕКУЩИЙ ЭТАП: ${stage.toUpperCase()}.\n` +
|
|
126
|
-
`ЦЕЛЬ ЭТАПА: ${
|
|
127
|
-
(
|
|
128
|
-
(stageCfg
|
|
130
|
+
`ЦЕЛЬ ЭТАПА: ${effGoal}.` +
|
|
131
|
+
(effGuidance ? `\nКАК: ${effGuidance}` : "") +
|
|
132
|
+
(stageCfg?.groundingRequired
|
|
129
133
|
? `\nGROUNDING: на этом этапе все конкретные факты (цифры, суммы, сроки) бери ТОЛЬКО из секции KB CONTEXT ниже. Если её нет или нужного факта в ней нет — не выдумывай, скажи что уточнишь.`
|
|
130
134
|
: "")
|
|
131
135
|
: `ТЕКУЩИЙ ЭТАП: ${stage}. (Специфических правил для этапа нет — используй общий стиль.)`;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ChatClient } from "@chatman-media/llm-router";
|
|
2
|
+
import { describe, expect, it } from "bun:test";
|
|
3
|
+
import { parseReflection, verifyAnswer } from "./reflect.ts";
|
|
4
|
+
|
|
5
|
+
const chat = (text: string): ChatClient => ({ complete: async () => text }) as unknown as ChatClient;
|
|
6
|
+
const chatThrows = (): ChatClient =>
|
|
7
|
+
({ complete: async () => { throw new Error("x"); } }) as unknown as ChatClient;
|
|
8
|
+
|
|
9
|
+
describe("parseReflection", () => {
|
|
10
|
+
it("нет {} → grounded:true", () => {
|
|
11
|
+
expect(parseReflection("plain")).toEqual({ grounded: true });
|
|
12
|
+
});
|
|
13
|
+
it("битый JSON → true", () => {
|
|
14
|
+
expect(parseReflection("{nope")).toEqual({ grounded: true });
|
|
15
|
+
});
|
|
16
|
+
it("grounded non-bool → true", () => {
|
|
17
|
+
expect(parseReflection('{"grounded":1}')).toEqual({ grounded: true });
|
|
18
|
+
});
|
|
19
|
+
it("grounded:true → {true}", () => {
|
|
20
|
+
expect(parseReflection('{"grounded":true}')).toEqual({ grounded: true });
|
|
21
|
+
});
|
|
22
|
+
it("grounded:false → {false, reason}", () => {
|
|
23
|
+
expect(parseReflection('{"grounded":false,"reason":"выдумал город"}')).toEqual({ grounded: false, reason: "выдумал город" });
|
|
24
|
+
});
|
|
25
|
+
it("grounded:false без reason → unknown", () => {
|
|
26
|
+
expect(parseReflection('{"grounded":false}')).toEqual({ grounded: false, reason: "unknown" });
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("verifyAnswer", () => {
|
|
31
|
+
const base = { question: "q", answer: "Дубай 1500$", context: "Дубай 1500$" };
|
|
32
|
+
it("пустой ответ → grounded:true", async () => {
|
|
33
|
+
expect(await verifyAnswer({ ...base, answer: " ", chat: chatThrows() })).toEqual({ grounded: true });
|
|
34
|
+
});
|
|
35
|
+
it("нет контекста → grounded:true", async () => {
|
|
36
|
+
expect(await verifyAnswer({ ...base, context: "", chat: chatThrows() })).toEqual({ grounded: true });
|
|
37
|
+
});
|
|
38
|
+
it("LLM-вердикт false → {false,reason}", async () => {
|
|
39
|
+
expect(await verifyAnswer({ ...base, chat: chat('{"grounded":false,"reason":"r"}') })).toMatchObject({ grounded: false });
|
|
40
|
+
});
|
|
41
|
+
it("LLM упал → grounded:true (fail-open)", async () => {
|
|
42
|
+
expect(await verifyAnswer({ ...base, chat: chatThrows() })).toEqual({ grounded: true });
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { ChatClient, ChatMessage } from "@chatman-media/llm-router";
|
|
2
|
+
import { describe, expect, it } from "bun:test";
|
|
3
|
+
import { questionNeedsRewrite, rewriteQuery, sanitizeRewritten } from "./rewrite-query.ts";
|
|
4
|
+
|
|
5
|
+
const chat = (text: string): ChatClient => ({ complete: async () => text }) as unknown as ChatClient;
|
|
6
|
+
const chatThrows = (): ChatClient =>
|
|
7
|
+
({ complete: async () => { throw new Error("x"); } }) as unknown as ChatClient;
|
|
8
|
+
const hist: ChatMessage[] = [{ role: "assistant", content: "в Дубае платят 1500" }];
|
|
9
|
+
|
|
10
|
+
describe("questionNeedsRewrite", () => {
|
|
11
|
+
it("пустой → false", () => {
|
|
12
|
+
expect(questionNeedsRewrite(" ", hist)).toBe(false);
|
|
13
|
+
});
|
|
14
|
+
it("нет истории → false", () => {
|
|
15
|
+
expect(questionNeedsRewrite("а там как?", [])).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
it("короткий (<=4 слова) с историей → true", () => {
|
|
18
|
+
expect(questionNeedsRewrite("а виза как?", hist)).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
it("дейктик-маркер → true", () => {
|
|
21
|
+
expect(questionNeedsRewrite("сколько стоит это оформление документов в итоге", hist)).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
it("follow-up союз в начале → true", () => {
|
|
24
|
+
expect(questionNeedsRewrite("или есть другие варианты работы там сейчас", hist)).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
it("длинный самостоятельный → false", () => {
|
|
27
|
+
expect(questionNeedsRewrite("сколько платят моделям в Дубае за месяц работы по контракту", hist)).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe("sanitizeRewritten", () => {
|
|
32
|
+
it("снимает think/fence/«ответ:» и берёт первую строку", () => {
|
|
33
|
+
expect(sanitizeRewritten('<think>..</think>ответ: какие условия в Дубае\nещё текст', "fb", 200)).toBe("какие условия в Дубае");
|
|
34
|
+
});
|
|
35
|
+
it("пусто/мусор → fallback", () => {
|
|
36
|
+
expect(sanitizeRewritten(" \n ", "ОРИГИНАЛ", 200)).toBe("ОРИГИНАЛ");
|
|
37
|
+
});
|
|
38
|
+
it("обрезка по maxLength", () => {
|
|
39
|
+
expect(sanitizeRewritten("абвгде", "fb", 3)).toBe("абв");
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe("rewriteQuery", () => {
|
|
44
|
+
it("пустой вопрос → как есть", async () => {
|
|
45
|
+
expect(await rewriteQuery({ question: " ", chat: chatThrows() })).toBe("");
|
|
46
|
+
});
|
|
47
|
+
it("не нужен рерайт (нет истории) → оригинал без вызова LLM", async () => {
|
|
48
|
+
expect(await rewriteQuery({ question: "а там?", chat: chatThrows() })).toBe("а там?");
|
|
49
|
+
});
|
|
50
|
+
it("нужен рерайт → sanitized из LLM", async () => {
|
|
51
|
+
expect(await rewriteQuery({ question: "а виза?", history: hist, chat: chat("как оформляется виза") })).toBe("как оформляется виза");
|
|
52
|
+
});
|
|
53
|
+
it("LLM упал → оригинал", async () => {
|
|
54
|
+
expect(await rewriteQuery({ question: "а виза?", history: hist, chat: chatThrows() })).toBe("а виза?");
|
|
55
|
+
});
|
|
56
|
+
});
|
package/src/styles.ts
CHANGED
|
@@ -135,4 +135,10 @@ export interface ComposeOptions {
|
|
|
135
135
|
* block instead. `docs` = collecting their documents, `submitted` = filed.
|
|
136
136
|
*/
|
|
137
137
|
supportPhase?: "docs" | "submitted";
|
|
138
|
+
/**
|
|
139
|
+
* Per-stage override (Phase 2): goal/guidance from the lead's current funnel
|
|
140
|
+
* stage_definition. Takes precedence over `style.stages[stage]` for the stage
|
|
141
|
+
* block. Lets AI-built funnels carry per-stage instructions.
|
|
142
|
+
*/
|
|
143
|
+
stageOverride?: { goal: string; guidance?: string };
|
|
138
144
|
}
|