@memberjunction/ng-dashboards 6.1.3 → 6.2.0-edge.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.
Files changed (99) hide show
  1. package/dist/AI/components/vectors/vector-management-resource.component.d.ts +6 -1
  2. package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -1
  3. package/dist/AI/components/vectors/vector-management-resource.component.js +99 -45
  4. package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
  5. package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.d.ts +4 -0
  6. package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.d.ts.map +1 -1
  7. package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.js +16 -6
  8. package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.js.map +1 -1
  9. package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +4 -0
  10. package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
  11. package/dist/DataExplorer/data-explorer-dashboard.component.js +14 -4
  12. package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
  13. package/dist/DataExplorer/services/explorer-state.service.js +2 -2
  14. package/dist/DataExplorer/services/explorer-state.service.js.map +1 -1
  15. package/dist/DevTools/graphql-console.component.d.ts.map +1 -1
  16. package/dist/DevTools/graphql-console.component.js +2 -0
  17. package/dist/DevTools/graphql-console.component.js.map +1 -1
  18. package/dist/Integration/components/connections/connections.component.d.ts.map +1 -1
  19. package/dist/Integration/components/connections/connections.component.js +2 -0
  20. package/dist/Integration/components/connections/connections.component.js.map +1 -1
  21. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.d.ts +4 -12
  22. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.d.ts.map +1 -1
  23. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js +39 -78
  24. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js.map +1 -1
  25. package/dist/Lists/components/lists-browse-resource.component.d.ts +3 -0
  26. package/dist/Lists/components/lists-browse-resource.component.d.ts.map +1 -1
  27. package/dist/Lists/components/lists-browse-resource.component.js +7 -6
  28. package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
  29. package/dist/Lists/components/lists-my-lists-resource.component.d.ts +3 -0
  30. package/dist/Lists/components/lists-my-lists-resource.component.d.ts.map +1 -1
  31. package/dist/Lists/components/lists-my-lists-resource.component.js +7 -6
  32. package/dist/Lists/components/lists-my-lists-resource.component.js.map +1 -1
  33. package/dist/PredictiveStudio/at-risk.view-models.d.ts +41 -3
  34. package/dist/PredictiveStudio/at-risk.view-models.d.ts.map +1 -1
  35. package/dist/PredictiveStudio/at-risk.view-models.js +99 -6
  36. package/dist/PredictiveStudio/at-risk.view-models.js.map +1 -1
  37. package/dist/PredictiveStudio/components/ps-catalog.component.d.ts +4 -1
  38. package/dist/PredictiveStudio/components/ps-catalog.component.d.ts.map +1 -1
  39. package/dist/PredictiveStudio/components/ps-catalog.component.js +61 -37
  40. package/dist/PredictiveStudio/components/ps-catalog.component.js.map +1 -1
  41. package/dist/PredictiveStudio/components/ps-experiments.component.d.ts +12 -0
  42. package/dist/PredictiveStudio/components/ps-experiments.component.d.ts.map +1 -1
  43. package/dist/PredictiveStudio/components/ps-experiments.component.js +426 -184
  44. package/dist/PredictiveStudio/components/ps-experiments.component.js.map +1 -1
  45. package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.d.ts +84 -0
  46. package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.d.ts.map +1 -0
  47. package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.js +1397 -0
  48. package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.js.map +1 -0
  49. package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts +66 -34
  50. package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts.map +1 -1
  51. package/dist/PredictiveStudio/components/ps-pipelines.component.js +2456 -743
  52. package/dist/PredictiveStudio/components/ps-pipelines.component.js.map +1 -1
  53. package/dist/PredictiveStudio/components/ps-predictions-grid.component.d.ts +194 -0
  54. package/dist/PredictiveStudio/components/ps-predictions-grid.component.d.ts.map +1 -0
  55. package/dist/PredictiveStudio/components/ps-predictions-grid.component.js +1902 -0
  56. package/dist/PredictiveStudio/components/ps-predictions-grid.component.js.map +1 -0
  57. package/dist/PredictiveStudio/components/ps-production.component.d.ts +17 -1
  58. package/dist/PredictiveStudio/components/ps-production.component.d.ts.map +1 -1
  59. package/dist/PredictiveStudio/components/ps-production.component.js +653 -372
  60. package/dist/PredictiveStudio/components/ps-production.component.js.map +1 -1
  61. package/dist/PredictiveStudio/components/ps-registry.component.d.ts +21 -61
  62. package/dist/PredictiveStudio/components/ps-registry.component.d.ts.map +1 -1
  63. package/dist/PredictiveStudio/components/ps-registry.component.js +214 -681
  64. package/dist/PredictiveStudio/components/ps-registry.component.js.map +1 -1
  65. package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.d.ts +49 -0
  66. package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.d.ts.map +1 -0
  67. package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.js +658 -0
  68. package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.js.map +1 -0
  69. package/dist/PredictiveStudio/predictive-studio-score-history.service.d.ts +42 -0
  70. package/dist/PredictiveStudio/predictive-studio-score-history.service.d.ts.map +1 -0
  71. package/dist/PredictiveStudio/predictive-studio-score-history.service.js +90 -0
  72. package/dist/PredictiveStudio/predictive-studio-score-history.service.js.map +1 -0
  73. package/dist/PredictiveStudio/predictive-studio.view-models.d.ts +14 -2
  74. package/dist/PredictiveStudio/predictive-studio.view-models.d.ts.map +1 -1
  75. package/dist/PredictiveStudio/predictive-studio.view-models.js +41 -5
  76. package/dist/PredictiveStudio/predictive-studio.view-models.js.map +1 -1
  77. package/dist/PredictiveStudio/resources/ps-models-resource.component.d.ts +9 -1
  78. package/dist/PredictiveStudio/resources/ps-models-resource.component.d.ts.map +1 -1
  79. package/dist/PredictiveStudio/resources/ps-models-resource.component.js +178 -65
  80. package/dist/PredictiveStudio/resources/ps-models-resource.component.js.map +1 -1
  81. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts +9 -1
  82. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts.map +1 -1
  83. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js +307 -369
  84. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js.map +1 -1
  85. package/dist/PredictiveStudio/resources/ps-resource-base.js +1 -1
  86. package/dist/PredictiveStudio/resources/ps-resource-base.js.map +1 -1
  87. package/dist/PredictiveStudio/resources/ps-studio-resource.component.d.ts +17 -1
  88. package/dist/PredictiveStudio/resources/ps-studio-resource.component.d.ts.map +1 -1
  89. package/dist/PredictiveStudio/resources/ps-studio-resource.component.js +309 -138
  90. package/dist/PredictiveStudio/resources/ps-studio-resource.component.js.map +1 -1
  91. package/dist/VersionHistory/components/graph-resource.component.d.ts +1 -0
  92. package/dist/VersionHistory/components/graph-resource.component.d.ts.map +1 -1
  93. package/dist/VersionHistory/components/graph-resource.component.js +6 -5
  94. package/dist/VersionHistory/components/graph-resource.component.js.map +1 -1
  95. package/dist/predictive-studio-dashboards.module.d.ts +10 -8
  96. package/dist/predictive-studio-dashboards.module.d.ts.map +1 -1
  97. package/dist/predictive-studio-dashboards.module.js +13 -3
  98. package/dist/predictive-studio-dashboards.module.js.map +1 -1
  99. package/package.json +64 -63
@@ -8,14 +8,16 @@
8
8
  *
9
9
  * Framework-free + deterministic → unit-tested with no Angular.
10
10
  */
11
+ import { resolveScoreBand, } from '@memberjunction/predictive-studio-core';
11
12
  /** Parse + humanize the raw per-record `drivers` (post-preprocessing `feature`/`value`) into {@link RowDriver}s. */
