@paro.io/expert-shared-components 1.14.55 → 1.14.56
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.
|
@@ -25,49 +25,46 @@ function positionStrengthLabel(positionStrength) {
|
|
|
25
25
|
return { label: positionStrength, color: "#9498B8", dots: 2 };
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
|
-
* Split engagement_recommendation
|
|
29
|
-
* - savingsSection: sentences/paragraphs that contain dollar figures or formula references
|
|
30
|
-
* → shown as "HOW SAVINGS BREAK DOWN"
|
|
31
|
-
* - cpaSection: the remaining implementation/CPA-action paragraphs
|
|
32
|
-
* → shown as "CPA ENGAGEMENT NOTES"
|
|
28
|
+
* Split engagement_recommendation at the implementation/action boundary.
|
|
33
29
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
30
|
+
* The LLM writes engagement_recommendation as a single paragraph covering both
|
|
31
|
+
* the savings math AND the CPA implementation steps. We split at the first
|
|
32
|
+
* implementation keyword so the savings math shows under "HOW SAVINGS BREAK DOWN"
|
|
33
|
+
* and the CPA action steps show under "CPA ENGAGEMENT NOTES".
|
|
34
|
+
*
|
|
35
|
+
* Heuristic: split at "Implementation:", "CPA should", "CPA must", or numbered
|
|
36
|
+
* action lists "(1)". If no split point found, entire text goes to savings section.
|
|
36
37
|
*/
|
|
37
38
|
function splitEngagementText(text) {
|
|
38
39
|
if (!text)
|
|
39
40
|
return { savingsSection: "", cpaSection: "" };
|
|
40
|
-
//
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const hasPercent = /\d+\.?\d*%/.test(para);
|
|
48
|
-
const hasCpaKeyword = /\bCPA\b|actuar|implement|deadline|establish|file|form\s+\d|verify|engage|model|recommend/i.test(para);
|
|
49
|
-
if ((hasDollar || hasFormula || hasPercent) && !hasCpaKeyword) {
|
|
50
|
-
savingsParas.push(para);
|
|
51
|
-
}
|
|
52
|
-
else if (savingsParas.length === 0) {
|
|
53
|
-
// First para with dollar amounts seeds savings section even if it has CPA keywords
|
|
54
|
-
if (hasDollar || hasFormula)
|
|
55
|
-
savingsParas.push(para);
|
|
56
|
-
else
|
|
57
|
-
cpaParas.push(para);
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
cpaParas.push(para);
|
|
61
|
-
}
|
|
41
|
+
// Try splitting on double-newline first (well-structured LLM output)
|
|
42
|
+
const paraBreak = text.indexOf("\n\n");
|
|
43
|
+
if (paraBreak !== -1) {
|
|
44
|
+
return {
|
|
45
|
+
savingsSection: text.slice(0, paraBreak).trim(),
|
|
46
|
+
cpaSection: text.slice(paraBreak).trim(),
|
|
47
|
+
};
|
|
62
48
|
}
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
49
|
+
// Single-paragraph: find the first implementation boundary
|
|
50
|
+
const splitPatterns = [
|
|
51
|
+
/\s+Implementation:\s+/i,
|
|
52
|
+
/\s+CPA should\s+/i,
|
|
53
|
+
/\s+CPA must\s+/i,
|
|
54
|
+
/\s+To implement:\s+/i,
|
|
55
|
+
/\s+\(1\)\s+/, // numbered list start
|
|
56
|
+
];
|
|
57
|
+
for (const pattern of splitPatterns) {
|
|
58
|
+
const match = text.search(pattern);
|
|
59
|
+
if (match > 80) { // only split if there's meaningful content before it
|
|
60
|
+
return {
|
|
61
|
+
savingsSection: text.slice(0, match).trim(),
|
|
62
|
+
cpaSection: text.slice(match).trim(),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
66
65
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
cpaSection: cpaParas.join("\n\n"),
|
|
70
|
-
};
|
|
66
|
+
// No clean split point — show everything as savings breakdown, nothing as CPA notes
|
|
67
|
+
return { savingsSection: text, cpaSection: "" };
|
|
71
68
|
}
|
|
72
69
|
function SourceDocRows({ docs, trace }) {
|
|
73
70
|
if (docs && docs.length > 0) {
|
|
@@ -27,6 +27,7 @@ exports.TaxAxisDashboard = TaxAxisDashboard;
|
|
|
27
27
|
const react_1 = __importStar(require("react"));
|
|
28
28
|
const data_1 = require("../../lib/data");
|
|
29
29
|
const compute_1 = require("../../lib/compute");
|
|
30
|
+
const useEngineOutput_1 = require("../../lib/adapters/useEngineOutput");
|
|
30
31
|
const TaxAxisButton_1 = require("../shared/TaxAxisButton");
|
|
31
32
|
const TaxAxisBadge_1 = require("../shared/TaxAxisBadge");
|
|
32
33
|
const DashboardSummary_1 = require("./DashboardSummary");
|
|
@@ -163,8 +164,29 @@ function TaxAxisDashboard({ profile, llmResult, parsedDocuments, onDownloadClien
|
|
|
163
164
|
// Extracted documents: live from session if provided, otherwise empty (no mock fallback)
|
|
164
165
|
const extractionDocs = parsedDocuments !== null && parsedDocuments !== void 0 ? parsedDocuments : [];
|
|
165
166
|
// ─── Derived data ──────────────────────────────────────────────
|
|
166
|
-
|
|
167
|
-
|
|
167
|
+
// Prefer useEngineOutput (full rich data: sourceDocuments, positionStrength,
|
|
168
|
+
// clientBrief from why_it_applies, etc.) over the thin mapLlmToStrategies fallback.
|
|
169
|
+
// engineOutput is at llmResult.engineOutput or llmResult.rawOutput (both present).
|
|
170
|
+
const engineOutputForAdapter = (0, react_1.useMemo)(() => { var _a, _b; return (_b = (_a = llmResult === null || llmResult === void 0 ? void 0 : llmResult.engineOutput) !== null && _a !== void 0 ? _a : llmResult === null || llmResult === void 0 ? void 0 : llmResult.rawOutput) !== null && _b !== void 0 ? _b : null; }, [llmResult]);
|
|
171
|
+
const adapted = (0, useEngineOutput_1.useEngineOutput)(engineOutputForAdapter);
|
|
172
|
+
const llmStrategies = (0, react_1.useMemo)(() => {
|
|
173
|
+
var _a;
|
|
174
|
+
if (!hasLlm)
|
|
175
|
+
return [];
|
|
176
|
+
// Use adapted strategies when available — they carry the full engine data.
|
|
177
|
+
if ((_a = adapted === null || adapted === void 0 ? void 0 : adapted.strategies) === null || _a === void 0 ? void 0 : _a.length)
|
|
178
|
+
return adapted.strategies;
|
|
179
|
+
// Fallback to thin mapping for old payload shapes without engineOutput.
|
|
180
|
+
return mapLlmToStrategies(llmResult);
|
|
181
|
+
}, [hasLlm, adapted, llmResult]);
|
|
182
|
+
const llmComputed = (0, react_1.useMemo)(() => {
|
|
183
|
+
var _a;
|
|
184
|
+
if (!hasLlm)
|
|
185
|
+
return new Map();
|
|
186
|
+
if ((_a = adapted === null || adapted === void 0 ? void 0 : adapted.computedMap) === null || _a === void 0 ? void 0 : _a.size)
|
|
187
|
+
return adapted.computedMap;
|
|
188
|
+
return buildLlmComputed(llmStrategies);
|
|
189
|
+
}, [hasLlm, adapted, llmStrategies]);
|
|
168
190
|
const computed = (0, react_1.useMemo)(() => (hasLlm ? llmComputed : (0, compute_1.computeAllStrategies)(profile)), [hasLlm, llmComputed, profile]);
|
|
169
191
|
const dashEligible = (0, react_1.useMemo)(() => (hasLlm ? llmStrategies : (0, compute_1.filterEligibleStrategies)(profile)), [hasLlm, llmStrategies, profile]);
|
|
170
192
|
const maxSavings = Math.max(...dashEligible.map((s) => { var _a, _b; return (_b = (_a = computed.get(s.rank)) === null || _a === void 0 ? void 0 : _a.hi) !== null && _b !== void 0 ? _b : s.hi; }), 1);
|