12
- function parseRowDrivers(raw) {
13
+ export function parseRowDrivers(raw) {
13
14
  if (!Array.isArray(raw) || raw.length === 0)
14
15
  return null;
15
16
  const out = [];
16
17
  for (const d of raw) {
17
18
  const feature = typeof d?.feature === 'string' ? d.feature : '';
18
- const value = typeof d?.value === 'number' ? d.value : NaN;
19
+ const rawVal = typeof d?.value === 'number' ? d.value : typeof d?.importance === 'number' ? d.importance : typeof d?.weight === 'number' ? d.weight : NaN;
20
+ const value = Number(rawVal);
19
21
  if (!feature || !Number.isFinite(value))
20
22
  continue;
21
23
  // Keep the one-hot category: for a per-record "why", the category IS the story — "Membership Type =
@@ -34,6 +36,9 @@ export function labelFromRecord(row) {
34
36
  if (!row)
35
37
  return null;
36
38
  const str = (v) => (typeof v === 'string' ? v.trim() : '');
39
+ const member = str(row['MemberName'] || row['Member'] || row['FullName']);
40
+ if (member)
41
+ return member;
37
42
  const name = str(row['Name']);
38
43
  if (name)
39
44
  return name;
@@ -52,12 +57,73 @@ export function labelFromRecord(row) {
52
57
  }
53
58
  return null;
54
59
  }
60
+ /**
61
+ * Robustly inspects model metadata and sample scoring payloads to determine:
62
+ * 1. Whether this is a renewal/retention/churn model (`isRenewalModel`)
63
+ * 2. Whether the raw payload `score` represents adverse lapse risk (P(Lapse)) or positive outcome (P(Renewed))
64
+ */
65
+ export function resolveRenewalPolarity(rawPayloads, targetVariable, modelName) {
66
+ const target = (targetVariable ?? '').toLowerCase();
67
+ const name = (modelName ?? '').toLowerCase();
68
+ const isTargetRenewal = target.includes('renew') ||
69
+ target.includes('lapse') ||
70
+ target.includes('retention') ||
71
+ target.includes('churn') ||
72
+ target === 'status' ||
73
+ name.includes('renew') ||
74
+ name.includes('retention') ||
75
+ name.includes('churn');
76
+ let hasRenewalKeywords = false;
77
+ const sampleScoresForRenewed = [];
78
+ for (const p of rawPayloads.slice(0, 50)) {
79
+ if (!p)
80
+ continue;
81
+ if (p.includes('"Renewed"') ||
82
+ p.includes('"renewed"') ||
83
+ p.includes('"Lapsed"') ||
84
+ p.includes('"lapsed"') ||
85
+ p.includes('Renewal Risk')) {
86
+ hasRenewalKeywords = true;
87
+ }
88
+ try {
89
+ const parsed = JSON.parse(p);
90
+ const out = (parsed.output ?? parsed);
91
+ if (typeof out.score === 'number' && typeof out.class === 'string') {
92
+ const cls = out.class.toLowerCase();
93
+ if (cls === 'renewed' || cls === 'active') {
94
+ sampleScoresForRenewed.push(out.score);
95
+ }
96
+ }
97
+ }
98
+ catch {
99
+ // skip invalid json
100
+ }
101
+ }
102
+ const isRenewalModel = isTargetRenewal || hasRenewalKeywords;
103
+ if (!isRenewalModel) {
104
+ return { isRenewalModel: false, scoreIsLapseRisk: false };
105
+ }
106
+ if (sampleScoresForRenewed.length > 0) {
107
+ const avg = sampleScoresForRenewed.reduce((a, b) => a + b, 0) / sampleScoresForRenewed.length;
108
+ // If members predicted 'Renewed' have small scores (< 0.5), the score represents Lapse Risk (P(Lapse))
109
+ return { isRenewalModel: true, scoreIsLapseRisk: avg < 0.5 };
110
+ }
111
+ // Fallback: target or name containing "risk", "lapse", or "churn" outputs lapse probability
112
+ const scoreIsLapseRisk = target.includes('risk') ||
113
+ target.includes('lapse') ||
114
+ target.includes('churn') ||
115
+ name.includes('risk') ||
116
+ name.includes('churn');
117
+ return { isRenewalModel: true, scoreIsLapseRisk };
118
+ }
55
119
  function bandFor(score) {
56
120
  return score >= 0.7 ? 'high' : score >= 0.4 ? 'medium' : 'low';
57
121
  }
58
122
  /** Parse + rank the per-record predictions into the at-risk list (highest risk first). */
59
- export function parseAtRiskRows(details) {
123
+ export function parseAtRiskRows(details, options) {
60
124
  const rows = [];
125
+ const isInverted = options?.invertedRisk === true;
126
+ const outcomeConfig = options?.outcomeConfig;
61
127
  for (const d of details) {
62
128
  if (!d.ResultPayload)
63
129
  continue;
@@ -73,17 +139,44 @@ export function parseAtRiskRows(details) {
73
139
  if (typeof p.score !== 'number' || !Number.isFinite(p.score))
74
140
  continue;
75
141
  const score = p.score;
142
+ const effectiveRisk = isInverted ? Math.max(0, Math.min(1, 1 - score)) : score;
143
+ const riskPct = Math.round(effectiveRisk * 100);
144
+ let bandKey;
145
+ let status = p.status;
146
+ let badgeColor = p.badgeColor;
147
+ let icon = p.icon;
148
+ if (outcomeConfig) {
149
+ const resolved = resolveScoreBand(score, outcomeConfig);
150
+ if (resolved) {
151
+ bandKey = resolved.Key;
152
+ status = status ?? resolved.Label;
153
+ badgeColor = badgeColor ?? resolved.BadgeColor;
154
+ icon = icon ?? resolved.Icon;
155
+ }
156
+ else {
157
+ bandKey = p.band ?? bandFor(effectiveRisk);
158
+ }
159
+ }
160
+ else if (p.band) {
161
+ bandKey = p.band;
162
+ }
163
+ else {
164
+ bandKey = bandFor(effectiveRisk);
165
+ }
76
166
  rows.push({
77
167
  recordId: d.recordId,
78
168
  label: null,
79
169
  score,
80
- riskPct: Math.round(score * 100),
170
+ riskPct,
81
171
  class: p.class ?? null,
82
- band: bandFor(score),
172
+ band: bandKey,
173
+ status,
174
+ badgeColor,
175
+ icon,
83
176
  drivers: parseRowDrivers(p.drivers),
84
177
  });
85
178
  }
86
- return rows.sort((a, b) => b.score - a.score);
179
+ return rows.sort((a, b) => b.riskPct - a.riskPct);
87
180
  }
88
181
  /**
89
182
  * The top plain-language drivers for the whole prediction, from the model's global feature importance.
@@ -1 +1 @@
1
- {"version":3,"file":"at-risk.view-models.js","sourceRoot":"","sources":["../../src/PredictiveStudio/at-risk.view-models.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure derivations for the business workspace's **ranked at-risk list** — turning a model's per-record\n * scoring results (`MJ: Process Run Details` payloads) into a \"who to focus on\" list sorted by risk, and\n * the model's global feature importance into plain-language \"what's driving this\" drivers.\n *\n * Honest about the gap: MJ stores only GLOBAL feature importance (not per-record/SHAP), so we surface the\n * top global drivers once for the whole prediction rather than fabricating per-member attribution.\n *\n * Framework-free + deterministic → unit-tested with no Angular.\n */\n\n/** A scored record in the at-risk list. */\nexport interface AtRiskRow {\n recordId: string;\n /**\n * Human-readable label for the record (e.g. the member's name/email), resolved from the model's\n * target entity. Null until resolved; the UI falls back to {@link recordId} so the row is never blank.\n */\n label: string | null;\n /** 0–1 prediction score (probability / risk). */\n score: number;\n /** Risk as a 0–100 integer, for display. */\n riskPct: number;\n /** Predicted class label, when present (classification). */\n class: string | null;\n /** Risk band, for color. */\n band: 'high' | 'medium' | 'low';\n /**\n * Top signed per-record drivers behind THIS row's prediction (P1-5), humanized + one-hot-collapsed for\n * display. `up: true` pushed the risk up, `false` down. Null when the model doesn't produce per-record\n * attribution (tree/ensemble/multiclass) — the UI then shows the model's global drivers instead.\n */\n drivers: RowDriver[] | null;\n}\n\n/** A humanized, signed per-record driver for the at-risk row's inline \"why\". */\nexport interface RowDriver {\n /** Display label (humanized, one-hot base collapsed). */\n label: string;\n /** Signed contribution magnitude for this row. */\n value: number;\n /** Whether this pushed the risk UP (value > 0) or down. */\n up: boolean;\n}\n\n/** Parse + humanize the raw per-record `drivers` (post-preprocessing `feature`/`value`) into {@link RowDriver}s. */\nfunction parseRowDrivers(raw: unknown): RowDriver[] | null {\n if (!Array.isArray(raw) || raw.length === 0) return null;\n const out: RowDriver[] = [];\n for (const d of raw as Array<{ feature?: unknown; value?: unknown }>) {\n const feature = typeof d?.feature === 'string' ? d.feature : '';\n const value = typeof d?.value === 'number' ? d.value : NaN;\n if (!feature || !Number.isFinite(value)) continue;\n // Keep the one-hot category: for a per-record \"why\", the category IS the story — \"Membership Type =\n // Student lowers risk\" is actionable where a collapsed \"Membership Type\" is close to meaningless.\n // (Collapsing across categories is only right for GLOBAL importance — see topGlobalDrivers.)\n out.push({ label: humanizeFeatureName(feature), value, up: value > 0 });\n }\n return out.length > 0 ? out : null;\n}\n\n/**\n * Build a human-readable label for a record from its (simple) row of the target entity — preferring a\n * single `Name`, else `FirstName`+`LastName`, else `Email`, else any first non-empty string field.\n * Returns null when nothing usable is found (caller falls back to the record id).\n */\nexport function labelFromRecord(row: Record<string, unknown> | undefined | null): string | null {\n if (!row) return null;\n const str = (v: unknown): string => (typeof v === 'string' ? v.trim() : '');\n const name = str(row['Name']);\n if (name) return name;\n const full = `${str(row['FirstName'])} ${str(row['LastName'])}`.trim();\n const email = str(row['Email']);\n if (full && email) return `${full} (${email})`;\n if (full) return full;\n if (email) return email;\n for (const v of Object.values(row)) {\n const s = str(v);\n if (s && s.length <= 120) return s;\n }\n return null;\n}\n\n/** The raw per-record detail the list is built from (a slice of `MJ: Process Run Details`). */\nexport interface RunDetailLike {\n recordId: string;\n ResultPayload?: string | null;\n}\n\nfunction bandFor(score: number): AtRiskRow['band'] {\n return score >= 0.7 ? 'high' : score >= 0.4 ? 'medium' : 'low';\n}\n\n/** Parse + rank the per-record predictions into the at-risk list (highest risk first). */\nexport function parseAtRiskRows(details: RunDetailLike[]): AtRiskRow[] {\n const rows: AtRiskRow[] = [];\n for (const d of details) {\n if (!d.ResultPayload) continue;\n let parsed: {\n score?: number;\n class?: string;\n drivers?: unknown;\n output?: { score?: number; class?: string; drivers?: unknown };\n };\n try {\n parsed = JSON.parse(d.ResultPayload);\n } catch {\n continue;\n }\n // Write-back runs nest the prediction under `output`; generic runs carry it at the top level.\n const p = parsed.output ?? parsed;\n if (typeof p.score !== 'number' || !Number.isFinite(p.score)) continue;\n const score = p.score;\n rows.push({\n recordId: d.recordId,\n label: null,\n score,\n riskPct: Math.round(score * 100),\n class: p.class ?? null,\n band: bandFor(score),\n drivers: parseRowDrivers(p.drivers),\n });\n }\n return rows.sort((a, b) => b.score - a.score);\n}\n\n/**\n * The top plain-language drivers for the whole prediction, from the model's global feature importance.\n * Accepts the object form (`{\"MembershipType=Student\": 0.9, ...}`) or the array form\n * (`[{feature, importance}]`), strips one-hot `=value` suffixes, de-duplicates, and returns the\n * highest-importance feature names — so a business user sees \"what's driving this\", not raw weights.\n */\nexport function topGlobalDrivers(featureImportanceJson: string | null | undefined, n = 3): string[] {\n if (!featureImportanceJson) return [];\n let parsed: unknown;\n try {\n parsed = JSON.parse(featureImportanceJson);\n } catch {\n return [];\n }\n const pairs: Array<{ name: string; weight: number }> = [];\n if (Array.isArray(parsed)) {\n for (const e of parsed as Array<Record<string, unknown>>) {\n const name = (e.feature ?? e.name) as string | undefined;\n const weight = (e.importance ?? e.value) as number | undefined;\n if (typeof name === 'string' && typeof weight === 'number') pairs.push({ name, weight: Math.abs(weight) });\n }\n } else if (parsed && typeof parsed === 'object') {\n for (const [name, weight] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof weight === 'number') pairs.push({ name, weight: Math.abs(weight) });\n }\n }\n // Collapse one-hot columns (\"Feature=Value\" → \"Feature\"), keeping each feature's max weight.\n const byFeature = new Map<string, number>();\n for (const { name, weight } of pairs) {\n const base = name.split('=')[0].trim();\n if (!base) continue;\n byFeature.set(base, Math.max(byFeature.get(base) ?? 0, weight));\n }\n return [...byFeature.entries()].sort((a, b) => b[1] - a[1]).slice(0, n).map(([name]) => humanizeFeatureName(name));\n}\n\n/**\n * Turn a raw feature/column name into a readable label — `RetentionOverdueInvoices` → `Retention Overdue\n * Invoices`, `overdue_invoices` → `Overdue Invoices`, and a one-hot `MembershipType=Student` →\n * `Membership Type = Student`. Splits camelCase + snake/kebab, spaces one-hot `=`, collapses whitespace,\n * and capitalizes the first letter. Already-spaced labels pass through unchanged.\n */\nexport function humanizeFeatureName(name: string): string {\n return name\n .replace(/[_-]+/g, ' ')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/\\s*=\\s*/g, ' = ')\n .replace(/\\s+/g, ' ')\n .trim()\n .replace(/^\\w/, (c) => c.toUpperCase());\n}\n"]}
1
+ {"version":3,"file":"at-risk.view-models.js","sourceRoot":"","sources":["../../src/PredictiveStudio/at-risk.view-models.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure derivations for the business workspace's **ranked at-risk list** — turning a model's per-record\n * scoring results (`MJ: Process Run Details` payloads) into a \"who to focus on\" list sorted by risk, and\n * the model's global feature importance into plain-language \"what's driving this\" drivers.\n *\n * Honest about the gap: MJ stores only GLOBAL feature importance (not per-record/SHAP), so we surface the\n * top global drivers once for the whole prediction rather than fabricating per-member attribution.\n *\n * Framework-free + deterministic → unit-tested with no Angular.\n */\n\nimport {\n type OutcomeConfig,\n resolveScoreBand,\n} from '@memberjunction/predictive-studio-core';\n\n/** A scored record in the at-risk list. */\nexport interface AtRiskRow {\n recordId: string;\n /**\n * Human-readable label for the record (e.g. the member's name/email), resolved from the model's\n * target entity. Null until resolved; the UI falls back to {@link recordId} so the row is never blank.\n */\n label: string | null;\n /** 0–1 prediction score (probability / risk). */\n score: number;\n /** Risk as a 0–100 integer, for display. */\n riskPct: number;\n /** Predicted class label, when present (classification). */\n class: string | null;\n /** Risk band, for color / filtering. */\n band: 'high' | 'medium' | 'low' | string;\n /** Qualitative status label (e.g. \"High\", \"Critical\", \"Low Risk\"). */\n status?: string;\n /** Semantic badge color. */\n badgeColor?: string;\n /** Icon class. */\n icon?: string;\n /**\n * Top signed per-record drivers behind THIS row's prediction (P1-5), humanized + one-hot-collapsed for\n * display. `up: true` pushed the risk up, `false` down. Null when the model doesn't produce per-record\n * attribution (tree/ensemble/multiclass) — the UI then shows the model's global drivers instead.\n */\n drivers: RowDriver[] | null;\n}\n\n/** A humanized, signed per-record driver for the at-risk row's inline \"why\". */\nexport interface RowDriver {\n /** Display label (humanized, one-hot base collapsed). */\n label: string;\n /** Signed contribution magnitude for this row. */\n value: number;\n /** Whether this pushed the risk UP (value > 0) or down. */\n up: boolean;\n}\n\n/** Parse + humanize the raw per-record `drivers` (post-preprocessing `feature`/`value`) into {@link RowDriver}s. */\nexport function parseRowDrivers(raw: unknown): RowDriver[] | null {\n if (!Array.isArray(raw) || raw.length === 0) return null;\n const out: RowDriver[] = [];\n for (const d of raw as Array<{ feature?: unknown; value?: unknown; importance?: unknown; weight?: unknown }>) {\n const feature = typeof d?.feature === 'string' ? d.feature : '';\n const rawVal = typeof d?.value === 'number' ? d.value : typeof d?.importance === 'number' ? d.importance : typeof d?.weight === 'number' ? d.weight : NaN;\n const value = Number(rawVal);\n if (!feature || !Number.isFinite(value)) continue;\n // Keep the one-hot category: for a per-record \"why\", the category IS the story — \"Membership Type =\n // Student lowers risk\" is actionable where a collapsed \"Membership Type\" is close to meaningless.\n // (Collapsing across categories is only right for GLOBAL importance — see topGlobalDrivers.)\n out.push({ label: humanizeFeatureName(feature), value, up: value > 0 });\n }\n return out.length > 0 ? out : null;\n}\n\n/**\n * Build a human-readable label for a record from its (simple) row of the target entity — preferring a\n * single `Name`, else `FirstName`+`LastName`, else `Email`, else any first non-empty string field.\n * Returns null when nothing usable is found (caller falls back to the record id).\n */\nexport function labelFromRecord(row: Record<string, unknown> | undefined | null): string | null {\n if (!row) return null;\n const str = (v: unknown): string => (typeof v === 'string' ? v.trim() : '');\n const member = str(row['MemberName'] || row['Member'] || row['FullName']);\n if (member) return member;\n const name = str(row['Name']);\n if (name) return name;\n const full = `${str(row['FirstName'])} ${str(row['LastName'])}`.trim();\n const email = str(row['Email']);\n if (full && email) return `${full} (${email})`;\n if (full) return full;\n if (email) return email;\n for (const v of Object.values(row)) {\n const s = str(v);\n if (s && s.length <= 120) return s;\n }\n return null;\n}\n\n/** The raw per-record detail the list is built from (a slice of `MJ: Process Run Details`). */\nexport interface RunDetailLike {\n recordId: string;\n ResultPayload?: string | null;\n}\n\n/** Options for parsing predictions into at-risk rows. */\nexport interface ParseAtRiskOptions {\n /**\n * When true, the model predicts probability of a positive outcome (e.g. `P(Renewed)`).\n * Churn / adverse risk is therefore inverted: `riskScore = 1.0 - score`.\n * High score (0.99) -> Low Risk (1%, green). Low score (0.10) -> High Risk (90%, red).\n */\n invertedRisk?: boolean;\n /**\n * Configured outcome metadata for the model (labels, thresholds, colors, icons).\n * When provided, `resolveScoreBand()` evaluates the dynamic band, status, badgeColor, and icon.\n */\n outcomeConfig?: OutcomeConfig;\n}\n\n/** Result of detecting whether a model is a renewal model and what its output score represents. */\nexport interface RenewalPolarityResult {\n isRenewalModel: boolean;\n /**\n * True if `score` represents adverse lapse risk (P(Lapse) / Churn Risk), e.g. 0.01 = 1% risk.\n * False if `score` represents positive outcome probability (P(Renewed)), e.g. 0.99 = 99% renewal chance.\n */\n scoreIsLapseRisk: boolean;\n}\n\n/**\n * Robustly inspects model metadata and sample scoring payloads to determine:\n * 1. Whether this is a renewal/retention/churn model (`isRenewalModel`)\n * 2. Whether the raw payload `score` represents adverse lapse risk (P(Lapse)) or positive outcome (P(Renewed))\n */\nexport function resolveRenewalPolarity(\n rawPayloads: Array<string | null | undefined>,\n targetVariable?: string | null,\n modelName?: string | null,\n): RenewalPolarityResult {\n const target = (targetVariable ?? '').toLowerCase();\n const name = (modelName ?? '').toLowerCase();\n const isTargetRenewal =\n target.includes('renew') ||\n target.includes('lapse') ||\n target.includes('retention') ||\n target.includes('churn') ||\n target === 'status' ||\n name.includes('renew') ||\n name.includes('retention') ||\n name.includes('churn');\n\n let hasRenewalKeywords = false;\n const sampleScoresForRenewed: number[] = [];\n\n for (const p of rawPayloads.slice(0, 50)) {\n if (!p) continue;\n if (\n p.includes('\"Renewed\"') ||\n p.includes('\"renewed\"') ||\n p.includes('\"Lapsed\"') ||\n p.includes('\"lapsed\"') ||\n p.includes('Renewal Risk')\n ) {\n hasRenewalKeywords = true;\n }\n try {\n const parsed = JSON.parse(p);\n const out = (parsed.output ?? parsed) as { score?: number; class?: string };\n if (typeof out.score === 'number' && typeof out.class === 'string') {\n const cls = out.class.toLowerCase();\n if (cls === 'renewed' || cls === 'active') {\n sampleScoresForRenewed.push(out.score);\n }\n }\n } catch {\n // skip invalid json\n }\n }\n\n const isRenewalModel = isTargetRenewal || hasRenewalKeywords;\n if (!isRenewalModel) {\n return { isRenewalModel: false, scoreIsLapseRisk: false };\n }\n\n if (sampleScoresForRenewed.length > 0) {\n const avg = sampleScoresForRenewed.reduce((a, b) => a + b, 0) / sampleScoresForRenewed.length;\n // If members predicted 'Renewed' have small scores (< 0.5), the score represents Lapse Risk (P(Lapse))\n return { isRenewalModel: true, scoreIsLapseRisk: avg < 0.5 };\n }\n\n // Fallback: target or name containing \"risk\", \"lapse\", or \"churn\" outputs lapse probability\n const scoreIsLapseRisk =\n target.includes('risk') ||\n target.includes('lapse') ||\n target.includes('churn') ||\n name.includes('risk') ||\n name.includes('churn');\n return { isRenewalModel: true, scoreIsLapseRisk };\n}\n\nfunction bandFor(score: number): AtRiskRow['band'] {\n return score >= 0.7 ? 'high' : score >= 0.4 ? 'medium' : 'low';\n}\n\n/** Parse + rank the per-record predictions into the at-risk list (highest risk first). */\nexport function parseAtRiskRows(details: RunDetailLike[], options?: ParseAtRiskOptions): AtRiskRow[] {\n const rows: AtRiskRow[] = [];\n const isInverted = options?.invertedRisk === true;\n const outcomeConfig = options?.outcomeConfig;\n\n for (const d of details) {\n if (!d.ResultPayload) continue;\n let parsed: {\n score?: number;\n class?: string;\n status?: string;\n band?: string;\n badgeColor?: string;\n icon?: string;\n drivers?: unknown;\n output?: {\n score?: number;\n class?: string;\n status?: string;\n band?: string;\n badgeColor?: string;\n icon?: string;\n drivers?: unknown;\n };\n };\n try {\n parsed = JSON.parse(d.ResultPayload);\n } catch {\n continue;\n }\n // Write-back runs nest the prediction under `output`; generic runs carry it at the top level.\n const p = parsed.output ?? parsed;\n if (typeof p.score !== 'number' || !Number.isFinite(p.score)) continue;\n const score = p.score;\n const effectiveRisk = isInverted ? Math.max(0, Math.min(1, 1 - score)) : score;\n const riskPct = Math.round(effectiveRisk * 100);\n\n let bandKey: string;\n let status: string | undefined = p.status;\n let badgeColor: string | undefined = p.badgeColor;\n let icon: string | undefined = p.icon;\n\n if (outcomeConfig) {\n const resolved = resolveScoreBand(score, outcomeConfig);\n if (resolved) {\n bandKey = resolved.Key;\n status = status ?? resolved.Label;\n badgeColor = badgeColor ?? resolved.BadgeColor;\n icon = icon ?? resolved.Icon;\n } else {\n bandKey = p.band ?? bandFor(effectiveRisk);\n }\n } else if (p.band) {\n bandKey = p.band;\n } else {\n bandKey = bandFor(effectiveRisk);\n }\n\n rows.push({\n recordId: d.recordId,\n label: null,\n score,\n riskPct,\n class: p.class ?? null,\n band: bandKey,\n status,\n badgeColor,\n icon,\n drivers: parseRowDrivers(p.drivers),\n });\n }\n return rows.sort((a, b) => b.riskPct - a.riskPct);\n}\n\n/**\n * The top plain-language drivers for the whole prediction, from the model's global feature importance.\n * Accepts the object form (`{\"MembershipType=Student\": 0.9, ...}`) or the array form\n * (`[{feature, importance}]`), strips one-hot `=value` suffixes, de-duplicates, and returns the\n * highest-importance feature names — so a business user sees \"what's driving this\", not raw weights.\n */\nexport function topGlobalDrivers(featureImportanceJson: string | null | undefined, n = 3): string[] {\n if (!featureImportanceJson) return [];\n let parsed: unknown;\n try {\n parsed = JSON.parse(featureImportanceJson);\n } catch {\n return [];\n }\n const pairs: Array<{ name: string; weight: number }> = [];\n if (Array.isArray(parsed)) {\n for (const e of parsed as Array<Record<string, unknown>>) {\n const name = (e.feature ?? e.name) as string | undefined;\n const weight = (e.importance ?? e.value) as number | undefined;\n if (typeof name === 'string' && typeof weight === 'number') pairs.push({ name, weight: Math.abs(weight) });\n }\n } else if (parsed && typeof parsed === 'object') {\n for (const [name, weight] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof weight === 'number') pairs.push({ name, weight: Math.abs(weight) });\n }\n }\n // Collapse one-hot columns (\"Feature=Value\" → \"Feature\"), keeping each feature's max weight.\n const byFeature = new Map<string, number>();\n for (const { name, weight } of pairs) {\n const base = name.split('=')[0].trim();\n if (!base) continue;\n byFeature.set(base, Math.max(byFeature.get(base) ?? 0, weight));\n }\n return [...byFeature.entries()].sort((a, b) => b[1] - a[1]).slice(0, n).map(([name]) => humanizeFeatureName(name));\n}\n\n/**\n * Turn a raw feature/column name into a readable label — `RetentionOverdueInvoices` → `Retention Overdue\n * Invoices`, `overdue_invoices` → `Overdue Invoices`, and a one-hot `MembershipType=Student` →\n * `Membership Type = Student`. Splits camelCase + snake/kebab, spaces one-hot `=`, collapses whitespace,\n * and capitalizes the first letter. Already-spaced labels pass through unchanged.\n */\nexport function humanizeFeatureName(name: string): string {\n return name\n .replace(/[_-]+/g, ' ')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/\\s*=\\s*/g, ' = ')\n .replace(/\\s+/g, ' ')\n .trim()\n .replace(/^\\w/, (c) => c.toUpperCase());\n}\n"]}
@@ -18,6 +18,9 @@ export declare class PSCatalogComponent implements OnInit {
18
18
  engine: PredictiveStudioEngine;
19
19
  /** Emitted with a starter prompt to open + seed the Model Development Agent chat. */
20
20
  askAgent: EventEmitter<string>;
21
+ /** Emitted when the user chooses to build a new training pipeline with this algorithm. */
22
+ createPipeline: EventEmitter<MJMLAlgorithmEntity>;
23
+ onCreatePipeline(algo: MJMLAlgorithmEntity): void;
21
24
  selectedUseCaseIds: string[];
22
25
  cards: AlgoCardVM[];
23
26
  /** The algorithm whose inline detail panel is expanded (null = none). */
@@ -50,7 +53,7 @@ export declare class PSCatalogComponent implements OnInit {
50
53
  };
51
54
  private rebuildCards;
52
55
  static ɵfac: i0.ɵɵFactoryDeclaration<PSCatalogComponent, never>;
53
- static ɵcmp: i0.ɵɵComponentDeclaration<PSCatalogComponent, "ps-catalog", never, { "engine": { "alias": "engine"; "required": false; }; }, { "askAgent": "askAgent"; }, never, never, true, never>;
56
+ static ɵcmp: i0.ɵɵComponentDeclaration<PSCatalogComponent, "ps-catalog", never, { "engine": { "alias": "engine"; "required": false; }; }, { "askAgent": "askAgent"; "createPipeline": "createPipeline"; }, never, never, true, never>;
54
57
  }
55
58
  export {};
56
59
  //# sourceMappingURL=ps-catalog.component.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ps-catalog.component.d.ts","sourceRoot":"","sources":["../../../src/PredictiveStudio/components/ps-catalog.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAS,MAAM,EAA6B,MAAM,eAAe,CAAC;AAIlG,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAChG,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAuB,MAAM,oCAAoC,CAAC;;AAEtH,UAAU,UAAU;IAClB,IAAI,EAAE,mBAAmB,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,mBAAmB,GAAG,IAAI,CAAC;CACvC;AAsBD;;;;;GAKG;AACH,qBAkIa,kBAAmB,YAAW,MAAM;IACtC,MAAM,EAAG,sBAAsB,CAAC;IAEzC,qFAAqF;IAC3E,QAAQ,uBAA8B;IAEzC,kBAAkB,EAAE,MAAM,EAAE,CAAM;IAClC,KAAK,EAAE,UAAU,EAAE,CAAM;IAChC,yEAAyE;IAClE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEtC,QAAQ,IAAI,IAAI;IAIhB,IAAW,QAAQ,IAAI,0BAA0B,EAAE,CAElD;IAEM,WAAW,CAAC,EAAE,EAAE,0BAA0B,GAAG,MAAM;IAYnD,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI/B,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAS/B,cAAc,IAAI,IAAI;IAO7B,4DAA4D;IACrD,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIrC,mFAAmF;IAC5E,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI;IAOpD,gGAAgG;IACzF,WAAW,CAAC,IAAI,EAAE,mBAAmB,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE;IAkBlE,IAAI,CAAC,KAAK,EAAE,mBAAmB,GAAG,MAAM;IAIxC,UAAU,CAAC,KAAK,EAAE,mBAAmB,GAAG,MAAM;IAUrD;;;;OAIG;IACH,IAAW,cAAc,IAAI;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAQ1E;IAED,OAAO,CAAC,YAAY;yCAhHT,kBAAkB;2CAAlB,kBAAkB;CAiI9B"}
1
+ {"version":3,"file":"ps-catalog.component.d.ts","sourceRoot":"","sources":["../../../src/PredictiveStudio/components/ps-catalog.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAS,MAAM,EAA6B,MAAM,eAAe,CAAC;AAIlG,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAChG,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAuB,MAAM,oCAAoC,CAAC;;AAEtH,UAAU,UAAU;IAClB,IAAI,EAAE,mBAAmB,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,mBAAmB,GAAG,IAAI,CAAC;CACvC;AAsBD;;;;;GAKG;AACH,qBAyIa,kBAAmB,YAAW,MAAM;IACtC,MAAM,EAAG,sBAAsB,CAAC;IAEzC,qFAAqF;IAC3E,QAAQ,uBAA8B;IAChD,0FAA0F;IAChF,cAAc,oCAA2C;IAE5D,gBAAgB,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI;IAIjD,kBAAkB,EAAE,MAAM,EAAE,CAAM;IAClC,KAAK,EAAE,UAAU,EAAE,CAAM;IAChC,yEAAyE;IAClE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEtC,QAAQ,IAAI,IAAI;IAIhB,IAAW,QAAQ,IAAI,0BAA0B,EAAE,CAElD;IAEM,WAAW,CAAC,EAAE,EAAE,0BAA0B,GAAG,MAAM;IAYnD,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI/B,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAS/B,cAAc,IAAI,IAAI;IAO7B,4DAA4D;IACrD,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIrC,mFAAmF;IAC5E,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI;IAOpD,gGAAgG;IACzF,WAAW,CAAC,IAAI,EAAE,mBAAmB,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE;IAkBlE,IAAI,CAAC,KAAK,EAAE,mBAAmB,GAAG,MAAM;IAIxC,UAAU,CAAC,KAAK,EAAE,mBAAmB,GAAG,MAAM;IAUrD;;;;OAIG;IACH,IAAW,cAAc,IAAI;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAQ1E;IAED,OAAO,CAAC,YAAY;yCAtHT,kBAAkB;2CAAlB,kBAAkB;CAuI9B"}
@@ -79,7 +79,7 @@ function PSCatalogComponent_Conditional_16_Template(rf, ctx) { if (rf & 1) {
79
79
  i0.ɵɵelementEnd()();
80
80
  } }
81
81
  function PSCatalogComponent_Conditional_17_For_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
82
- i0.ɵɵelementStart(0, "div", 20)(1, "span", 40);
82
+ i0.ɵɵelementStart(0, "div", 20)(1, "span", 42);
83
83
  i0.ɵɵtext(2, "Primary");
84
84
  i0.ɵɵelementEnd()();
85
85
  } }
@@ -103,18 +103,18 @@ function PSCatalogComponent_Conditional_17_For_2_For_16_Template(rf, ctx) { if (
103
103
  } }
104
104
  function PSCatalogComponent_Conditional_17_For_2_Conditional_18_Template(rf, ctx) { if (rf & 1) {
105
105
  i0.ɵɵelementStart(0, "span", 30);
106
- i0.ɵɵelement(1, "i", 41);
106
+ i0.ɵɵelement(1, "i", 43);
107
107
  i0.ɵɵtext(2, " Feature importance");
108
108
  i0.ɵɵelementEnd();
109
109
  } }
110
110
  function PSCatalogComponent_Conditional_17_For_2_Conditional_19_Template(rf, ctx) { if (rf & 1) {
111
111
  i0.ɵɵelementStart(0, "span", 31);
112
- i0.ɵɵelement(1, "i", 42);
112
+ i0.ɵɵelement(1, "i", 44);
113
113
  i0.ɵɵtext(2, " No importance");
114
114
  i0.ɵɵelementEnd();
115
115
  } }
116
116
  function PSCatalogComponent_Conditional_17_For_2_Conditional_21_Template(rf, ctx) { if (rf & 1) {
117
- i0.ɵɵelementStart(0, "span", 43);
117
+ i0.ɵɵelementStart(0, "span", 45);
118
118
  i0.ɵɵtext(1);
119
119
  i0.ɵɵelementEnd();
120
120
  } if (rf & 2) {
@@ -124,8 +124,8 @@ function PSCatalogComponent_Conditional_17_For_2_Conditional_21_Template(rf, ctx
124
124
  i0.ɵɵadvance();
125
125
  i0.ɵɵtextInterpolate(ctx_r2.levelLabel(card_r6.bestLevel));
126
126
  } }
127
- function PSCatalogComponent_Conditional_17_For_2_Conditional_29_Conditional_1_Template(rf, ctx) { if (rf & 1) {
128
- i0.ɵɵelementStart(0, "p", 44);
127
+ function PSCatalogComponent_Conditional_17_For_2_Conditional_32_Conditional_1_Template(rf, ctx) { if (rf & 1) {
128
+ i0.ɵɵelementStart(0, "p", 46);
129
129
  i0.ɵɵtext(1);
130
130
  i0.ɵɵelementEnd();
131
131
  } if (rf & 2) {
@@ -133,11 +133,11 @@ function PSCatalogComponent_Conditional_17_For_2_Conditional_29_Conditional_1_Te
133
133
  i0.ɵɵadvance();
134
134
  i0.ɵɵtextInterpolate(card_r6.algo.Description);
135
135
  } }
136
- function PSCatalogComponent_Conditional_17_For_2_Conditional_29_Conditional_12_For_4_Template(rf, ctx) { if (rf & 1) {
137
- i0.ɵɵelementStart(0, "div", 54)(1, "span", 55);
136
+ function PSCatalogComponent_Conditional_17_For_2_Conditional_32_Conditional_12_For_4_Template(rf, ctx) { if (rf & 1) {
137
+ i0.ɵɵelementStart(0, "div", 57)(1, "span", 58);
138
138
  i0.ɵɵtext(2);
139
139
  i0.ɵɵelementEnd();
140
- i0.ɵɵelementStart(3, "span", 56);
140
+ i0.ɵɵelementStart(3, "span", 59);
141
141
  i0.ɵɵtext(4);
142
142
  i0.ɵɵelementEnd()();
143
143
  } if (rf & 2) {
@@ -147,12 +147,12 @@ function PSCatalogComponent_Conditional_17_For_2_Conditional_29_Conditional_12_F
147
147
  i0.ɵɵadvance(2);
148
148
  i0.ɵɵtextInterpolate(hp_r9.v);
149
149
  } }
150
- function PSCatalogComponent_Conditional_17_For_2_Conditional_29_Conditional_12_Template(rf, ctx) { if (rf & 1) {
151
- i0.ɵɵelementStart(0, "div", 52);
150
+ function PSCatalogComponent_Conditional_17_For_2_Conditional_32_Conditional_12_Template(rf, ctx) { if (rf & 1) {
151
+ i0.ɵɵelementStart(0, "div", 55);
152
152
  i0.ɵɵtext(1, "Default hyperparameters");
153
153
  i0.ɵɵelementEnd();
154
- i0.ɵɵelementStart(2, "div", 53);
155
- i0.ɵɵrepeaterCreate(3, PSCatalogComponent_Conditional_17_For_2_Conditional_29_Conditional_12_For_4_Template, 5, 2, "div", 54, _forTrack2);
154
+ i0.ɵɵelementStart(2, "div", 56);
155
+ i0.ɵɵrepeaterCreate(3, PSCatalogComponent_Conditional_17_For_2_Conditional_32_Conditional_12_For_4_Template, 5, 2, "div", 57, _forTrack2);
156
156
  i0.ɵɵelementEnd();
157
157
  } if (rf & 2) {
158
158
  const card_r6 = i0.ɵɵnextContext(2).$implicit;
@@ -160,32 +160,37 @@ function PSCatalogComponent_Conditional_17_For_2_Conditional_29_Conditional_12_T
160
160
  i0.ɵɵadvance(3);
161
161
  i0.ɵɵrepeater(ctx_r2.hyperparams(card_r6.algo));
162
162
  } }
163
- function PSCatalogComponent_Conditional_17_For_2_Conditional_29_Conditional_13_Template(rf, ctx) { if (rf & 1) {
164
- i0.ɵɵelementStart(0, "div", 49);
163
+ function PSCatalogComponent_Conditional_17_For_2_Conditional_32_Conditional_13_Template(rf, ctx) { if (rf & 1) {
164
+ i0.ɵɵelementStart(0, "div", 51);
165
165
  i0.ɵɵtext(1, "Sensible defaults are applied at training time.");
166
166
  i0.ɵɵelementEnd();
167
167
  } }
168
- function PSCatalogComponent_Conditional_17_For_2_Conditional_29_Template(rf, ctx) { if (rf & 1) {
168
+ function PSCatalogComponent_Conditional_17_For_2_Conditional_32_Template(rf, ctx) { if (rf & 1) {
169
169
  const _r8 = i0.ɵɵgetCurrentView();
170
- i0.ɵɵelementStart(0, "div", 39);
171
- i0.ɵɵconditionalCreate(1, PSCatalogComponent_Conditional_17_For_2_Conditional_29_Conditional_1_Template, 2, 1, "p", 44);
172
- i0.ɵɵelementStart(2, "div", 45)(3, "span", 46);
170
+ i0.ɵɵelementStart(0, "div", 41);
171
+ i0.ɵɵconditionalCreate(1, PSCatalogComponent_Conditional_17_For_2_Conditional_32_Conditional_1_Template, 2, 1, "p", 46);
172
+ i0.ɵɵelementStart(2, "div", 47)(3, "span", 48);
173
173
  i0.ɵɵtext(4, "Problem types");
174
174
  i0.ɵɵelementEnd();
175
- i0.ɵɵelementStart(5, "span", 47);
175
+ i0.ɵɵelementStart(5, "span", 49);
176
176
  i0.ɵɵtext(6);
177
177
  i0.ɵɵelementEnd()();
178
- i0.ɵɵelementStart(7, "div", 45)(8, "span", 46);
178
+ i0.ɵɵelementStart(7, "div", 47)(8, "span", 48);
179
179
  i0.ɵɵtext(9, "Driver class");
180
180
  i0.ɵɵelementEnd();
181
- i0.ɵɵelementStart(10, "span", 48);
181
+ i0.ɵɵelementStart(10, "span", 50);
182
182
  i0.ɵɵtext(11);
183
183
  i0.ɵɵelementEnd()();
184
- i0.ɵɵconditionalCreate(12, PSCatalogComponent_Conditional_17_For_2_Conditional_29_Conditional_12_Template, 5, 0)(13, PSCatalogComponent_Conditional_17_For_2_Conditional_29_Conditional_13_Template, 2, 0, "div", 49);
185
- i0.ɵɵelementStart(14, "div", 50)(15, "button", 51);
186
- i0.ɵɵlistener("click", function PSCatalogComponent_Conditional_17_For_2_Conditional_29_Template_button_click_15_listener() { i0.ɵɵrestoreView(_r8); const card_r6 = i0.ɵɵnextContext().$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.useAlgorithm(card_r6.algo)); });
184
+ i0.ɵɵconditionalCreate(12, PSCatalogComponent_Conditional_17_For_2_Conditional_32_Conditional_12_Template, 5, 0)(13, PSCatalogComponent_Conditional_17_For_2_Conditional_32_Conditional_13_Template, 2, 0, "div", 51);
185
+ i0.ɵɵelementStart(14, "div", 52)(15, "button", 53);
186
+ i0.ɵɵlistener("click", function PSCatalogComponent_Conditional_17_For_2_Conditional_32_Template_button_click_15_listener() { i0.ɵɵrestoreView(_r8); const card_r6 = i0.ɵɵnextContext().$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onCreatePipeline(card_r6.algo)); });
187
187
  i0.ɵɵelement(16, "i", 38);
188
- i0.ɵɵtext(17, " Use this algorithm ");
188
+ i0.ɵɵtext(17, " Create Pipeline with this algorithm ");
189
+ i0.ɵɵelementEnd();
190
+ i0.ɵɵelementStart(18, "button", 54);
191
+ i0.ɵɵlistener("click", function PSCatalogComponent_Conditional_17_For_2_Conditional_32_Template_button_click_18_listener() { i0.ɵɵrestoreView(_r8); const card_r6 = i0.ɵɵnextContext().$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.useAlgorithm(card_r6.algo)); });
192
+ i0.ɵɵelement(19, "i", 40);
193
+ i0.ɵɵtext(20, " Ask Agent ");
189
194
  i0.ɵɵelementEnd()()();
190
195
  } if (rf & 2) {
191
196
  const card_r6 = i0.ɵɵnextContext().$implicit;
@@ -232,11 +237,16 @@ function PSCatalogComponent_Conditional_17_For_2_Template(rf, ctx) { if (rf & 1)
232
237
  i0.ɵɵtext(25, " Details ");
233
238
  i0.ɵɵelementEnd();
234
239
  i0.ɵɵelementStart(26, "button", 37);
235
- i0.ɵɵlistener("click", function PSCatalogComponent_Conditional_17_For_2_Template_button_click_26_listener() { const card_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.useAlgorithm(card_r6.algo)); });
240
+ i0.ɵɵlistener("click", function PSCatalogComponent_Conditional_17_For_2_Template_button_click_26_listener() { const card_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onCreatePipeline(card_r6.algo)); });
236
241
  i0.ɵɵelement(27, "i", 38);
237
- i0.ɵɵtext(28, " Use ");
242
+ i0.ɵɵtext(28, " Pipeline ");
243
+ i0.ɵɵelementEnd();
244
+ i0.ɵɵelementStart(29, "button", 39);
245
+ i0.ɵɵlistener("click", function PSCatalogComponent_Conditional_17_For_2_Template_button_click_29_listener() { const card_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.useAlgorithm(card_r6.algo)); });
246
+ i0.ɵɵelement(30, "i", 40);
247
+ i0.ɵɵtext(31, " Agent ");
238
248
  i0.ɵɵelementEnd()();
239
- i0.ɵɵconditionalCreate(29, PSCatalogComponent_Conditional_17_For_2_Conditional_29_Template, 18, 4, "div", 39);
249
+ i0.ɵɵconditionalCreate(32, PSCatalogComponent_Conditional_17_For_2_Conditional_32_Template, 21, 4, "div", 41);
240
250
  i0.ɵɵelementEnd();
241
251
  } if (rf & 2) {
242
252
  const card_r6 = ctx.$implicit;
@@ -260,12 +270,12 @@ function PSCatalogComponent_Conditional_17_For_2_Template(rf, ctx) { if (rf & 1)
260
270
  i0.ɵɵconditional(card_r6.bestLevel ? 21 : -1);
261
271
  i0.ɵɵadvance(2);
262
272
  i0.ɵɵclassProp("on", ctx_r2.detailId === card_r6.algo.ID);
263
- i0.ɵɵadvance(6);
264
- i0.ɵɵconditional(ctx_r2.detailId === card_r6.algo.ID ? 29 : -1);
273
+ i0.ɵɵadvance(9);
274
+ i0.ɵɵconditional(ctx_r2.detailId === card_r6.algo.ID ? 32 : -1);
265
275
  } }
266
276
  function PSCatalogComponent_Conditional_17_Template(rf, ctx) { if (rf & 1) {
267
277
  i0.ɵɵelementStart(0, "div", 12);
268
- i0.ɵɵrepeaterCreate(1, PSCatalogComponent_Conditional_17_For_2_Template, 30, 15, "div", 18, _forTrack1);
278
+ i0.ɵɵrepeaterCreate(1, PSCatalogComponent_Conditional_17_For_2_Template, 33, 15, "div", 18, _forTrack1);
269
279
  i0.ɵɵelementEnd();
270
280
  } if (rf & 2) {
271
281
  const ctx_r2 = i0.ɵɵnextContext();
@@ -300,6 +310,11 @@ export class PSCatalogComponent {
300
310
  engine;
301
311
  /** Emitted with a starter prompt to open + seed the Model Development Agent chat. */
302
312
  askAgent = new EventEmitter();
313
+ /** Emitted when the user chooses to build a new training pipeline with this algorithm. */
314
+ createPipeline = new EventEmitter();
315
+ onCreatePipeline(algo) {
316
+ this.createPipeline.emit(algo);
317
+ }
303
318
  selectedUseCaseIds = [];
304
319
  cards = [];
305
320
  /** The algorithm whose inline detail panel is expanded (null = none). */
@@ -416,7 +431,7 @@ export class PSCatalogComponent {
416
431
  this.cards = cards;
417
432
  }
418
433
  static ɵfac = function PSCatalogComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PSCatalogComponent)(); };
419
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PSCatalogComponent, selectors: [["ps-catalog"]], inputs: { engine: "engine" }, outputs: { askAgent: "askAgent" }, decls: 18, vars: 3, consts: [["data-testid", "ps-catalog-panel", 1, "ps-panel", "ps-catalog"], [1, "ps-card", "guide-card"], [1, "ps-card-body"], [1, "guide-head"], [1, "fa-solid", "fa-compass", 2, "color", "var(--mj-brand-primary)"], [1, "ps-muted", "ps-small"], [1, "chips-row"], ["data-testid", "ps-catalog-scenarios", 1, "chips"], ["data-testid", "ps-catalog-scenario-chip", 1, "chip", 3, "on"], [1, "clearx"], ["data-testid", "ps-catalog-reco-banner", 1, "ps-callout", "info", "reco-banner"], [1, "ps-card"], ["data-testid", "ps-catalog-gallery", 1, "gallery"], ["data-testid", "ps-catalog-scenario-chip", 1, "chip", 3, "click"], [1, "clearx", 3, "click"], [1, "fa-solid", "fa-xmark"], [1, "fa-solid", "fa-wand-magic-sparkles"], [1, "ps-card-body", "ps-muted"], ["data-testid", "ps-catalog-card", 1, "acard", 3, "hit", "dim"], ["data-testid", "ps-catalog-card", 1, "acard"], [1, "ribbon"], [1, "ah"], [1, "ic"], [1, "title"], [1, "nm"], [1, "ps-mono", "key"], [1, "ab"], [1, "kv-title"], [1, "meta-row"], [1, "ps-tag"], [1, "ps-badge", "green"], [1, "ps-badge", "red"], [1, "af"], [1, "ps-lvl", 3, "class"], [1, "ps-spacer"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-catalog-details", 3, "click"], [1, "fa-solid", "fa-circle-info"], ["mjButton", "", "variant", "primary", "size", "sm", "data-testid", "ps-catalog-use", 3, "click"], [1, "fa-solid", "fa-diagram-project"], ["data-testid", "ps-catalog-detail", 1, "adetail"], [1, "ps-lvl", "ps-lvl-Primary"], [1, "fa-solid", "fa-chart-column"], [1, "fa-solid", "fa-ban"], [1, "ps-lvl"], [1, "ps-small", "adetail-desc"], [1, "dkv"], [1, "dk"], [1, "dv"], [1, "dv", "ps-mono"], [1, "ps-small", "ps-muted", 2, "margin-top", "6px"], [1, "adetail-foot"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click"], [1, "dk", 2, "margin-top", "8px"], [1, "hp-list"], [1, "hp"], [1, "ps-mono"], [1, "ps-mono", "ps-muted"]], template: function PSCatalogComponent_Template(rf, ctx) { if (rf & 1) {
434
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PSCatalogComponent, selectors: [["ps-catalog"]], inputs: { engine: "engine" }, outputs: { askAgent: "askAgent", createPipeline: "createPipeline" }, decls: 18, vars: 3, consts: [["data-testid", "ps-catalog-panel", 1, "ps-panel", "ps-catalog"], [1, "ps-card", "guide-card"], [1, "ps-card-body"], [1, "guide-head"], [1, "fa-solid", "fa-compass", 2, "color", "var(--mj-brand-primary)"], [1, "ps-muted", "ps-small"], [1, "chips-row"], ["data-testid", "ps-catalog-scenarios", 1, "chips"], ["data-testid", "ps-catalog-scenario-chip", 1, "chip", 3, "on"], [1, "clearx"], ["data-testid", "ps-catalog-reco-banner", 1, "ps-callout", "info", "reco-banner"], [1, "ps-card"], ["data-testid", "ps-catalog-gallery", 1, "gallery"], ["data-testid", "ps-catalog-scenario-chip", 1, "chip", 3, "click"], [1, "clearx", 3, "click"], [1, "fa-solid", "fa-xmark"], [1, "fa-solid", "fa-wand-magic-sparkles"], [1, "ps-card-body", "ps-muted"], ["data-testid", "ps-catalog-card", 1, "acard", 3, "hit", "dim"], ["data-testid", "ps-catalog-card", 1, "acard"], [1, "ribbon"], [1, "ah"], [1, "ic"], [1, "title"], [1, "nm"], [1, "ps-mono", "key"], [1, "ab"], [1, "kv-title"], [1, "meta-row"], [1, "ps-tag"], [1, "ps-badge", "green"], [1, "ps-badge", "red"], [1, "af"], [1, "ps-lvl", 3, "class"], [1, "ps-spacer"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-catalog-details", 3, "click"], [1, "fa-solid", "fa-circle-info"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-catalog-create-pipeline", 3, "click"], [1, "fa-solid", "fa-plus"], ["mjButton", "", "variant", "primary", "size", "sm", "data-testid", "ps-catalog-use", 3, "click"], [1, "fa-solid", "fa-robot"], ["data-testid", "ps-catalog-detail", 1, "adetail"], [1, "ps-lvl", "ps-lvl-Primary"], [1, "fa-solid", "fa-chart-column"], [1, "fa-solid", "fa-ban"], [1, "ps-lvl"], [1, "ps-small", "adetail-desc"], [1, "dkv"], [1, "dk"], [1, "dv"], [1, "dv", "ps-mono"], [1, "ps-small", "ps-muted", 2, "margin-top", "6px"], [1, "adetail-foot", 2, "display", "flex", "gap", "8px"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "click"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click"], [1, "dk", 2, "margin-top", "8px"], [1, "hp-list"], [1, "hp"], [1, "ps-mono"], [1, "ps-mono", "ps-muted"]], template: function PSCatalogComponent_Template(rf, ctx) { if (rf & 1) {
420
435
  i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2)(3, "div", 3);
421
436
  i0.ɵɵelement(4, "i", 4);
422
437
  i0.ɵɵelementStart(5, "div")(6, "h3");
@@ -535,9 +550,13 @@ export class PSCatalogComponent {
535
550
  [class.on]="detailId === card.algo.ID" (click)="toggleDetail(card.algo.ID)">
536
551
  <i class="fa-solid fa-circle-info"></i> Details
537
552
  </button>
553
+ <button mjButton variant="secondary" size="sm" data-testid="ps-catalog-create-pipeline"
554
+ (click)="onCreatePipeline(card.algo)">
555
+ <i class="fa-solid fa-plus"></i> Pipeline
556
+ </button>
538
557
  <button mjButton variant="primary" size="sm" data-testid="ps-catalog-use"
539
558
  (click)="useAlgorithm(card.algo)">
540
- <i class="fa-solid fa-diagram-project"></i> Use
559
+ <i class="fa-solid fa-robot"></i> Agent
541
560
  </button>
542
561
  </div>
543
562
  @if (detailId === card.algo.ID) {
@@ -557,9 +576,12 @@ export class PSCatalogComponent {
557
576
  } @else {
558
577
  <div class="ps-small ps-muted" style="margin-top:6px">Sensible defaults are applied at training time.</div>
559
578
  }
560
- <div class="adetail-foot">
579
+ <div class="adetail-foot" style="display: flex; gap: 8px;">
580
+ <button mjButton variant="secondary" size="sm" (click)="onCreatePipeline(card.algo)">
581
+ <i class="fa-solid fa-plus"></i> Create Pipeline with this algorithm
582
+ </button>
561
583
  <button mjButton variant="primary" size="sm" (click)="useAlgorithm(card.algo)">
562
- <i class="fa-solid fa-diagram-project"></i> Use this algorithm
584
+ <i class="fa-solid fa-robot"></i> Ask Agent
563
585
  </button>
564
586
  </div>
565
587
  </div>
@@ -574,6 +596,8 @@ export class PSCatalogComponent {
574
596
  type: Input
575
597
  }], askAgent: [{
576
598
  type: Output
599
+ }], createPipeline: [{
600
+ type: Output
577
601
  }] }); })();
578
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PSCatalogComponent, { className: "PSCatalogComponent", filePath: "src/PredictiveStudio/components/ps-catalog.component.ts", lineNumber: 171 }); })();
602
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PSCatalogComponent, { className: "PSCatalogComponent", filePath: "src/PredictiveStudio/components/ps-catalog.component.ts", lineNumber: 178 }); })();
579
603
  //# sourceMappingURL=ps-catalog.component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ps-catalog.component.js","sourceRoot":"","sources":["../../../src/PredictiveStudio/components/ps-catalog.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { MJButtonDirective } from '@memberjunction/ng-ui-components';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { MJMLAlgorithmEntity, MJMLAlgorithmUseCaseEntity } from '@memberjunction/core-entities';\nimport { PredictiveStudioEngine, RecommendationLevel, RECOMMENDATION_RANK } from '../engine/predictive-studio.engine';\n\ninterface AlgoCardVM {\n algo: MJMLAlgorithmEntity;\n icon: string;\n problemTypes: string[];\n bestLevel: RecommendationLevel | null;\n}\n\nconst ALGO_ICONS: Record<string, string> = {\n xgboost: 'fa-solid fa-bolt',\n lightgbm: 'fa-solid fa-feather',\n logistic_regression: 'fa-solid fa-wave-square',\n random_forest: 'fa-solid fa-tree',\n ridge: 'fa-solid fa-ruler',\n linear_regression: 'fa-solid fa-ruler',\n mlp: 'fa-solid fa-network-wired',\n};\n\nconst USE_CASE_ICONS: Record<string, string> = {\n binary: 'fa-solid fa-circle-half-stroke',\n regression: 'fa-solid fa-arrow-trend-up',\n interpret: 'fa-solid fa-magnifying-glass-chart',\n tuning: 'fa-solid fa-sliders',\n large: 'fa-solid fa-gauge-high',\n embedding: 'fa-solid fa-vector-square',\n small: 'fa-solid fa-compress',\n};\n\n/**\n * Algorithm Catalog panel (mockup algorithms-2): a card gallery of the fixed algorithm catalog plus\n * a \"Guide me\" scenario picker. Selecting use-case chips highlights and re-sorts the algorithm cards\n * by each algorithm's best RecommendationLevel across the chosen scenarios — driven entirely by the\n * cached MJ: ML Algorithm Use Case Rankings matrix.\n */\n@Component({\n standalone: true,\n selector: 'ps-catalog',\n imports: [CommonModule, MJButtonDirective],\n encapsulation: ViewEncapsulation.None,\n styleUrls: ['../predictive-studio.shared.css', './ps-catalog.component.css'],\n template: `\n <div class=\"ps-panel ps-catalog\" data-testid=\"ps-catalog-panel\">\n <!-- Guide me scenario picker -->\n <div class=\"ps-card guide-card\">\n <div class=\"ps-card-body\">\n <div class=\"guide-head\">\n <i class=\"fa-solid fa-compass\" style=\"color: var(--mj-brand-primary)\"></i>\n <div>\n <h3>Guide me — what does your problem look like?</h3>\n <div class=\"ps-muted ps-small\">\n Tap the scenarios that describe your data and goals. We'll highlight the algorithms that fit\n and pull the best matches to the front. Pick one or several.\n </div>\n </div>\n </div>\n <div class=\"chips-row\">\n <div class=\"chips\" data-testid=\"ps-catalog-scenarios\">\n @for (uc of useCases; track uc.ID) {\n <button class=\"chip\" [class.on]=\"isSelected(uc.ID)\" data-testid=\"ps-catalog-scenario-chip\" (click)=\"toggleUseCase(uc.ID)\">\n <i [class]=\"useCaseIcon(uc)\"></i> {{ uc.Name }}\n </button>\n }\n </div>\n @if (selectedUseCaseIds.length > 0) {\n <button class=\"clearx\" (click)=\"clearScenarios()\"><i class=\"fa-solid fa-xmark\"></i> Clear</button>\n }\n </div>\n @if (selectedUseCaseIds.length > 0) {\n <div class=\"ps-callout info reco-banner\" data-testid=\"ps-catalog-reco-banner\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n @if (recommendation.primaries.length > 0) {\n <div>For <strong>{{ recommendation.scenarioLabel }}</strong>, your top pick(s):\n <strong>{{ recommendation.primaries.join(' & ') }}</strong>. Stronger fits are pulled to the front.</div>\n } @else {\n <div>For <strong>{{ recommendation.scenarioLabel }}</strong>, no single primary stands out —\n review the strong fits at the top.</div>\n }\n </div>\n }\n </div>\n </div>\n\n <!-- Card gallery -->\n @if (cards.length === 0) {\n <div class=\"ps-card\"><div class=\"ps-card-body ps-muted\">No algorithms in the catalog yet.</div></div>\n } @else {\n <div class=\"gallery\" data-testid=\"ps-catalog-gallery\">\n @for (card of cards; track card.algo.ID) {\n <div class=\"acard\" data-testid=\"ps-catalog-card\"\n [class.hit]=\"card.bestLevel === 'Primary'\"\n [class.dim]=\"card.bestLevel && rank(card.bestLevel) <= 1\">\n @if (card.bestLevel === 'Primary') {\n <div class=\"ribbon\"><span class=\"ps-lvl ps-lvl-Primary\">Primary</span></div>\n }\n <div class=\"ah\">\n <div class=\"ic\"><i [class]=\"card.icon\"></i></div>\n <div class=\"title\">\n <div class=\"nm\">{{ card.algo.Name }}</div>\n <div class=\"ps-mono key\">{{ card.algo.DriverClass }}</div>\n </div>\n </div>\n <div class=\"ab\">\n @if (card.algo.Description) {\n <div class=\"ps-muted ps-small\">{{ card.algo.Description }}</div>\n }\n <div class=\"kv-title\">Problem types</div>\n <div class=\"meta-row\">\n @for (pt of card.problemTypes; track pt) {\n <span class=\"ps-tag\">{{ pt }}</span>\n }\n </div>\n <div class=\"meta-row\">\n @if (card.algo.SupportsFeatureImportance) {\n <span class=\"ps-badge green\"><i class=\"fa-solid fa-chart-column\"></i> Feature importance</span>\n } @else {\n <span class=\"ps-badge red\"><i class=\"fa-solid fa-ban\"></i> No importance</span>\n }\n </div>\n </div>\n <div class=\"af\">\n @if (card.bestLevel) {\n <span class=\"ps-lvl\" [class]=\"'ps-lvl-' + card.bestLevel\">{{ levelLabel(card.bestLevel) }}</span>\n }\n <span class=\"ps-spacer\"></span>\n <button mjButton variant=\"secondary\" size=\"sm\" data-testid=\"ps-catalog-details\"\n [class.on]=\"detailId === card.algo.ID\" (click)=\"toggleDetail(card.algo.ID)\">\n <i class=\"fa-solid fa-circle-info\"></i> Details\n </button>\n <button mjButton variant=\"primary\" size=\"sm\" data-testid=\"ps-catalog-use\"\n (click)=\"useAlgorithm(card.algo)\">\n <i class=\"fa-solid fa-diagram-project\"></i> Use\n </button>\n </div>\n @if (detailId === card.algo.ID) {\n <div class=\"adetail\" data-testid=\"ps-catalog-detail\">\n @if (card.algo.Description) {\n <p class=\"ps-small adetail-desc\">{{ card.algo.Description }}</p>\n }\n <div class=\"dkv\"><span class=\"dk\">Problem types</span><span class=\"dv\">{{ card.problemTypes.join(', ') || '—' }}</span></div>\n <div class=\"dkv\"><span class=\"dk\">Driver class</span><span class=\"dv ps-mono\">{{ card.algo.DriverClass }}</span></div>\n @if (hyperparams(card.algo).length > 0) {\n <div class=\"dk\" style=\"margin-top:8px\">Default hyperparameters</div>\n <div class=\"hp-list\">\n @for (hp of hyperparams(card.algo); track hp.k) {\n <div class=\"hp\"><span class=\"ps-mono\">{{ hp.k }}</span><span class=\"ps-mono ps-muted\">{{ hp.v }}</span></div>\n }\n </div>\n } @else {\n <div class=\"ps-small ps-muted\" style=\"margin-top:6px\">Sensible defaults are applied at training time.</div>\n }\n <div class=\"adetail-foot\">\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"useAlgorithm(card.algo)\">\n <i class=\"fa-solid fa-diagram-project\"></i> Use this algorithm\n </button>\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n `,\n})\nexport class PSCatalogComponent implements OnInit {\n @Input() engine!: PredictiveStudioEngine;\n\n /** Emitted with a starter prompt to open + seed the Model Development Agent chat. */\n @Output() askAgent = new EventEmitter<string>();\n\n public selectedUseCaseIds: string[] = [];\n public cards: AlgoCardVM[] = [];\n /** The algorithm whose inline detail panel is expanded (null = none). */\n public detailId: string | null = null;\n\n ngOnInit(): void {\n this.rebuildCards();\n }\n\n public get useCases(): MJMLAlgorithmUseCaseEntity[] {\n return this.engine?.UseCases ?? [];\n }\n\n public useCaseIcon(uc: MJMLAlgorithmUseCaseEntity): string {\n const name = (uc.Name || '').toLowerCase();\n if (name.includes('binary')) return USE_CASE_ICONS['binary'];\n if (name.includes('regression')) return USE_CASE_ICONS['regression'];\n if (name.includes('interpret')) return USE_CASE_ICONS['interpret'];\n if (name.includes('tuning') || name.includes('minimal')) return USE_CASE_ICONS['tuning'];\n if (name.includes('large') || name.includes('wide') || name.includes('speed')) return USE_CASE_ICONS['large'];\n if (name.includes('embedding') || name.includes('llm')) return USE_CASE_ICONS['embedding'];\n if (name.includes('small')) return USE_CASE_ICONS['small'];\n return 'fa-solid fa-circle-dot';\n }\n\n public isSelected(id: string): boolean {\n return this.selectedUseCaseIds.some((s) => UUIDsEqual(s, id));\n }\n\n public toggleUseCase(id: string): void {\n if (this.isSelected(id)) {\n this.selectedUseCaseIds = this.selectedUseCaseIds.filter((s) => !UUIDsEqual(s, id));\n } else {\n this.selectedUseCaseIds = [...this.selectedUseCaseIds, id];\n }\n this.rebuildCards();\n }\n\n public clearScenarios(): void {\n this.selectedUseCaseIds = [];\n this.rebuildCards();\n }\n\n // ---- algorithm card actions ----\n\n /** Toggle the inline detail panel for an algorithm card. */\n public toggleDetail(id: string): void {\n this.detailId = this.detailId === id ? null : id;\n }\n\n /** Seed the Model Development Agent to build a model with the chosen algorithm. */\n public useAlgorithm(algo: MJMLAlgorithmEntity): void {\n this.askAgent.emit(\n `Help me build a predictive model using the ${algo.Name} algorithm. ` +\n `Walk me through choosing the entity, the outcome to predict, and the features to assemble.`,\n );\n }\n\n /** Parse an algorithm's `DefaultHyperparameters` JSON into a small key/value list (≤8 rows). */\n public hyperparams(algo: MJMLAlgorithmEntity): { k: string; v: string }[] {\n const raw = algo.DefaultHyperparameters;\n if (!raw) {\n return [];\n }\n try {\n const parsed: unknown = JSON.parse(raw);\n if (!parsed || typeof parsed !== 'object') {\n return [];\n }\n return Object.entries(parsed as Record<string, unknown>)\n .map(([k, v]) => ({ k, v: String(v) }))\n .slice(0, 8);\n } catch {\n return [];\n }\n }\n\n public rank(level: RecommendationLevel): number {\n return RECOMMENDATION_RANK[level];\n }\n\n public levelLabel(level: RecommendationLevel): string {\n switch (level) {\n case 'Primary': return 'Primary fit';\n case 'Strong': return 'Strong fit';\n case 'Viable': return 'Viable';\n case 'Weak': return 'Weak fit';\n case 'NotRecommended': return 'N/A';\n }\n }\n\n /**\n * Structured recommendation data for the \"Guide me\" banner — rendered with plain template\n * interpolation + `<strong>` markup (no `[innerHTML]`). `scenarioLabel` is the human-readable\n * scenario name(s); `primaries` is the list of Primary-fit algorithm names.\n */\n public get recommendation(): { scenarioLabel: string; primaries: string[] } {\n const levels = this.engine.BestLevelsForScenarios(this.selectedUseCaseIds);\n const primaries = this.engine.Algorithms.filter((a) => levels.get(a.ID) === 'Primary').map((a) => a.Name);\n const scenarioLabel =\n this.selectedUseCaseIds.length === 1\n ? this.useCases.find((u) => UUIDsEqual(u.ID, this.selectedUseCaseIds[0]))?.Name ?? 'your scenario'\n : `${this.selectedUseCaseIds.length} scenarios`;\n return { scenarioLabel, primaries };\n }\n\n private rebuildCards(): void {\n const levels = this.engine.BestLevelsForScenarios(this.selectedUseCaseIds);\n const cards: AlgoCardVM[] = this.engine.Algorithms.map((algo) => ({\n algo,\n icon: ALGO_ICONS[algo.DriverClass] ?? 'fa-solid fa-shapes',\n problemTypes: (algo.ProblemTypes || '').split(',').map((p) => p.trim()).filter((p) => p.length > 0),\n bestLevel: levels.get(algo.ID) ?? null,\n }));\n if (this.selectedUseCaseIds.length > 0) {\n cards.sort((a, b) => {\n const ra = a.bestLevel ? RECOMMENDATION_RANK[a.bestLevel] : -1;\n const rb = b.bestLevel ? RECOMMENDATION_RANK[b.bestLevel] : -1;\n return rb - ra;\n });\n }\n this.cards = cards;\n }\n}\n"]}
1
+ {"version":3,"file":"ps-catalog.component.js","sourceRoot":"","sources":["../../../src/PredictiveStudio/components/ps-catalog.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { MJButtonDirective } from '@memberjunction/ng-ui-components';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { MJMLAlgorithmEntity, MJMLAlgorithmUseCaseEntity } from '@memberjunction/core-entities';\nimport { PredictiveStudioEngine, RecommendationLevel, RECOMMENDATION_RANK } from '../engine/predictive-studio.engine';\n\ninterface AlgoCardVM {\n algo: MJMLAlgorithmEntity;\n icon: string;\n problemTypes: string[];\n bestLevel: RecommendationLevel | null;\n}\n\nconst ALGO_ICONS: Record<string, string> = {\n xgboost: 'fa-solid fa-bolt',\n lightgbm: 'fa-solid fa-feather',\n logistic_regression: 'fa-solid fa-wave-square',\n random_forest: 'fa-solid fa-tree',\n ridge: 'fa-solid fa-ruler',\n linear_regression: 'fa-solid fa-ruler',\n mlp: 'fa-solid fa-network-wired',\n};\n\nconst USE_CASE_ICONS: Record<string, string> = {\n binary: 'fa-solid fa-circle-half-stroke',\n regression: 'fa-solid fa-arrow-trend-up',\n interpret: 'fa-solid fa-magnifying-glass-chart',\n tuning: 'fa-solid fa-sliders',\n large: 'fa-solid fa-gauge-high',\n embedding: 'fa-solid fa-vector-square',\n small: 'fa-solid fa-compress',\n};\n\n/**\n * Algorithm Catalog panel (mockup algorithms-2): a card gallery of the fixed algorithm catalog plus\n * a \"Guide me\" scenario picker. Selecting use-case chips highlights and re-sorts the algorithm cards\n * by each algorithm's best RecommendationLevel across the chosen scenarios — driven entirely by the\n * cached MJ: ML Algorithm Use Case Rankings matrix.\n */\n@Component({\n standalone: true,\n selector: 'ps-catalog',\n imports: [CommonModule, MJButtonDirective],\n encapsulation: ViewEncapsulation.None,\n styleUrls: ['../predictive-studio.shared.css', './ps-catalog.component.css'],\n template: `\n <div class=\"ps-panel ps-catalog\" data-testid=\"ps-catalog-panel\">\n <!-- Guide me scenario picker -->\n <div class=\"ps-card guide-card\">\n <div class=\"ps-card-body\">\n <div class=\"guide-head\">\n <i class=\"fa-solid fa-compass\" style=\"color: var(--mj-brand-primary)\"></i>\n <div>\n <h3>Guide me — what does your problem look like?</h3>\n <div class=\"ps-muted ps-small\">\n Tap the scenarios that describe your data and goals. We'll highlight the algorithms that fit\n and pull the best matches to the front. Pick one or several.\n </div>\n </div>\n </div>\n <div class=\"chips-row\">\n <div class=\"chips\" data-testid=\"ps-catalog-scenarios\">\n @for (uc of useCases; track uc.ID) {\n <button class=\"chip\" [class.on]=\"isSelected(uc.ID)\" data-testid=\"ps-catalog-scenario-chip\" (click)=\"toggleUseCase(uc.ID)\">\n <i [class]=\"useCaseIcon(uc)\"></i> {{ uc.Name }}\n </button>\n }\n </div>\n @if (selectedUseCaseIds.length > 0) {\n <button class=\"clearx\" (click)=\"clearScenarios()\"><i class=\"fa-solid fa-xmark\"></i> Clear</button>\n }\n </div>\n @if (selectedUseCaseIds.length > 0) {\n <div class=\"ps-callout info reco-banner\" data-testid=\"ps-catalog-reco-banner\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n @if (recommendation.primaries.length > 0) {\n <div>For <strong>{{ recommendation.scenarioLabel }}</strong>, your top pick(s):\n <strong>{{ recommendation.primaries.join(' & ') }}</strong>. Stronger fits are pulled to the front.</div>\n } @else {\n <div>For <strong>{{ recommendation.scenarioLabel }}</strong>, no single primary stands out —\n review the strong fits at the top.</div>\n }\n </div>\n }\n </div>\n </div>\n\n <!-- Card gallery -->\n @if (cards.length === 0) {\n <div class=\"ps-card\"><div class=\"ps-card-body ps-muted\">No algorithms in the catalog yet.</div></div>\n } @else {\n <div class=\"gallery\" data-testid=\"ps-catalog-gallery\">\n @for (card of cards; track card.algo.ID) {\n <div class=\"acard\" data-testid=\"ps-catalog-card\"\n [class.hit]=\"card.bestLevel === 'Primary'\"\n [class.dim]=\"card.bestLevel && rank(card.bestLevel) <= 1\">\n @if (card.bestLevel === 'Primary') {\n <div class=\"ribbon\"><span class=\"ps-lvl ps-lvl-Primary\">Primary</span></div>\n }\n <div class=\"ah\">\n <div class=\"ic\"><i [class]=\"card.icon\"></i></div>\n <div class=\"title\">\n <div class=\"nm\">{{ card.algo.Name }}</div>\n <div class=\"ps-mono key\">{{ card.algo.DriverClass }}</div>\n </div>\n </div>\n <div class=\"ab\">\n @if (card.algo.Description) {\n <div class=\"ps-muted ps-small\">{{ card.algo.Description }}</div>\n }\n <div class=\"kv-title\">Problem types</div>\n <div class=\"meta-row\">\n @for (pt of card.problemTypes; track pt) {\n <span class=\"ps-tag\">{{ pt }}</span>\n }\n </div>\n <div class=\"meta-row\">\n @if (card.algo.SupportsFeatureImportance) {\n <span class=\"ps-badge green\"><i class=\"fa-solid fa-chart-column\"></i> Feature importance</span>\n } @else {\n <span class=\"ps-badge red\"><i class=\"fa-solid fa-ban\"></i> No importance</span>\n }\n </div>\n </div>\n <div class=\"af\">\n @if (card.bestLevel) {\n <span class=\"ps-lvl\" [class]=\"'ps-lvl-' + card.bestLevel\">{{ levelLabel(card.bestLevel) }}</span>\n }\n <span class=\"ps-spacer\"></span>\n <button mjButton variant=\"secondary\" size=\"sm\" data-testid=\"ps-catalog-details\"\n [class.on]=\"detailId === card.algo.ID\" (click)=\"toggleDetail(card.algo.ID)\">\n <i class=\"fa-solid fa-circle-info\"></i> Details\n </button>\n <button mjButton variant=\"secondary\" size=\"sm\" data-testid=\"ps-catalog-create-pipeline\"\n (click)=\"onCreatePipeline(card.algo)\">\n <i class=\"fa-solid fa-plus\"></i> Pipeline\n </button>\n <button mjButton variant=\"primary\" size=\"sm\" data-testid=\"ps-catalog-use\"\n (click)=\"useAlgorithm(card.algo)\">\n <i class=\"fa-solid fa-robot\"></i> Agent\n </button>\n </div>\n @if (detailId === card.algo.ID) {\n <div class=\"adetail\" data-testid=\"ps-catalog-detail\">\n @if (card.algo.Description) {\n <p class=\"ps-small adetail-desc\">{{ card.algo.Description }}</p>\n }\n <div class=\"dkv\"><span class=\"dk\">Problem types</span><span class=\"dv\">{{ card.problemTypes.join(', ') || '—' }}</span></div>\n <div class=\"dkv\"><span class=\"dk\">Driver class</span><span class=\"dv ps-mono\">{{ card.algo.DriverClass }}</span></div>\n @if (hyperparams(card.algo).length > 0) {\n <div class=\"dk\" style=\"margin-top:8px\">Default hyperparameters</div>\n <div class=\"hp-list\">\n @for (hp of hyperparams(card.algo); track hp.k) {\n <div class=\"hp\"><span class=\"ps-mono\">{{ hp.k }}</span><span class=\"ps-mono ps-muted\">{{ hp.v }}</span></div>\n }\n </div>\n } @else {\n <div class=\"ps-small ps-muted\" style=\"margin-top:6px\">Sensible defaults are applied at training time.</div>\n }\n <div class=\"adetail-foot\" style=\"display: flex; gap: 8px;\">\n <button mjButton variant=\"secondary\" size=\"sm\" (click)=\"onCreatePipeline(card.algo)\">\n <i class=\"fa-solid fa-plus\"></i> Create Pipeline with this algorithm\n </button>\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"useAlgorithm(card.algo)\">\n <i class=\"fa-solid fa-robot\"></i> Ask Agent\n </button>\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n `,\n})\nexport class PSCatalogComponent implements OnInit {\n @Input() engine!: PredictiveStudioEngine;\n\n /** Emitted with a starter prompt to open + seed the Model Development Agent chat. */\n @Output() askAgent = new EventEmitter<string>();\n /** Emitted when the user chooses to build a new training pipeline with this algorithm. */\n @Output() createPipeline = new EventEmitter<MJMLAlgorithmEntity>();\n\n public onCreatePipeline(algo: MJMLAlgorithmEntity): void {\n this.createPipeline.emit(algo);\n }\n\n public selectedUseCaseIds: string[] = [];\n public cards: AlgoCardVM[] = [];\n /** The algorithm whose inline detail panel is expanded (null = none). */\n public detailId: string | null = null;\n\n ngOnInit(): void {\n this.rebuildCards();\n }\n\n public get useCases(): MJMLAlgorithmUseCaseEntity[] {\n return this.engine?.UseCases ?? [];\n }\n\n public useCaseIcon(uc: MJMLAlgorithmUseCaseEntity): string {\n const name = (uc.Name || '').toLowerCase();\n if (name.includes('binary')) return USE_CASE_ICONS['binary'];\n if (name.includes('regression')) return USE_CASE_ICONS['regression'];\n if (name.includes('interpret')) return USE_CASE_ICONS['interpret'];\n if (name.includes('tuning') || name.includes('minimal')) return USE_CASE_ICONS['tuning'];\n if (name.includes('large') || name.includes('wide') || name.includes('speed')) return USE_CASE_ICONS['large'];\n if (name.includes('embedding') || name.includes('llm')) return USE_CASE_ICONS['embedding'];\n if (name.includes('small')) return USE_CASE_ICONS['small'];\n return 'fa-solid fa-circle-dot';\n }\n\n public isSelected(id: string): boolean {\n return this.selectedUseCaseIds.some((s) => UUIDsEqual(s, id));\n }\n\n public toggleUseCase(id: string): void {\n if (this.isSelected(id)) {\n this.selectedUseCaseIds = this.selectedUseCaseIds.filter((s) => !UUIDsEqual(s, id));\n } else {\n this.selectedUseCaseIds = [...this.selectedUseCaseIds, id];\n }\n this.rebuildCards();\n }\n\n public clearScenarios(): void {\n this.selectedUseCaseIds = [];\n this.rebuildCards();\n }\n\n // ---- algorithm card actions ----\n\n /** Toggle the inline detail panel for an algorithm card. */\n public toggleDetail(id: string): void {\n this.detailId = this.detailId === id ? null : id;\n }\n\n /** Seed the Model Development Agent to build a model with the chosen algorithm. */\n public useAlgorithm(algo: MJMLAlgorithmEntity): void {\n this.askAgent.emit(\n `Help me build a predictive model using the ${algo.Name} algorithm. ` +\n `Walk me through choosing the entity, the outcome to predict, and the features to assemble.`,\n );\n }\n\n /** Parse an algorithm's `DefaultHyperparameters` JSON into a small key/value list (≤8 rows). */\n public hyperparams(algo: MJMLAlgorithmEntity): { k: string; v: string }[] {\n const raw = algo.DefaultHyperparameters;\n if (!raw) {\n return [];\n }\n try {\n const parsed: unknown = JSON.parse(raw);\n if (!parsed || typeof parsed !== 'object') {\n return [];\n }\n return Object.entries(parsed as Record<string, unknown>)\n .map(([k, v]) => ({ k, v: String(v) }))\n .slice(0, 8);\n } catch {\n return [];\n }\n }\n\n public rank(level: RecommendationLevel): number {\n return RECOMMENDATION_RANK[level];\n }\n\n public levelLabel(level: RecommendationLevel): string {\n switch (level) {\n case 'Primary': return 'Primary fit';\n case 'Strong': return 'Strong fit';\n case 'Viable': return 'Viable';\n case 'Weak': return 'Weak fit';\n case 'NotRecommended': return 'N/A';\n }\n }\n\n /**\n * Structured recommendation data for the \"Guide me\" banner — rendered with plain template\n * interpolation + `<strong>` markup (no `[innerHTML]`). `scenarioLabel` is the human-readable\n * scenario name(s); `primaries` is the list of Primary-fit algorithm names.\n */\n public get recommendation(): { scenarioLabel: string; primaries: string[] } {\n const levels = this.engine.BestLevelsForScenarios(this.selectedUseCaseIds);\n const primaries = this.engine.Algorithms.filter((a) => levels.get(a.ID) === 'Primary').map((a) => a.Name);\n const scenarioLabel =\n this.selectedUseCaseIds.length === 1\n ? this.useCases.find((u) => UUIDsEqual(u.ID, this.selectedUseCaseIds[0]))?.Name ?? 'your scenario'\n : `${this.selectedUseCaseIds.length} scenarios`;\n return { scenarioLabel, primaries };\n }\n\n private rebuildCards(): void {\n const levels = this.engine.BestLevelsForScenarios(this.selectedUseCaseIds);\n const cards: AlgoCardVM[] = this.engine.Algorithms.map((algo) => ({\n algo,\n icon: ALGO_ICONS[algo.DriverClass] ?? 'fa-solid fa-shapes',\n problemTypes: (algo.ProblemTypes || '').split(',').map((p) => p.trim()).filter((p) => p.length > 0),\n bestLevel: levels.get(algo.ID) ?? null,\n }));\n if (this.selectedUseCaseIds.length > 0) {\n cards.sort((a, b) => {\n const ra = a.bestLevel ? RECOMMENDATION_RANK[a.bestLevel] : -1;\n const rb = b.bestLevel ? RECOMMENDATION_RANK[b.bestLevel] : -1;\n return rb - ra;\n });\n }\n this.cards = cards;\n }\n}\n"]}