@memberjunction/ng-dashboards 6.2.0-edge.4 → 6.2.0-edge.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts +1 -1
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js +15 -7
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts +7 -1
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js +209 -57
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-run-scope.d.ts +12 -11
- package/dist/AI/components/duplicates/duplicate-detection-run-scope.d.ts.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-run-scope.js +15 -15
- package/dist/AI/components/duplicates/duplicate-detection-run-scope.js.map +1 -1
- package/dist/AI/components/execution-monitoring.component.d.ts.map +1 -1
- package/dist/AI/components/execution-monitoring.component.js +28 -16
- package/dist/AI/components/execution-monitoring.component.js.map +1 -1
- package/dist/AI/components/prompts/model-prompt-priority-matrix.component.d.ts +1 -0
- package/dist/AI/components/prompts/model-prompt-priority-matrix.component.d.ts.map +1 -1
- package/dist/AI/components/prompts/model-prompt-priority-matrix.component.js +30 -6
- package/dist/AI/components/prompts/model-prompt-priority-matrix.component.js.map +1 -1
- package/dist/AI/components/tags/tags-resource.component.d.ts +1 -1
- package/dist/AI/components/tags/tags-resource.component.d.ts.map +1 -1
- package/dist/AI/components/tags/tags-resource.component.js +15 -7
- package/dist/AI/components/tags/tags-resource.component.js.map +1 -1
- package/dist/AI/components/vectors/sensitive-fields.d.ts +58 -0
- package/dist/AI/components/vectors/sensitive-fields.d.ts.map +1 -0
- package/dist/AI/components/vectors/sensitive-fields.js +251 -0
- package/dist/AI/components/vectors/sensitive-fields.js.map +1 -0
- package/dist/AI/components/vectors/vector-document-rules.d.ts +139 -0
- package/dist/AI/components/vectors/vector-document-rules.d.ts.map +1 -0
- package/dist/AI/components/vectors/vector-document-rules.js +182 -0
- package/dist/AI/components/vectors/vector-document-rules.js.map +1 -0
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts +83 -14
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.js +546 -271
- package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
- package/dist/AI/components/vectors/vector-store-selection.d.ts +32 -0
- package/dist/AI/components/vectors/vector-store-selection.d.ts.map +1 -0
- package/dist/AI/components/vectors/vector-store-selection.js +57 -0
- package/dist/AI/components/vectors/vector-store-selection.js.map +1 -0
- package/dist/ChiefOfStaff/chief-of-staff-agent-context.d.ts +236 -0
- package/dist/ChiefOfStaff/chief-of-staff-agent-context.d.ts.map +1 -0
- package/dist/ChiefOfStaff/chief-of-staff-agent-context.js +374 -0
- package/dist/ChiefOfStaff/chief-of-staff-agent-context.js.map +1 -0
- package/dist/ChiefOfStaff/chief-of-staff-dashboard.component.d.ts +195 -0
- package/dist/ChiefOfStaff/chief-of-staff-dashboard.component.d.ts.map +1 -0
- package/dist/ChiefOfStaff/chief-of-staff-dashboard.component.js +2910 -0
- package/dist/ChiefOfStaff/chief-of-staff-dashboard.component.js.map +1 -0
- package/dist/ChiefOfStaff/index.d.ts +3 -0
- package/dist/ChiefOfStaff/index.d.ts.map +1 -0
- package/dist/ChiefOfStaff/index.js +3 -0
- package/dist/ChiefOfStaff/index.js.map +1 -0
- package/dist/Communication/communication-agent-context.d.ts +11 -1
- package/dist/Communication/communication-agent-context.d.ts.map +1 -1
- package/dist/Communication/communication-agent-context.js +15 -0
- package/dist/Communication/communication-agent-context.js.map +1 -1
- package/dist/Communication/communication-dashboard.component.d.ts.map +1 -1
- package/dist/Communication/communication-dashboard.component.js +5 -0
- package/dist/Communication/communication-dashboard.component.js.map +1 -1
- package/dist/Communication/communication-logs-resource.component.d.ts.map +1 -1
- package/dist/Communication/communication-logs-resource.component.js +2 -1
- package/dist/Communication/communication-logs-resource.component.js.map +1 -1
- package/dist/Communication/communication-monitor-resource.component.d.ts.map +1 -1
- package/dist/Communication/communication-monitor-resource.component.js +23 -18
- package/dist/Communication/communication-monitor-resource.component.js.map +1 -1
- package/dist/Communication/communication-providers-resource.component.d.ts +27 -1
- package/dist/Communication/communication-providers-resource.component.d.ts.map +1 -1
- package/dist/Communication/communication-providers-resource.component.js +722 -78
- package/dist/Communication/communication-providers-resource.component.js.map +1 -1
- package/dist/Communication/communication-runs-resource.component.d.ts.map +1 -1
- package/dist/Communication/communication-runs-resource.component.js +11 -10
- package/dist/Communication/communication-runs-resource.component.js.map +1 -1
- package/dist/DashboardEditor/dashboard-editor.component.d.ts +17 -2
- package/dist/DashboardEditor/dashboard-editor.component.d.ts.map +1 -1
- package/dist/DashboardEditor/dashboard-editor.component.js +94 -43
- package/dist/DashboardEditor/dashboard-editor.component.js.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +49 -0
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.js +220 -55
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/DataExplorer/models/explorer-state.interface.d.ts +2 -0
- package/dist/DataExplorer/models/explorer-state.interface.d.ts.map +1 -1
- package/dist/DataExplorer/models/explorer-state.interface.js.map +1 -1
- package/dist/DevTools/app-state-inspector.component.d.ts.map +1 -1
- package/dist/DevTools/app-state-inspector.component.js +1 -0
- package/dist/DevTools/app-state-inspector.component.js.map +1 -1
- package/dist/DevTools/class-registry.component.d.ts.map +1 -1
- package/dist/DevTools/class-registry.component.js +1 -0
- package/dist/DevTools/class-registry.component.js.map +1 -1
- package/dist/DevTools/event-monitor.component.d.ts.map +1 -1
- package/dist/DevTools/event-monitor.component.js +2 -0
- package/dist/DevTools/event-monitor.component.js.map +1 -1
- package/dist/DevTools/graphql-console.component.d.ts.map +1 -1
- package/dist/DevTools/graphql-console.component.js.map +1 -1
- package/dist/DevTools/grid-width-lab.component.d.ts.map +1 -1
- package/dist/DevTools/grid-width-lab.component.js +1 -0
- package/dist/DevTools/grid-width-lab.component.js.map +1 -1
- package/dist/DevTools/layout-inspector.component.d.ts.map +1 -1
- package/dist/DevTools/layout-inspector.component.js +1 -0
- package/dist/DevTools/layout-inspector.component.js.map +1 -1
- package/dist/DevTools/lazy-module-status.component.d.ts.map +1 -1
- package/dist/DevTools/lazy-module-status.component.js +1 -0
- package/dist/DevTools/lazy-module-status.component.js.map +1 -1
- package/dist/DevTools/settings-explorer.component.d.ts.map +1 -1
- package/dist/DevTools/settings-explorer.component.js +1 -0
- package/dist/DevTools/settings-explorer.component.js.map +1 -1
- package/dist/DevTools/tab-strip-lab.component.d.ts.map +1 -1
- package/dist/DevTools/tab-strip-lab.component.js +1 -0
- package/dist/DevTools/tab-strip-lab.component.js.map +1 -1
- package/dist/EntityAdmin/entity-admin-dashboard.component.d.ts +5 -0
- package/dist/EntityAdmin/entity-admin-dashboard.component.d.ts.map +1 -1
- package/dist/EntityAdmin/entity-admin-dashboard.component.js +8 -5
- package/dist/EntityAdmin/entity-admin-dashboard.component.js.map +1 -1
- package/dist/Integration/components/overview/overview.component.js +2 -2
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js +2 -2
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js.map +1 -1
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.d.ts +11 -1
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.d.ts.map +1 -1
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.js +24 -2
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.js.map +1 -1
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.d.ts +16 -2
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.d.ts.map +1 -1
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js +87 -33
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js.map +1 -1
- package/dist/LoginStudio/login-studio-inspector.component.d.ts +130 -0
- package/dist/LoginStudio/login-studio-inspector.component.d.ts.map +1 -0
- package/dist/LoginStudio/login-studio-inspector.component.js +781 -0
- package/dist/LoginStudio/login-studio-inspector.component.js.map +1 -0
- package/dist/LoginStudio/login-studio-resource.component.d.ts +161 -0
- package/dist/LoginStudio/login-studio-resource.component.d.ts.map +1 -0
- package/dist/LoginStudio/login-studio-resource.component.js +890 -0
- package/dist/LoginStudio/login-studio-resource.component.js.map +1 -0
- package/dist/MCP/mcp-dashboard.component.d.ts +24 -4
- package/dist/MCP/mcp-dashboard.component.d.ts.map +1 -1
- package/dist/MCP/mcp-dashboard.component.js +118 -65
- package/dist/MCP/mcp-dashboard.component.js.map +1 -1
- package/dist/PredictiveStudio/business-predictions.view-models.d.ts +1 -1
- package/dist/PredictiveStudio/business-predictions.view-models.d.ts.map +1 -1
- package/dist/PredictiveStudio/business-predictions.view-models.js +3 -3
- package/dist/PredictiveStudio/business-predictions.view-models.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-experiments.component.d.ts +6 -63
- package/dist/PredictiveStudio/components/ps-experiments.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/components/ps-experiments.component.js +256 -521
- package/dist/PredictiveStudio/components/ps-experiments.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.d.ts +0 -18
- package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.js +122 -249
- package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts +5 -9
- package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/components/ps-pipelines.component.js +98 -130
- package/dist/PredictiveStudio/components/ps-pipelines.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-predictions-grid.component.d.ts +53 -4
- package/dist/PredictiveStudio/components/ps-predictions-grid.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/components/ps-predictions-grid.component.js +335 -326
- package/dist/PredictiveStudio/components/ps-predictions-grid.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-row-drivers.component.d.ts +19 -0
- package/dist/PredictiveStudio/components/ps-row-drivers.component.d.ts.map +1 -0
- package/dist/PredictiveStudio/components/ps-row-drivers.component.js +85 -0
- package/dist/PredictiveStudio/components/ps-row-drivers.component.js.map +1 -0
- package/dist/PredictiveStudio/engine/predictive-studio.engine.d.ts +19 -1
- package/dist/PredictiveStudio/engine/predictive-studio.engine.d.ts.map +1 -1
- package/dist/PredictiveStudio/engine/predictive-studio.engine.js +51 -2
- package/dist/PredictiveStudio/engine/predictive-studio.engine.js.map +1 -1
- package/dist/PredictiveStudio/predictive-studio-agent-context.d.ts +3 -1
- package/dist/PredictiveStudio/predictive-studio-agent-context.d.ts.map +1 -1
- package/dist/PredictiveStudio/predictive-studio-agent-context.js +3 -0
- package/dist/PredictiveStudio/predictive-studio-agent-context.js.map +1 -1
- package/dist/PredictiveStudio/predictive-studio-score-history.service.d.ts +27 -4
- package/dist/PredictiveStudio/predictive-studio-score-history.service.d.ts.map +1 -1
- package/dist/PredictiveStudio/predictive-studio-score-history.service.js +94 -61
- package/dist/PredictiveStudio/predictive-studio-score-history.service.js.map +1 -1
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts +29 -5
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js +185 -103
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js.map +1 -1
- package/dist/PredictiveStudio/resources/ps-studio-resource.component.js +1 -1
- package/dist/PredictiveStudio/resources/ps-studio-resource.component.js.map +1 -1
- package/dist/Workflows/components/workflow-runs-resource.component.js +4 -4
- package/dist/Workflows/components/workflow-runs-resource.component.js.map +1 -1
- package/dist/ai-dashboards.module.d.ts +22 -21
- package/dist/ai-dashboards.module.d.ts.map +1 -1
- package/dist/ai-dashboards.module.js +8 -2
- package/dist/ai-dashboards.module.js.map +1 -1
- package/dist/core-dashboards.module.d.ts +2 -1
- package/dist/core-dashboards.module.d.ts.map +1 -1
- package/dist/core-dashboards.module.js +11 -3
- package/dist/core-dashboards.module.js.map +1 -1
- package/dist/public-api.d.ts +3 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +4 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +72 -69
- package/dist/PredictiveStudio/at-risk.view-models.d.ts +0 -120
- package/dist/PredictiveStudio/at-risk.view-models.d.ts.map +0 -1
- package/dist/PredictiveStudio/at-risk.view-models.js +0 -261
- package/dist/PredictiveStudio/at-risk.view-models.js.map +0 -1
- package/dist/PredictiveStudio/components/ps-confirm-modal.component.d.ts +0 -109
- package/dist/PredictiveStudio/components/ps-confirm-modal.component.d.ts.map +0 -1
- package/dist/PredictiveStudio/components/ps-confirm-modal.component.js +0 -323
- package/dist/PredictiveStudio/components/ps-confirm-modal.component.js.map +0 -1
- package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.d.ts +0 -137
- package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.d.ts.map +0 -1
- package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.js +0 -826
- package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.js.map +0 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BaseEngine, IMetadataProvider, UserInfo } from '@memberjunction/core';
|
|
2
2
|
import { MJRecordProcessEntity, MJProcessRunEntity } from '@memberjunction/core-entities';
|
|
3
|
+
import { type FeaturePipelineRunResult } from '@memberjunction/feature-pipelines';
|
|
3
4
|
import type { Observable } from 'rxjs';
|
|
4
5
|
/**
|
|
5
6
|
* Name of the seeded `MJ: Record Process Categories` row that marks a Record
|
|
@@ -55,6 +56,10 @@ export interface FeaturePipelineSummary {
|
|
|
55
56
|
LastRunSuccess: number | null;
|
|
56
57
|
/** Records that errored in the most recent run, or null. */
|
|
57
58
|
LastRunErrors: number | null;
|
|
59
|
+
/** What the most recent run did with its records ({@link FeaturePipelineEngine.DescribeRunOutcome}), or null for a run without a result. */
|
|
60
|
+
LastRunOutcomeText: string | null;
|
|
61
|
+
/** The most recent run's error message when it did not complete (a refused or failed run), else null. */
|
|
62
|
+
LastRunErrorMessage: string | null;
|
|
58
63
|
/**
|
|
59
64
|
* The pipeline type ('LLM' or 'Decision' or custom). Defaults to 'LLM' when unset. The two built-in
|
|
60
65
|
* names are given in their canonical case whatever case the spec uses, as the processor matches them.
|
|
@@ -109,7 +114,12 @@ export declare class FeaturePipelineEngine extends BaseEngine<FeaturePipelineEng
|
|
|
109
114
|
private static isFeaturePipeline;
|
|
110
115
|
/** Project one pipeline + the run set into a {@link FeaturePipelineSummary}. */
|
|
111
116
|
private static buildSummary;
|
|
112
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* The card's last-run line from a run's result. Records skipped on their watermark count as unchanged: they still
|
|
119
|
+
* hold what the pipeline wrote. A completed run with nothing to write is "up to date".
|
|
120
|
+
*/
|
|
121
|
+
static DescribeRunOutcome(result: FeaturePipelineRunResult, run: MJProcessRunEntity): string;
|
|
122
|
+
/** Newest real (not dry) run for a pipeline id, scanning by StartTime/EndTime/createdAt regardless of input order. */
|
|
113
123
|
private static newestRun;
|
|
114
124
|
/** Normalize a Process Run status into the small union the UI renders. `undefined` → `'Never'`. */
|
|
115
125
|
private static mapRunStatus;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-pipeline.engine.d.ts","sourceRoot":"","sources":["../../../../src/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAEV,iBAAiB,EAEjB,QAAQ,EACT,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAC1F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEvC;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,qBAAqB,CAAC;AAEjE,mGAAmG;AACnG,MAAM,MAAM,wBAAwB,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;AAElG;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,oGAAoG;IACpG,EAAE,EAAE,MAAM,CAAC;IACX,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,uCAAuC;IACvC,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC;IACxC;;;;OAIG;IACH,QAAQ,EAAE,qBAAqB,CAAC,UAAU,CAAC,CAAC;IAC5C,+EAA+E;IAC/E,cAAc,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,0FAA0F;IAC1F,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,6EAA6E;IAC7E,eAAe,EAAE,OAAO,CAAC;IACzB,oDAAoD;IACpD,eAAe,EAAE,OAAO,CAAC;IACzB,wFAAwF;IACxF,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,gDAAgD;IAChD,aAAa,EAAE,wBAAwB,CAAC;IACxC,6DAA6D;IAC7D,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,yDAAyD;IACzD,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,2EAA2E;IAC3E,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,4DAA4D;IAC5D,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;CACtB;AAUD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,qBAAsB,SAAQ,UAAU,CAAC,qBAAqB,CAAC;IAC1E,+EAA+E;IAC/E,WAAkB,QAAQ,IAAI,qBAAqB,CAElD;IAED,OAAO,CAAC,gBAAgB,CAA+B;IACvD,OAAO,CAAC,YAAY,CAA4B;IAEhD;;;;;OAKG;IACU,MAAM,CACjB,YAAY,CAAC,EAAE,OAAO,EACtB,WAAW,CAAC,EAAE,QAAQ,EACtB,QAAQ,CAAC,EAAE,iBAAiB,GAC3B,OAAO,CAAC,IAAI,CAAC;IAUhB,qEAAqE;IACrE,IAAW,SAAS,IAAI,qBAAqB,EAAE,CAE9C;IAED,oFAAoF;IACpF,IAAW,IAAI,IAAI,kBAAkB,EAAE,CAEtC;IAED,mGAAmG;IACnG,IAAW,UAAU,IAAI,UAAU,CAAC,qBAAqB,EAAE,CAAC,CAE3D;IAED,kDAAkD;IAClD,IAAW,KAAK,IAAI,UAAU,CAAC,kBAAkB,EAAE,CAAC,CAEnD;IAID,gFAAgF;IACzE,YAAY,IAAI,sBAAsB,EAAE;IAI/C;;;OAGG;WACW,cAAc,CAC1B,SAAS,EAAE,aAAa,CAAC,qBAAqB,CAAC,EAC/C,IAAI,EAAE,aAAa,CAAC,kBAAkB,CAAC,GACtC,sBAAsB,EAAE;IAM3B,gFAAgF;IAChF,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAIhC,gFAAgF;IAChF,OAAO,CAAC,MAAM,CAAC,YAAY;
|
|
1
|
+
{"version":3,"file":"feature-pipeline.engine.d.ts","sourceRoot":"","sources":["../../../../src/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAEV,iBAAiB,EAEjB,QAAQ,EACT,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAC1F,OAAO,EAA6B,KAAK,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAC7G,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEvC;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,qBAAqB,CAAC;AAEjE,mGAAmG;AACnG,MAAM,MAAM,wBAAwB,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;AAElG;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,oGAAoG;IACpG,EAAE,EAAE,MAAM,CAAC;IACX,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,uCAAuC;IACvC,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC;IACxC;;;;OAIG;IACH,QAAQ,EAAE,qBAAqB,CAAC,UAAU,CAAC,CAAC;IAC5C,+EAA+E;IAC/E,cAAc,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,0FAA0F;IAC1F,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,6EAA6E;IAC7E,eAAe,EAAE,OAAO,CAAC;IACzB,oDAAoD;IACpD,eAAe,EAAE,OAAO,CAAC;IACzB,wFAAwF;IACxF,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,gDAAgD;IAChD,aAAa,EAAE,wBAAwB,CAAC;IACxC,6DAA6D;IAC7D,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,yDAAyD;IACzD,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,2EAA2E;IAC3E,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,4DAA4D;IAC5D,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,4IAA4I;IAC5I,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,yGAAyG;IACzG,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;CACtB;AAUD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,qBAAsB,SAAQ,UAAU,CAAC,qBAAqB,CAAC;IAC1E,+EAA+E;IAC/E,WAAkB,QAAQ,IAAI,qBAAqB,CAElD;IAED,OAAO,CAAC,gBAAgB,CAA+B;IACvD,OAAO,CAAC,YAAY,CAA4B;IAEhD;;;;;OAKG;IACU,MAAM,CACjB,YAAY,CAAC,EAAE,OAAO,EACtB,WAAW,CAAC,EAAE,QAAQ,EACtB,QAAQ,CAAC,EAAE,iBAAiB,GAC3B,OAAO,CAAC,IAAI,CAAC;IAUhB,qEAAqE;IACrE,IAAW,SAAS,IAAI,qBAAqB,EAAE,CAE9C;IAED,oFAAoF;IACpF,IAAW,IAAI,IAAI,kBAAkB,EAAE,CAEtC;IAED,mGAAmG;IACnG,IAAW,UAAU,IAAI,UAAU,CAAC,qBAAqB,EAAE,CAAC,CAE3D;IAED,kDAAkD;IAClD,IAAW,KAAK,IAAI,UAAU,CAAC,kBAAkB,EAAE,CAAC,CAEnD;IAID,gFAAgF;IACzE,YAAY,IAAI,sBAAsB,EAAE;IAI/C;;;OAGG;WACW,cAAc,CAC1B,SAAS,EAAE,aAAa,CAAC,qBAAqB,CAAC,EAC/C,IAAI,EAAE,aAAa,CAAC,kBAAkB,CAAC,GACtC,sBAAsB,EAAE;IAM3B,gFAAgF;IAChF,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAIhC,gFAAgF;IAChF,OAAO,CAAC,MAAM,CAAC,YAAY;IA6B3B;;;OAGG;WACW,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,EAAE,GAAG,EAAE,kBAAkB,GAAG,MAAM;IAenG,sHAAsH;IACtH,OAAO,CAAC,MAAM,CAAC,SAAS;IAkBxB,mGAAmG;IACnG,OAAO,CAAC,MAAM,CAAC,YAAY;IAkB3B;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAkBpC,0EAA0E;IAC1E,OAAO,CAAC,MAAM,CAAC,cAAc;IAU7B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;CAQlC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BaseEngine, Metadata, } from '@memberjunction/core';
|
|
2
2
|
import { SafeJSONParse, UUIDsEqual } from '@memberjunction/global';
|
|
3
|
+
import { FeaturePipelineRunResults } from '@memberjunction/feature-pipelines';
|
|
3
4
|
/**
|
|
4
5
|
* Name of the seeded `MJ: Record Process Categories` row that marks a Record
|
|
5
6
|
* Process as a Feature Pipeline. Authored in
|
|
@@ -86,6 +87,7 @@ export class FeaturePipelineEngine extends BaseEngine {
|
|
|
86
87
|
/** Project one pipeline + the run set into a {@link FeaturePipelineSummary}. */
|
|
87
88
|
static buildSummary(p, runs) {
|
|
88
89
|
const latest = FeaturePipelineEngine.newestRun(runs, p.ID);
|
|
90
|
+
const result = FeaturePipelineRunResults.Parse(latest?.ResultSummary);
|
|
89
91
|
return {
|
|
90
92
|
ID: p.ID,
|
|
91
93
|
Name: p.Name,
|
|
@@ -103,15 +105,35 @@ export class FeaturePipelineEngine extends BaseEngine {
|
|
|
103
105
|
LastRunProcessed: latest?.ProcessedItems ?? null,
|
|
104
106
|
LastRunSuccess: latest?.SuccessCount ?? null,
|
|
105
107
|
LastRunErrors: latest?.ErrorCount ?? null,
|
|
108
|
+
LastRunOutcomeText: latest && result ? FeaturePipelineEngine.DescribeRunOutcome(result, latest) : null,
|
|
109
|
+
LastRunErrorMessage: latest && latest.Status !== 'Completed' ? latest.ErrorMessage || null : null,
|
|
106
110
|
PipelineType: FeaturePipelineEngine.derivePipelineType(p.Configuration),
|
|
107
111
|
};
|
|
108
112
|
}
|
|
109
|
-
/**
|
|
113
|
+
/**
|
|
114
|
+
* The card's last-run line from a run's result. Records skipped on their watermark count as unchanged: they still
|
|
115
|
+
* hold what the pipeline wrote. A completed run with nothing to write is "up to date".
|
|
116
|
+
*/
|
|
117
|
+
static DescribeRunOutcome(result, run) {
|
|
118
|
+
const total = run.ProcessedItems || 0;
|
|
119
|
+
const failed = run.ErrorCount || 0;
|
|
120
|
+
const unchanged = result.Unchanged + result.Skipped;
|
|
121
|
+
if (total === 0) {
|
|
122
|
+
return 'no records processed';
|
|
123
|
+
}
|
|
124
|
+
if (run.Status === 'Completed' && result.Upserted === 0 && failed === 0) {
|
|
125
|
+
return `${unchanged} of ${total} up to date (${unchanged} unchanged)`;
|
|
126
|
+
}
|
|
127
|
+
const parts = [[result.Upserted, 'written'], [unchanged, 'unchanged'], [failed, 'failed']];
|
|
128
|
+
const shown = parts.filter(([count]) => count > 0).map(([count, label]) => `${count} ${label}`);
|
|
129
|
+
return `${shown.length > 0 ? shown.join(', ') : '0 written'} of ${total}`;
|
|
130
|
+
}
|
|
131
|
+
/** Newest real (not dry) run for a pipeline id, scanning by StartTime/EndTime/createdAt regardless of input order. */
|
|
110
132
|
static newestRun(runs, recordProcessID) {
|
|
111
133
|
let best;
|
|
112
134
|
let bestTime = -Infinity;
|
|
113
135
|
for (const r of runs) {
|
|
114
|
-
if (r.RecordProcessID == null || !UUIDsEqual(r.RecordProcessID, recordProcessID))
|
|
136
|
+
if (r.RecordProcessID == null || !UUIDsEqual(r.RecordProcessID, recordProcessID) || r.DryRun)
|
|
115
137
|
continue;
|
|
116
138
|
const candidate = r.StartTime ?? r.EndTime ?? r.__mj_CreatedAt;
|
|
117
139
|
const t = candidate ? new Date(candidate).getTime() : -Infinity;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-pipeline.engine.js","sourceRoot":"","sources":["../../../../src/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BaseEngine,\n BaseEnginePropertyConfig,\n IMetadataProvider,\n Metadata,\n UserInfo,\n} from '@memberjunction/core';\nimport { SafeJSONParse, UUIDsEqual } from '@memberjunction/global';\nimport { MJRecordProcessEntity, MJProcessRunEntity } from '@memberjunction/core-entities';\nimport type { Observable } from 'rxjs';\n\n/**\n * Name of the seeded `MJ: Record Process Categories` row that marks a Record\n * Process as a Feature Pipeline. Authored in\n * `metadata/record-process-categories/.record-process-categories.json` and pushed\n * with `mj sync`. Mirrors `FEATURE_PIPELINE_CATEGORY_NAME` in the server-side\n * `@memberjunction/predictive-studio` engine — kept in sync by value (the two\n * packages don't share a dependency, so the constant is intentionally duplicated\n * rather than re-exported across the package boundary).\n */\nexport const FEATURE_PIPELINE_CATEGORY_NAME = 'Feature Pipeline';\n\n/** Normalized last-run status for a Feature Pipeline, derived from the most recent Process Run. */\nexport type FeaturePipelineRunStatus = 'Never' | 'Running' | 'Completed' | 'Failed' | 'Cancelled';\n\n/**\n * UI-facing summary of one Feature Pipeline: what it is, the entity it writes to,\n * the attribute it produces, and last-run / freshness. Built purely from the\n * cached Record Process + Process Run entities by {@link FeaturePipelineEngine.GetSummaries}.\n */\nexport interface FeaturePipelineSummary {\n /** The underlying `MJ: Record Processes` id (also the `RunFeaturePipeline` `featurePipelineID`). */\n ID: string;\n /** Display name (the Record Process name). */\n Name: string;\n /** Optional description. */\n Description: string | null;\n /** Record Process lifecycle status. */\n Status: 'Active' | 'Disabled' | 'Draft';\n /**\n * The per-record work the pipeline performs. Derived from the entity's `WorkType` so it tracks\n * the CodeGen-generated union (sourced from the `CK_RecordProcess_WorkType` CHECK constraint) and\n * never drifts as work types are added (e.g. `'ML Model'`).\n */\n WorkType: MJRecordProcessEntity['WorkType'];\n /** Id of the target entity whose records the pipeline derives features for. */\n TargetEntityID: string;\n /** Display name of the target entity. */\n TargetEntity: string;\n /** Best-effort attribute written back, parsed from the Record Process `OutputMapping`. */\n OutputAttribute: string | null;\n /** Whether the pipeline can be run on demand (the Run button is enabled). */\n OnDemandEnabled: boolean;\n /** Whether the pipeline runs on a cron schedule. */\n ScheduleEnabled: boolean;\n /** Timestamp of the most recent run (start time, falling back to end time), or null. */\n LastRunAt: Date | null;\n /** Normalized status of the most recent run. */\n LastRunStatus: FeaturePipelineRunStatus;\n /** Id of the most recent `MJ: Process Runs` row, or null. */\n LastRunProcessRunID: string | null;\n /** Records processed in the most recent run, or null. */\n LastRunProcessed: number | null;\n /** Records whose features were written in the most recent run, or null. */\n LastRunSuccess: number | null;\n /** Records that errored in the most recent run, or null. */\n LastRunErrors: number | null;\n /**\n * The pipeline type ('LLM' or 'Decision' or custom). Defaults to 'LLM' when unset. The two built-in\n * names are given in their canonical case whatever case the spec uses, as the processor matches them.\n */\n PipelineType: string;\n}\n\n/** The one DataFeatureSpec property the summary reads from a Record Process `Configuration`. */\ninterface ConfigurationPipelineType {\n PipelineType?: string | null;\n}\n\n/** The built-in pipeline type names, by their lower-case form. */\nconst BUILT_IN_PIPELINE_TYPES: Record<string, string> = { llm: 'LLM', decision: 'Decision' };\n\n/**\n * **FeaturePipelineEngine** (Knowledge Hub, Angular) — the discovery + monitoring\n * cache for Feature Pipelines (Predictive Studio SP6).\n *\n * A Feature Pipeline has no dedicated entity: it IS an `MJ: Record Processes` row\n * categorized into the seeded {@link FEATURE_PIPELINE_CATEGORY_NAME} category. This\n * engine caches those rows (plus the recent `MJ: Process Runs` headers) so the KH\n * UI can list \"what feature pipelines exist, what entity each writes to, and when\n * each last ran\" — reactively, via the canonical MJ BaseEngine pattern (`Config()`\n * declaring `BaseEnginePropertyConfig[]`, lazy-loaded, `ObserveProperty`\n * reactivity for free).\n *\n * The freshness/projection logic is a pure static function ({@link BuildSummaries})\n * so it can be exercised without a provider, and it intentionally mirrors the\n * server-side `@memberjunction/predictive-studio` engine of the same name (the two\n * don't share a dependency).\n */\nexport class FeaturePipelineEngine extends BaseEngine<FeaturePipelineEngine> {\n /** Singleton accessor for the default provider — never `new` this directly. */\n public static get Instance(): FeaturePipelineEngine {\n return super.getInstance<FeaturePipelineEngine>();\n }\n\n private _recordProcesses: MJRecordProcessEntity[] = [];\n private _processRuns: MJProcessRunEntity[] = [];\n\n /**\n * Lazy-load the Record Process + recent Process Run caches. Safe to call from\n * every entry point — a no-op when already loaded (unless `forceRefresh`).\n * Process Runs are ordered newest-first so the most recent run per pipeline is\n * the first match.\n */\n public async Config(\n forceRefresh?: boolean,\n contextUser?: UserInfo,\n provider?: IMetadataProvider,\n ): Promise<void> {\n const c: Partial<BaseEnginePropertyConfig>[] = [\n { Type: 'entity', EntityName: 'MJ: Record Processes', PropertyName: '_recordProcesses', OrderBy: 'Name' },\n { Type: 'entity', EntityName: 'MJ: Process Runs', PropertyName: '_processRuns', OrderBy: '__mj_CreatedAt DESC' },\n ];\n await super.Load(c, provider ?? Metadata.Provider, forceRefresh, contextUser);\n }\n\n // ---- Cached arrays (sync getters; observe via the streams for reactive UI) ----\n\n /** Every cached Record Process categorized as a Feature Pipeline. */\n public get Pipelines(): MJRecordProcessEntity[] {\n return (this._recordProcesses ?? []).filter((rp) => FeaturePipelineEngine.isFeaturePipeline(rp));\n }\n\n /** All cached Process Run headers (newest first; all entities — callers filter). */\n public get Runs(): MJProcessRunEntity[] {\n return this._processRuns ?? [];\n }\n\n /** Reactive stream of the raw Record Processes — re-emits on any save/delete/remote-invalidate. */\n public get Pipelines$(): Observable<MJRecordProcessEntity[]> {\n return this.ObserveProperty<MJRecordProcessEntity>('_recordProcesses');\n }\n\n /** Reactive stream of the Process Run headers. */\n public get Runs$(): Observable<MJProcessRunEntity[]> {\n return this.ObserveProperty<MJProcessRunEntity>('_processRuns');\n }\n\n // ---- Projection ----\n\n /** Build the UI summaries for every feature pipeline from the loaded caches. */\n public GetSummaries(): FeaturePipelineSummary[] {\n return FeaturePipelineEngine.BuildSummaries(this.Pipelines, this.Runs);\n }\n\n /**\n * Pure, side-effect-free summary builder — used by {@link GetSummaries} and by\n * unit tests (which pass in-memory arrays, no provider).\n */\n public static BuildSummaries(\n pipelines: ReadonlyArray<MJRecordProcessEntity>,\n runs: ReadonlyArray<MJProcessRunEntity>,\n ): FeaturePipelineSummary[] {\n return pipelines.map((p) => FeaturePipelineEngine.buildSummary(p, runs));\n }\n\n // ---- Internal helpers ----\n\n /** Whether a Record Process belongs to the seeded Feature Pipeline category. */\n private static isFeaturePipeline(rp: MJRecordProcessEntity): boolean {\n return (rp.Category ?? '').trim().toLowerCase() === FEATURE_PIPELINE_CATEGORY_NAME.toLowerCase();\n }\n\n /** Project one pipeline + the run set into a {@link FeaturePipelineSummary}. */\n private static buildSummary(\n p: MJRecordProcessEntity,\n runs: ReadonlyArray<MJProcessRunEntity>,\n ): FeaturePipelineSummary {\n const latest = FeaturePipelineEngine.newestRun(runs, p.ID);\n return {\n ID: p.ID,\n Name: p.Name,\n Description: p.Description ?? null,\n Status: p.Status,\n WorkType: p.WorkType,\n TargetEntityID: p.EntityID,\n TargetEntity: p.Entity,\n OutputAttribute: FeaturePipelineEngine.deriveOutputAttribute(p.OutputMapping),\n OnDemandEnabled: p.OnDemandEnabled,\n ScheduleEnabled: p.ScheduleEnabled,\n LastRunAt: latest?.StartTime ?? latest?.EndTime ?? null,\n LastRunStatus: FeaturePipelineEngine.mapRunStatus(latest),\n LastRunProcessRunID: latest?.ID ?? null,\n LastRunProcessed: latest?.ProcessedItems ?? null,\n LastRunSuccess: latest?.SuccessCount ?? null,\n LastRunErrors: latest?.ErrorCount ?? null,\n PipelineType: FeaturePipelineEngine.derivePipelineType(p.Configuration),\n };\n }\n\n /** Newest run for a pipeline id, scanning by StartTime/EndTime/createdAt regardless of input order. */\n private static newestRun(\n runs: ReadonlyArray<MJProcessRunEntity>,\n recordProcessID: string,\n ): MJProcessRunEntity | undefined {\n let best: MJProcessRunEntity | undefined;\n let bestTime = -Infinity;\n for (const r of runs) {\n if (r.RecordProcessID == null || !UUIDsEqual(r.RecordProcessID, recordProcessID)) continue;\n const candidate = r.StartTime ?? r.EndTime ?? r.__mj_CreatedAt;\n const t = candidate ? new Date(candidate).getTime() : -Infinity;\n if (t > bestTime) {\n bestTime = t;\n best = r;\n }\n }\n return best;\n }\n\n /** Normalize a Process Run status into the small union the UI renders. `undefined` → `'Never'`. */\n private static mapRunStatus(run: MJProcessRunEntity | undefined): FeaturePipelineRunStatus {\n if (!run) return 'Never';\n switch (run.Status) {\n case 'Completed':\n return 'Completed';\n case 'Failed':\n return 'Failed';\n case 'Cancelled':\n return 'Cancelled';\n case 'Running':\n case 'Pending':\n case 'Paused':\n return 'Running';\n default:\n return 'Never';\n }\n }\n\n /**\n * Best-effort attribute the pipeline writes, parsed from the Record Process\n * `OutputMapping` JSON. Probes the common shapes (`field` / `targetField` /\n * `attribute`, or a `fields` array / object) and returns null otherwise. Never\n * throws on malformed JSON.\n */\n private static deriveOutputAttribute(outputMapping: string | null): string | null {\n if (!outputMapping) return null;\n let parsed: unknown;\n try {\n parsed = JSON.parse(outputMapping);\n } catch {\n return null;\n }\n if (parsed == null || typeof parsed !== 'object') return null;\n const obj = parsed as Record<string, unknown>;\n\n const single = obj['field'] ?? obj['targetField'] ?? obj['attribute'];\n if (typeof single === 'string' && single.trim()) return single.trim();\n\n const names = FeaturePipelineEngine.fieldNamesFrom(obj['fields']);\n return names.length > 0 ? names.join(', ') : null;\n }\n\n /** Collect field names from either an object map or an array of names. */\n private static fieldNamesFrom(fields: unknown): string[] {\n if (Array.isArray(fields)) {\n return fields.filter((f): f is string => typeof f === 'string' && f.trim().length > 0);\n }\n if (fields != null && typeof fields === 'object') {\n return Object.keys(fields as Record<string, unknown>);\n }\n return [];\n }\n\n /**\n * Best-effort pipeline type derived from the Record Process `Configuration` JSON\n * (which serializes `DataFeatureSpec`). Returns 'LLM' when unset, empty, or unparseable, and a built-in\n * name ('decision') in its canonical case ('Decision').\n */\n private static derivePipelineType(config: string | null): string {\n const parsed = config ? SafeJSONParse<ConfigurationPipelineType>(config) : null;\n const typeName = parsed && typeof parsed === 'object' && typeof parsed.PipelineType === 'string' ? parsed.PipelineType.trim() : '';\n if (typeName.length === 0) {\n return 'LLM';\n }\n return BUILT_IN_PIPELINE_TYPES[typeName.toLowerCase()] ?? typeName;\n }\n}\n\n"]}
|
|
1
|
+
{"version":3,"file":"feature-pipeline.engine.js","sourceRoot":"","sources":["../../../../src/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BaseEngine,\n BaseEnginePropertyConfig,\n IMetadataProvider,\n Metadata,\n UserInfo,\n} from '@memberjunction/core';\nimport { SafeJSONParse, UUIDsEqual } from '@memberjunction/global';\nimport { MJRecordProcessEntity, MJProcessRunEntity } from '@memberjunction/core-entities';\nimport { FeaturePipelineRunResults, type FeaturePipelineRunResult } from '@memberjunction/feature-pipelines';\nimport type { Observable } from 'rxjs';\n\n/**\n * Name of the seeded `MJ: Record Process Categories` row that marks a Record\n * Process as a Feature Pipeline. Authored in\n * `metadata/record-process-categories/.record-process-categories.json` and pushed\n * with `mj sync`. Mirrors `FEATURE_PIPELINE_CATEGORY_NAME` in the server-side\n * `@memberjunction/predictive-studio` engine — kept in sync by value (the two\n * packages don't share a dependency, so the constant is intentionally duplicated\n * rather than re-exported across the package boundary).\n */\nexport const FEATURE_PIPELINE_CATEGORY_NAME = 'Feature Pipeline';\n\n/** Normalized last-run status for a Feature Pipeline, derived from the most recent Process Run. */\nexport type FeaturePipelineRunStatus = 'Never' | 'Running' | 'Completed' | 'Failed' | 'Cancelled';\n\n/**\n * UI-facing summary of one Feature Pipeline: what it is, the entity it writes to,\n * the attribute it produces, and last-run / freshness. Built purely from the\n * cached Record Process + Process Run entities by {@link FeaturePipelineEngine.GetSummaries}.\n */\nexport interface FeaturePipelineSummary {\n /** The underlying `MJ: Record Processes` id (also the `RunFeaturePipeline` `featurePipelineID`). */\n ID: string;\n /** Display name (the Record Process name). */\n Name: string;\n /** Optional description. */\n Description: string | null;\n /** Record Process lifecycle status. */\n Status: 'Active' | 'Disabled' | 'Draft';\n /**\n * The per-record work the pipeline performs. Derived from the entity's `WorkType` so it tracks\n * the CodeGen-generated union (sourced from the `CK_RecordProcess_WorkType` CHECK constraint) and\n * never drifts as work types are added (e.g. `'ML Model'`).\n */\n WorkType: MJRecordProcessEntity['WorkType'];\n /** Id of the target entity whose records the pipeline derives features for. */\n TargetEntityID: string;\n /** Display name of the target entity. */\n TargetEntity: string;\n /** Best-effort attribute written back, parsed from the Record Process `OutputMapping`. */\n OutputAttribute: string | null;\n /** Whether the pipeline can be run on demand (the Run button is enabled). */\n OnDemandEnabled: boolean;\n /** Whether the pipeline runs on a cron schedule. */\n ScheduleEnabled: boolean;\n /** Timestamp of the most recent run (start time, falling back to end time), or null. */\n LastRunAt: Date | null;\n /** Normalized status of the most recent run. */\n LastRunStatus: FeaturePipelineRunStatus;\n /** Id of the most recent `MJ: Process Runs` row, or null. */\n LastRunProcessRunID: string | null;\n /** Records processed in the most recent run, or null. */\n LastRunProcessed: number | null;\n /** Records whose features were written in the most recent run, or null. */\n LastRunSuccess: number | null;\n /** Records that errored in the most recent run, or null. */\n LastRunErrors: number | null;\n /** What the most recent run did with its records ({@link FeaturePipelineEngine.DescribeRunOutcome}), or null for a run without a result. */\n LastRunOutcomeText: string | null;\n /** The most recent run's error message when it did not complete (a refused or failed run), else null. */\n LastRunErrorMessage: string | null;\n /**\n * The pipeline type ('LLM' or 'Decision' or custom). Defaults to 'LLM' when unset. The two built-in\n * names are given in their canonical case whatever case the spec uses, as the processor matches them.\n */\n PipelineType: string;\n}\n\n/** The one DataFeatureSpec property the summary reads from a Record Process `Configuration`. */\ninterface ConfigurationPipelineType {\n PipelineType?: string | null;\n}\n\n/** The built-in pipeline type names, by their lower-case form. */\nconst BUILT_IN_PIPELINE_TYPES: Record<string, string> = { llm: 'LLM', decision: 'Decision' };\n\n/**\n * **FeaturePipelineEngine** (Knowledge Hub, Angular) — the discovery + monitoring\n * cache for Feature Pipelines (Predictive Studio SP6).\n *\n * A Feature Pipeline has no dedicated entity: it IS an `MJ: Record Processes` row\n * categorized into the seeded {@link FEATURE_PIPELINE_CATEGORY_NAME} category. This\n * engine caches those rows (plus the recent `MJ: Process Runs` headers) so the KH\n * UI can list \"what feature pipelines exist, what entity each writes to, and when\n * each last ran\" — reactively, via the canonical MJ BaseEngine pattern (`Config()`\n * declaring `BaseEnginePropertyConfig[]`, lazy-loaded, `ObserveProperty`\n * reactivity for free).\n *\n * The freshness/projection logic is a pure static function ({@link BuildSummaries})\n * so it can be exercised without a provider, and it intentionally mirrors the\n * server-side `@memberjunction/predictive-studio` engine of the same name (the two\n * don't share a dependency).\n */\nexport class FeaturePipelineEngine extends BaseEngine<FeaturePipelineEngine> {\n /** Singleton accessor for the default provider — never `new` this directly. */\n public static get Instance(): FeaturePipelineEngine {\n return super.getInstance<FeaturePipelineEngine>();\n }\n\n private _recordProcesses: MJRecordProcessEntity[] = [];\n private _processRuns: MJProcessRunEntity[] = [];\n\n /**\n * Lazy-load the Record Process + recent Process Run caches. Safe to call from\n * every entry point — a no-op when already loaded (unless `forceRefresh`).\n * Process Runs are ordered newest-first so the most recent run per pipeline is\n * the first match.\n */\n public async Config(\n forceRefresh?: boolean,\n contextUser?: UserInfo,\n provider?: IMetadataProvider,\n ): Promise<void> {\n const c: Partial<BaseEnginePropertyConfig>[] = [\n { Type: 'entity', EntityName: 'MJ: Record Processes', PropertyName: '_recordProcesses', OrderBy: 'Name' },\n { Type: 'entity', EntityName: 'MJ: Process Runs', PropertyName: '_processRuns', OrderBy: '__mj_CreatedAt DESC' },\n ];\n await super.Load(c, provider ?? Metadata.Provider, forceRefresh, contextUser);\n }\n\n // ---- Cached arrays (sync getters; observe via the streams for reactive UI) ----\n\n /** Every cached Record Process categorized as a Feature Pipeline. */\n public get Pipelines(): MJRecordProcessEntity[] {\n return (this._recordProcesses ?? []).filter((rp) => FeaturePipelineEngine.isFeaturePipeline(rp));\n }\n\n /** All cached Process Run headers (newest first; all entities — callers filter). */\n public get Runs(): MJProcessRunEntity[] {\n return this._processRuns ?? [];\n }\n\n /** Reactive stream of the raw Record Processes — re-emits on any save/delete/remote-invalidate. */\n public get Pipelines$(): Observable<MJRecordProcessEntity[]> {\n return this.ObserveProperty<MJRecordProcessEntity>('_recordProcesses');\n }\n\n /** Reactive stream of the Process Run headers. */\n public get Runs$(): Observable<MJProcessRunEntity[]> {\n return this.ObserveProperty<MJProcessRunEntity>('_processRuns');\n }\n\n // ---- Projection ----\n\n /** Build the UI summaries for every feature pipeline from the loaded caches. */\n public GetSummaries(): FeaturePipelineSummary[] {\n return FeaturePipelineEngine.BuildSummaries(this.Pipelines, this.Runs);\n }\n\n /**\n * Pure, side-effect-free summary builder — used by {@link GetSummaries} and by\n * unit tests (which pass in-memory arrays, no provider).\n */\n public static BuildSummaries(\n pipelines: ReadonlyArray<MJRecordProcessEntity>,\n runs: ReadonlyArray<MJProcessRunEntity>,\n ): FeaturePipelineSummary[] {\n return pipelines.map((p) => FeaturePipelineEngine.buildSummary(p, runs));\n }\n\n // ---- Internal helpers ----\n\n /** Whether a Record Process belongs to the seeded Feature Pipeline category. */\n private static isFeaturePipeline(rp: MJRecordProcessEntity): boolean {\n return (rp.Category ?? '').trim().toLowerCase() === FEATURE_PIPELINE_CATEGORY_NAME.toLowerCase();\n }\n\n /** Project one pipeline + the run set into a {@link FeaturePipelineSummary}. */\n private static buildSummary(\n p: MJRecordProcessEntity,\n runs: ReadonlyArray<MJProcessRunEntity>,\n ): FeaturePipelineSummary {\n const latest = FeaturePipelineEngine.newestRun(runs, p.ID);\n const result = FeaturePipelineRunResults.Parse(latest?.ResultSummary);\n return {\n ID: p.ID,\n Name: p.Name,\n Description: p.Description ?? null,\n Status: p.Status,\n WorkType: p.WorkType,\n TargetEntityID: p.EntityID,\n TargetEntity: p.Entity,\n OutputAttribute: FeaturePipelineEngine.deriveOutputAttribute(p.OutputMapping),\n OnDemandEnabled: p.OnDemandEnabled,\n ScheduleEnabled: p.ScheduleEnabled,\n LastRunAt: latest?.StartTime ?? latest?.EndTime ?? null,\n LastRunStatus: FeaturePipelineEngine.mapRunStatus(latest),\n LastRunProcessRunID: latest?.ID ?? null,\n LastRunProcessed: latest?.ProcessedItems ?? null,\n LastRunSuccess: latest?.SuccessCount ?? null,\n LastRunErrors: latest?.ErrorCount ?? null,\n LastRunOutcomeText: latest && result ? FeaturePipelineEngine.DescribeRunOutcome(result, latest) : null,\n LastRunErrorMessage: latest && latest.Status !== 'Completed' ? latest.ErrorMessage || null : null,\n PipelineType: FeaturePipelineEngine.derivePipelineType(p.Configuration),\n };\n }\n\n /**\n * The card's last-run line from a run's result. Records skipped on their watermark count as unchanged: they still\n * hold what the pipeline wrote. A completed run with nothing to write is \"up to date\".\n */\n public static DescribeRunOutcome(result: FeaturePipelineRunResult, run: MJProcessRunEntity): string {\n const total = run.ProcessedItems || 0;\n const failed = run.ErrorCount || 0;\n const unchanged = result.Unchanged + result.Skipped;\n if (total === 0) {\n return 'no records processed';\n }\n if (run.Status === 'Completed' && result.Upserted === 0 && failed === 0) {\n return `${unchanged} of ${total} up to date (${unchanged} unchanged)`;\n }\n const parts: Array<[number, string]> = [[result.Upserted, 'written'], [unchanged, 'unchanged'], [failed, 'failed']];\n const shown = parts.filter(([count]) => count > 0).map(([count, label]) => `${count} ${label}`);\n return `${shown.length > 0 ? shown.join(', ') : '0 written'} of ${total}`;\n }\n\n /** Newest real (not dry) run for a pipeline id, scanning by StartTime/EndTime/createdAt regardless of input order. */\n private static newestRun(\n runs: ReadonlyArray<MJProcessRunEntity>,\n recordProcessID: string,\n ): MJProcessRunEntity | undefined {\n let best: MJProcessRunEntity | undefined;\n let bestTime = -Infinity;\n for (const r of runs) {\n if (r.RecordProcessID == null || !UUIDsEqual(r.RecordProcessID, recordProcessID) || r.DryRun) continue;\n const candidate = r.StartTime ?? r.EndTime ?? r.__mj_CreatedAt;\n const t = candidate ? new Date(candidate).getTime() : -Infinity;\n if (t > bestTime) {\n bestTime = t;\n best = r;\n }\n }\n return best;\n }\n\n /** Normalize a Process Run status into the small union the UI renders. `undefined` → `'Never'`. */\n private static mapRunStatus(run: MJProcessRunEntity | undefined): FeaturePipelineRunStatus {\n if (!run) return 'Never';\n switch (run.Status) {\n case 'Completed':\n return 'Completed';\n case 'Failed':\n return 'Failed';\n case 'Cancelled':\n return 'Cancelled';\n case 'Running':\n case 'Pending':\n case 'Paused':\n return 'Running';\n default:\n return 'Never';\n }\n }\n\n /**\n * Best-effort attribute the pipeline writes, parsed from the Record Process\n * `OutputMapping` JSON. Probes the common shapes (`field` / `targetField` /\n * `attribute`, or a `fields` array / object) and returns null otherwise. Never\n * throws on malformed JSON.\n */\n private static deriveOutputAttribute(outputMapping: string | null): string | null {\n if (!outputMapping) return null;\n let parsed: unknown;\n try {\n parsed = JSON.parse(outputMapping);\n } catch {\n return null;\n }\n if (parsed == null || typeof parsed !== 'object') return null;\n const obj = parsed as Record<string, unknown>;\n\n const single = obj['field'] ?? obj['targetField'] ?? obj['attribute'];\n if (typeof single === 'string' && single.trim()) return single.trim();\n\n const names = FeaturePipelineEngine.fieldNamesFrom(obj['fields']);\n return names.length > 0 ? names.join(', ') : null;\n }\n\n /** Collect field names from either an object map or an array of names. */\n private static fieldNamesFrom(fields: unknown): string[] {\n if (Array.isArray(fields)) {\n return fields.filter((f): f is string => typeof f === 'string' && f.trim().length > 0);\n }\n if (fields != null && typeof fields === 'object') {\n return Object.keys(fields as Record<string, unknown>);\n }\n return [];\n }\n\n /**\n * Best-effort pipeline type derived from the Record Process `Configuration` JSON\n * (which serializes `DataFeatureSpec`). Returns 'LLM' when unset, empty, or unparseable, and a built-in\n * name ('decision') in its canonical case ('Decision').\n */\n private static derivePipelineType(config: string | null): string {\n const parsed = config ? SafeJSONParse<ConfigurationPipelineType>(config) : null;\n const typeName = parsed && typeof parsed === 'object' && typeof parsed.PipelineType === 'string' ? parsed.PipelineType.trim() : '';\n if (typeName.length === 0) {\n return 'LLM';\n }\n return BUILT_IN_PIPELINE_TYPES[typeName.toLowerCase()] ?? typeName;\n }\n}\n\n"]}
|
package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.d.ts
CHANGED
|
@@ -22,10 +22,18 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import { OnDestroy, AfterViewInit } from '@angular/core';
|
|
24
24
|
import { Subject } from 'rxjs';
|
|
25
|
-
import {
|
|
25
|
+
import { type RemoteOpResult } from '@memberjunction/core';
|
|
26
|
+
import { ResourceData, type PredictiveStudioRunFeaturePipelineOutput } from '@memberjunction/core-entities';
|
|
26
27
|
import { BaseResourceComponent, NavigationService } from '@memberjunction/ng-shared';
|
|
27
28
|
import { type FeaturePipelineSummary, type FeaturePipelineRunStatus } from './feature-pipeline.engine';
|
|
28
29
|
import * as i0 from "@angular/core";
|
|
30
|
+
/** The toast a finished Run shows. */
|
|
31
|
+
export interface FeaturePipelineRunNotice {
|
|
32
|
+
Message: string;
|
|
33
|
+
Style: 'success' | 'warning' | 'error';
|
|
34
|
+
/** True only when the run's status is `Completed`. */
|
|
35
|
+
Completed: boolean;
|
|
36
|
+
}
|
|
29
37
|
export declare class FeaturePipelinesResourceComponent extends BaseResourceComponent implements AfterViewInit, OnDestroy {
|
|
30
38
|
private cdr;
|
|
31
39
|
protected navigationService: NavigationService;
|
|
@@ -63,8 +71,9 @@ export declare class FeaturePipelinesResourceComponent extends BaseResourceCompo
|
|
|
63
71
|
* `PredictiveStudio.RunFeaturePipeline` Remote Operation through the active
|
|
64
72
|
* provider. The op delegates to the Record Set Processing substrate — the same
|
|
65
73
|
* batching/resume/audit path the rest of the platform uses.
|
|
74
|
+
* @returns the notice the run showed, or null when a run of the pipeline was already in flight
|
|
66
75
|
*/
|
|
67
|
-
OnRunPipeline(pipeline: FeaturePipelineSummary): Promise<
|
|
76
|
+
OnRunPipeline(pipeline: FeaturePipelineSummary): Promise<FeaturePipelineRunNotice | null>;
|
|
68
77
|
/** Whether a given pipeline currently has a Run in flight. */
|
|
69
78
|
IsRunning(pipelineID: string): boolean;
|
|
70
79
|
/** Font Awesome icon for a pipeline's work type. */
|
|
@@ -85,6 +94,11 @@ export declare class FeaturePipelinesResourceComponent extends BaseResourceCompo
|
|
|
85
94
|
*/
|
|
86
95
|
private subscribeToEngine;
|
|
87
96
|
private applyFilter;
|
|
97
|
+
/**
|
|
98
|
+
* The toast for a Run's result. The operation succeeding only means the run was carried out; a refused or failed
|
|
99
|
+
* run ends `Failed` with its error message, and a cancelled or paused run did not finish.
|
|
100
|
+
*/
|
|
101
|
+
static RunResultNotice(pipelineName: string, result: RemoteOpResult<PredictiveStudioRunFeaturePipelineOutput>): FeaturePipelineRunNotice;
|
|
88
102
|
private notifyRunResult;
|
|
89
103
|
private emitAgentContext;
|
|
90
104
|
/** Resolve a pipeline reference (id/name/contains) or return a tolerant failure. */
|
package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-pipelines-resource.component.d.ts","sourceRoot":"","sources":["../../../../src/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAGL,SAAS,EACT,aAAa,EAEd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,OAAO,EAAa,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"feature-pipelines-resource.component.d.ts","sourceRoot":"","sources":["../../../../src/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.ts"],"names":[],"mappings":"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"}
|
package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js
CHANGED
|
@@ -4,6 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
+
var FeaturePipelinesResourceComponent_1;
|
|
7
8
|
/**
|
|
8
9
|
* @fileoverview Knowledge Hub — Feature Pipelines dashboard tab.
|
|
9
10
|
*
|
|
@@ -82,14 +83,34 @@ function FeaturePipelinesResourceComponent_Conditional_16_For_2_Conditional_33_T
|
|
|
82
83
|
} if (rf & 2) {
|
|
83
84
|
const pipeline_r4 = i0.ɵɵnextContext().$implicit;
|
|
84
85
|
i0.ɵɵadvance();
|
|
85
|
-
i0.ɵɵ
|
|
86
|
+
i0.ɵɵtextInterpolate1("\u00B7 ", pipeline_r4.LastRunOutcomeText);
|
|
86
87
|
} }
|
|
87
|
-
function
|
|
88
|
-
i0.ɵɵ
|
|
88
|
+
function FeaturePipelinesResourceComponent_Conditional_16_For_2_Conditional_34_Template(rf, ctx) { if (rf & 1) {
|
|
89
|
+
i0.ɵɵelementStart(0, "span", 35);
|
|
90
|
+
i0.ɵɵtext(1);
|
|
91
|
+
i0.ɵɵelementEnd();
|
|
92
|
+
} if (rf & 2) {
|
|
93
|
+
const pipeline_r4 = i0.ɵɵnextContext().$implicit;
|
|
94
|
+
i0.ɵɵadvance();
|
|
95
|
+
i0.ɵɵtextInterpolate2("\u00B7 ", pipeline_r4.LastRunSuccess, "/", pipeline_r4.LastRunProcessed, " processed");
|
|
96
|
+
} }
|
|
97
|
+
function FeaturePipelinesResourceComponent_Conditional_16_For_2_Conditional_35_Template(rf, ctx) { if (rf & 1) {
|
|
98
|
+
i0.ɵɵelementStart(0, "p", 36);
|
|
99
|
+
i0.ɵɵelement(1, "i", 41);
|
|
100
|
+
i0.ɵɵtext(2);
|
|
101
|
+
i0.ɵɵelementEnd();
|
|
102
|
+
} if (rf & 2) {
|
|
103
|
+
const pipeline_r4 = i0.ɵɵnextContext().$implicit;
|
|
104
|
+
i0.ɵɵproperty("title", pipeline_r4.LastRunErrorMessage);
|
|
105
|
+
i0.ɵɵadvance(2);
|
|
106
|
+
i0.ɵɵtextInterpolate1(" ", pipeline_r4.LastRunErrorMessage, " ");
|
|
107
|
+
} }
|
|
108
|
+
function FeaturePipelinesResourceComponent_Conditional_16_For_2_Conditional_38_Template(rf, ctx) { if (rf & 1) {
|
|
109
|
+
i0.ɵɵelement(0, "i", 42);
|
|
89
110
|
i0.ɵɵtext(1, " Running\u2026 ");
|
|
90
111
|
} }
|
|
91
|
-
function
|
|
92
|
-
i0.ɵɵelement(0, "i",
|
|
112
|
+
function FeaturePipelinesResourceComponent_Conditional_16_For_2_Conditional_39_Template(rf, ctx) { if (rf & 1) {
|
|
113
|
+
i0.ɵɵelement(0, "i", 43);
|
|
93
114
|
i0.ɵɵtext(1, " Run ");
|
|
94
115
|
} }
|
|
95
116
|
function FeaturePipelinesResourceComponent_Conditional_16_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
@@ -129,16 +150,18 @@ function FeaturePipelinesResourceComponent_Conditional_16_For_2_Template(rf, ctx
|
|
|
129
150
|
i0.ɵɵelementEnd();
|
|
130
151
|
i0.ɵɵelementStart(31, "span", 32);
|
|
131
152
|
i0.ɵɵtext(32);
|
|
132
|
-
i0.ɵɵconditionalCreate(33, FeaturePipelinesResourceComponent_Conditional_16_For_2_Conditional_33_Template, 2, 2, "span", 35);
|
|
133
|
-
i0.ɵɵelementEnd()()
|
|
134
|
-
i0.ɵɵ
|
|
135
|
-
i0.ɵɵ
|
|
136
|
-
i0.ɵɵ
|
|
153
|
+
i0.ɵɵconditionalCreate(33, FeaturePipelinesResourceComponent_Conditional_16_For_2_Conditional_33_Template, 2, 1, "span", 35)(34, FeaturePipelinesResourceComponent_Conditional_16_For_2_Conditional_34_Template, 2, 2, "span", 35);
|
|
154
|
+
i0.ɵɵelementEnd()();
|
|
155
|
+
i0.ɵɵconditionalCreate(35, FeaturePipelinesResourceComponent_Conditional_16_For_2_Conditional_35_Template, 3, 2, "p", 36);
|
|
156
|
+
i0.ɵɵelementEnd();
|
|
157
|
+
i0.ɵɵelementStart(36, "div", 37)(37, "button", 38);
|
|
158
|
+
i0.ɵɵlistener("click", function FeaturePipelinesResourceComponent_Conditional_16_For_2_Template_button_click_37_listener() { const pipeline_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.OnRunPipeline(pipeline_r4)); });
|
|
159
|
+
i0.ɵɵconditionalCreate(38, FeaturePipelinesResourceComponent_Conditional_16_For_2_Conditional_38_Template, 2, 0)(39, FeaturePipelinesResourceComponent_Conditional_16_For_2_Conditional_39_Template, 2, 0);
|
|
137
160
|
i0.ɵɵelementEnd();
|
|
138
|
-
i0.ɵɵelementStart(
|
|
139
|
-
i0.ɵɵlistener("click", function
|
|
140
|
-
i0.ɵɵelement(
|
|
141
|
-
i0.ɵɵtext(
|
|
161
|
+
i0.ɵɵelementStart(40, "button", 39);
|
|
162
|
+
i0.ɵɵlistener("click", function FeaturePipelinesResourceComponent_Conditional_16_For_2_Template_button_click_40_listener() { const pipeline_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.OnViewPipeline(pipeline_r4)); });
|
|
163
|
+
i0.ɵɵelement(41, "i", 40);
|
|
164
|
+
i0.ɵɵtext(42, " View ");
|
|
142
165
|
i0.ɵɵelementEnd()()();
|
|
143
166
|
} if (rf & 2) {
|
|
144
167
|
const pipeline_r4 = ctx.$implicit;
|
|
@@ -169,15 +192,17 @@ function FeaturePipelinesResourceComponent_Conditional_16_For_2_Template(rf, ctx
|
|
|
169
192
|
i0.ɵɵadvance(6);
|
|
170
193
|
i0.ɵɵtextInterpolate1(" ", ctx_r0.TimeAgo(pipeline_r4.LastRunAt), " ");
|
|
171
194
|
i0.ɵɵadvance();
|
|
172
|
-
i0.ɵɵconditional(pipeline_r4.LastRunProcessed != null ?
|
|
195
|
+
i0.ɵɵconditional(pipeline_r4.LastRunOutcomeText ? 33 : pipeline_r4.LastRunProcessed != null ? 34 : -1);
|
|
196
|
+
i0.ɵɵadvance(2);
|
|
197
|
+
i0.ɵɵconditional(pipeline_r4.LastRunErrorMessage ? 35 : -1);
|
|
173
198
|
i0.ɵɵadvance(2);
|
|
174
199
|
i0.ɵɵproperty("disabled", !pipeline_r4.OnDemandEnabled || pipeline_r4.Status !== "Active" || ctx_r0.IsRunning(pipeline_r4.ID));
|
|
175
200
|
i0.ɵɵadvance();
|
|
176
|
-
i0.ɵɵconditional(ctx_r0.IsRunning(pipeline_r4.ID) ?
|
|
201
|
+
i0.ɵɵconditional(ctx_r0.IsRunning(pipeline_r4.ID) ? 38 : 39);
|
|
177
202
|
} }
|
|
178
203
|
function FeaturePipelinesResourceComponent_Conditional_16_Template(rf, ctx) { if (rf & 1) {
|
|
179
204
|
i0.ɵɵelementStart(0, "div", 14);
|
|
180
|
-
i0.ɵɵrepeaterCreate(1, FeaturePipelinesResourceComponent_Conditional_16_For_2_Template,
|
|
205
|
+
i0.ɵɵrepeaterCreate(1, FeaturePipelinesResourceComponent_Conditional_16_For_2_Template, 43, 21, "div", 17, _forTrack0);
|
|
181
206
|
i0.ɵɵelementEnd();
|
|
182
207
|
} if (rf & 2) {
|
|
183
208
|
const ctx_r0 = i0.ɵɵnextContext();
|
|
@@ -187,6 +212,7 @@ function FeaturePipelinesResourceComponent_Conditional_16_Template(rf, ctx) { if
|
|
|
187
212
|
/** Stable operation key of the Run Feature Pipeline Remote Operation. */
|
|
188
213
|
const RUN_FEATURE_PIPELINE_OP = 'PredictiveStudio.RunFeaturePipeline';
|
|
189
214
|
let FeaturePipelinesResourceComponent = class FeaturePipelinesResourceComponent extends BaseResourceComponent {
|
|
215
|
+
static { FeaturePipelinesResourceComponent_1 = this; }
|
|
190
216
|
cdr = inject(ChangeDetectorRef);
|
|
191
217
|
navigationService = inject(NavigationService);
|
|
192
218
|
destroy$ = new Subject();
|
|
@@ -271,10 +297,11 @@ let FeaturePipelinesResourceComponent = class FeaturePipelinesResourceComponent
|
|
|
271
297
|
* `PredictiveStudio.RunFeaturePipeline` Remote Operation through the active
|
|
272
298
|
* provider. The op delegates to the Record Set Processing substrate — the same
|
|
273
299
|
* batching/resume/audit path the rest of the platform uses.
|
|
300
|
+
* @returns the notice the run showed, or null when a run of the pipeline was already in flight
|
|
274
301
|
*/
|
|
275
302
|
async OnRunPipeline(pipeline) {
|
|
276
303
|
if (this.RunningIDs.has(pipeline.ID))
|
|
277
|
-
return;
|
|
304
|
+
return null;
|
|
278
305
|
this.RunningIDs.add(pipeline.ID);
|
|
279
306
|
this.cdr.detectChanges();
|
|
280
307
|
try {
|
|
@@ -284,10 +311,16 @@ let FeaturePipelinesResourceComponent = class FeaturePipelinesResourceComponent
|
|
|
284
311
|
// The narrowing cast is the documented "power-tool seam" entry point.
|
|
285
312
|
const opProvider = this.ProviderToUse;
|
|
286
313
|
const result = await opProvider.RouteOperation(RUN_FEATURE_PIPELINE_OP, input, { provider: this.ProviderToUse });
|
|
287
|
-
this.notifyRunResult(pipeline, result);
|
|
314
|
+
return this.notifyRunResult(pipeline, result);
|
|
288
315
|
}
|
|
289
316
|
catch (error) {
|
|
290
|
-
|
|
317
|
+
const notice = {
|
|
318
|
+
Style: 'error',
|
|
319
|
+
Completed: false,
|
|
320
|
+
Message: `Failed to run "${pipeline.Name}": ${error instanceof Error ? error.message : String(error)}`,
|
|
321
|
+
};
|
|
322
|
+
MJNotificationService.Instance.CreateSimpleNotification(notice.Message, notice.Style, 5000);
|
|
323
|
+
return notice;
|
|
291
324
|
}
|
|
292
325
|
finally {
|
|
293
326
|
this.RunningIDs.delete(pipeline.ID);
|
|
@@ -393,14 +426,32 @@ let FeaturePipelinesResourceComponent = class FeaturePipelinesResourceComponent
|
|
|
393
426
|
p.TargetEntity.toLowerCase().includes(q) ||
|
|
394
427
|
(p.OutputAttribute ?? '').toLowerCase().includes(q));
|
|
395
428
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
429
|
+
/**
|
|
430
|
+
* The toast for a Run's result. The operation succeeding only means the run was carried out; a refused or failed
|
|
431
|
+
* run ends `Failed` with its error message, and a cancelled or paused run did not finish.
|
|
432
|
+
*/
|
|
433
|
+
static RunResultNotice(pipelineName, result) {
|
|
434
|
+
const out = result.Output;
|
|
435
|
+
if (!result.Success || !out) {
|
|
436
|
+
return { Style: 'error', Completed: false, Message: `"${pipelineName}" did not complete: ${result.ErrorMessage ?? 'unknown error'}` };
|
|
437
|
+
}
|
|
438
|
+
const reason = out.errorMessage || `the run ended with status ${out.status}`;
|
|
439
|
+
if (out.status === 'Failed') {
|
|
440
|
+
return { Style: 'error', Completed: false, Message: `"${pipelineName}" failed: ${reason}` };
|
|
400
441
|
}
|
|
401
|
-
|
|
402
|
-
|
|
442
|
+
if (out.status !== 'Completed') {
|
|
443
|
+
return { Style: 'warning', Completed: false, Message: `"${pipelineName}" did not finish: ${reason}` };
|
|
403
444
|
}
|
|
445
|
+
return {
|
|
446
|
+
Style: out.error > 0 ? 'warning' : 'success',
|
|
447
|
+
Completed: true,
|
|
448
|
+
Message: `"${pipelineName}" finished: ${out.processed} processed, ${out.written} succeeded, ${out.skipped} skipped unchanged, ${out.error} error(s).`,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
notifyRunResult(pipeline, result) {
|
|
452
|
+
const notice = FeaturePipelinesResourceComponent_1.RunResultNotice(pipeline.Name, result);
|
|
453
|
+
MJNotificationService.Instance.CreateSimpleNotification(notice.Message, notice.Style, 5000);
|
|
454
|
+
return notice;
|
|
404
455
|
}
|
|
405
456
|
// ---- Agent context + client tools (required for every dashboard) ----
|
|
406
457
|
//
|
|
@@ -459,8 +510,11 @@ let FeaturePipelinesResourceComponent = class FeaturePipelinesResourceComponent
|
|
|
459
510
|
if (!r.pipeline.OnDemandEnabled) {
|
|
460
511
|
return { Success: false, ErrorMessage: `"${r.pipeline.Name}" is not enabled for on-demand runs.` };
|
|
461
512
|
}
|
|
462
|
-
await this.OnRunPipeline(r.pipeline);
|
|
463
|
-
|
|
513
|
+
const notice = await this.OnRunPipeline(r.pipeline);
|
|
514
|
+
if (!notice?.Completed) {
|
|
515
|
+
return { Success: false, ErrorMessage: notice?.Message ?? `"${r.pipeline.Name}" is already running.` };
|
|
516
|
+
}
|
|
517
|
+
return { Success: true, Data: { Pipeline: r.pipeline.Name, Message: notice.Message } };
|
|
464
518
|
},
|
|
465
519
|
},
|
|
466
520
|
{
|
|
@@ -509,7 +563,7 @@ let FeaturePipelinesResourceComponent = class FeaturePipelinesResourceComponent
|
|
|
509
563
|
]);
|
|
510
564
|
}
|
|
511
565
|
static ɵfac = /*@__PURE__*/ (() => { let ɵFeaturePipelinesResourceComponent_BaseFactory; return function FeaturePipelinesResourceComponent_Factory(__ngFactoryType__) { return (ɵFeaturePipelinesResourceComponent_BaseFactory || (ɵFeaturePipelinesResourceComponent_BaseFactory = i0.ɵɵgetInheritedFactory(FeaturePipelinesResourceComponent)))(__ngFactoryType__ || FeaturePipelinesResourceComponent); }; })();
|
|
512
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FeaturePipelinesResourceComponent, selectors: [["app-feature-pipelines-resource"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 17, vars: 5, consts: [["Title", "Feature Pipelines", "Icon", "fa-solid fa-diagram-project", "Subtitle", "Derive feature attributes from your data and persist them back to entities"], ["meta", ""], ["Label", "last run failed", "Variant", "error", 3, "Count"], ["Label", "pipelines", 3, "Count", "Total"], ["actions", ""], [3, "Clicked", "Loading"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click"], [1, "fa-solid", "fa-plus"], ["toolbar", ""], ["Placeholder", "Search by name, entity, or attribute\u2026", 3, "ValueChange", "Value"], [3, "Flex"], [1, "fp-loading"], ["Icon", "fa-solid fa-diagram-project", "Title", "No feature pipelines yet", "Message", "A Feature Pipeline derives an attribute per record (e.g. an LLM summary or score) and writes it back to your data \u2014 reusable analytics, independent of any model.", "ActionText", "New Pipeline", "ActionIcon", "fa-solid fa-plus"], ["Variant", "no-results", "Icon", "fa-solid fa-magnifying-glass", "Title", "No matching pipelines", "Message", "No feature pipelines match your search."], [1, "fp-grid"], ["text", "Loading feature pipelines\u2026", "size", "medium"], ["Icon", "fa-solid fa-diagram-project", "Title", "No feature pipelines yet", "Message", "A Feature Pipeline derives an attribute per record (e.g. an LLM summary or score) and writes it back to your data \u2014 reusable analytics, independent of any model.", "ActionText", "New Pipeline", "ActionIcon", "fa-solid fa-plus", 3, "Action"], [1, "fp-card", 3, "fp-card--disabled"], [1, "fp-card"], [1, "fp-card__head"], [1, "fp-card__icon"], [1, "fp-card__title"], [3, "title"], [1, "fp-card__subhead"], [1, "fp-card__worktype"], [1, "fp-type-badge"], [1, "fp-status"], [1, "fp-card__desc", 3, "title"], [1, "fp-card__meta"], [1, "fp-meta-row"], [1, "fp-meta-row__k"], [1, "fa-solid", "fa-table"], [1, "fp-meta-row__v"], [1, "fa-solid", "fa-pen-to-square"], [1, "fa-solid", "fa-clock-rotate-left"], [1, "fp-meta-row__sub"], [1, "fp-card__actions"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click", "disabled"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "click"], [1, "fa-solid", "fa-eye"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-play"]], template: function FeaturePipelinesResourceComponent_Template(rf, ctx) { if (rf & 1) {
|
|
566
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FeaturePipelinesResourceComponent, selectors: [["app-feature-pipelines-resource"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 17, vars: 5, consts: [["Title", "Feature Pipelines", "Icon", "fa-solid fa-diagram-project", "Subtitle", "Derive feature attributes from your data and persist them back to entities"], ["meta", ""], ["Label", "last run failed", "Variant", "error", 3, "Count"], ["Label", "pipelines", 3, "Count", "Total"], ["actions", ""], [3, "Clicked", "Loading"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click"], [1, "fa-solid", "fa-plus"], ["toolbar", ""], ["Placeholder", "Search by name, entity, or attribute\u2026", 3, "ValueChange", "Value"], [3, "Flex"], [1, "fp-loading"], ["Icon", "fa-solid fa-diagram-project", "Title", "No feature pipelines yet", "Message", "A Feature Pipeline derives an attribute per record (e.g. an LLM summary or score) and writes it back to your data \u2014 reusable analytics, independent of any model.", "ActionText", "New Pipeline", "ActionIcon", "fa-solid fa-plus"], ["Variant", "no-results", "Icon", "fa-solid fa-magnifying-glass", "Title", "No matching pipelines", "Message", "No feature pipelines match your search."], [1, "fp-grid"], ["text", "Loading feature pipelines\u2026", "size", "medium"], ["Icon", "fa-solid fa-diagram-project", "Title", "No feature pipelines yet", "Message", "A Feature Pipeline derives an attribute per record (e.g. an LLM summary or score) and writes it back to your data \u2014 reusable analytics, independent of any model.", "ActionText", "New Pipeline", "ActionIcon", "fa-solid fa-plus", 3, "Action"], [1, "fp-card", 3, "fp-card--disabled"], [1, "fp-card"], [1, "fp-card__head"], [1, "fp-card__icon"], [1, "fp-card__title"], [3, "title"], [1, "fp-card__subhead"], [1, "fp-card__worktype"], [1, "fp-type-badge"], [1, "fp-status"], [1, "fp-card__desc", 3, "title"], [1, "fp-card__meta"], [1, "fp-meta-row"], [1, "fp-meta-row__k"], [1, "fa-solid", "fa-table"], [1, "fp-meta-row__v"], [1, "fa-solid", "fa-pen-to-square"], [1, "fa-solid", "fa-clock-rotate-left"], [1, "fp-meta-row__sub"], [1, "fp-card__error", 3, "title"], [1, "fp-card__actions"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click", "disabled"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "click"], [1, "fa-solid", "fa-eye"], [1, "fa-solid", "fa-circle-exclamation"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-play"]], template: function FeaturePipelinesResourceComponent_Template(rf, ctx) { if (rf & 1) {
|
|
513
567
|
i0.ɵɵelementStart(0, "mj-page-layout")(1, "mj-page-header", 0)(2, "div", 1);
|
|
514
568
|
i0.ɵɵconditionalCreate(3, FeaturePipelinesResourceComponent_Conditional_3_Template, 1, 1, "mj-stat-badge", 2)(4, FeaturePipelinesResourceComponent_Conditional_4_Template, 1, 2, "mj-stat-badge", 3);
|
|
515
569
|
i0.ɵɵelementEnd();
|
|
@@ -538,17 +592,17 @@ let FeaturePipelinesResourceComponent = class FeaturePipelinesResourceComponent
|
|
|
538
592
|
i0.ɵɵproperty("Flex", true);
|
|
539
593
|
i0.ɵɵadvance();
|
|
540
594
|
i0.ɵɵconditional(ctx.IsLoading ? 13 : ctx.AllPipelines.length === 0 ? 14 : ctx.FilteredPipelines.length === 0 ? 15 : 16);
|
|
541
|
-
} }, dependencies: [i1.LoadingComponent, i2.MJButtonDirective, i2.MJPageHeaderComponent, i2.MJPageLayoutComponent, i2.MJPageBodyComponent, i2.MJPageSearchComponent, i2.MJStatBadgeComponent, i2.MJRefreshButtonComponent, i2.MJEmptyStateComponent], styles: ["\n\n\n\n\n\n\n\n\n\n\n.fp-loading[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 240px;\n width: 100%;\n}\n\n.fp-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n gap: 16px;\n padding: 4px;\n align-content: start;\n}\n\n.fp-card[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 10px);\n background: var(--mj-bg-surface-card);\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n}\n\n.fp-card[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-border-strong);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n}\n\n.fp-card--disabled[_ngcontent-%COMP%] {\n opacity: 0.65;\n}\n\n.fp-card__head[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n}\n\n.fp-card__icon[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--mj-radius-md, 8px);\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 16px;\n}\n\n.fp-card__title[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.fp-card__title[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.fp-card__worktype[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-muted);\n}\n\n.fp-card__subhead[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 2px;\n}\n\n.fp-type-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 1px 6px;\n border-radius: var(--mj-radius-sm, 4px);\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.02em;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n border: 1px solid var(--mj-border-default);\n}\n\n.fp-type-badge--decision[_ngcontent-%COMP%] {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n border-color: var(--mj-status-info-border);\n}\n\n.fp-card__desc[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 13px;\n line-height: 1.4;\n color: var(--mj-text-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.fp-card__meta[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 0;\n border-top: 1px solid var(--mj-border-subtle);\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.fp-meta-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 12px;\n font-size: 13px;\n}\n\n.fp-meta-row__k[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n color: var(--mj-text-muted);\n}\n\n.fp-meta-row__k[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n width: 14px;\n margin-right: 4px;\n}\n\n.fp-meta-row__v[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n text-align: right;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.fp-meta-row__sub[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 12px;\n}\n\n.fp-card__actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n margin-top: auto;\n}\n\n\n.fp-status[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n align-self: flex-start;\n padding: 2px 10px;\n border-radius: var(--mj-radius-full, 999px);\n font-size: 11px;\n font-weight: 600;\n white-space: nowrap;\n}\n\n.fp-status.status--never[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.fp-status.status--running[_ngcontent-%COMP%] {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n}\n\n.fp-status.status--completed[_ngcontent-%COMP%] {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.fp-status.status--failed[_ngcontent-%COMP%] {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n.fp-status.status--cancelled[_ngcontent-%COMP%] {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n}"] });
|
|
595
|
+
} }, dependencies: [i1.LoadingComponent, i2.MJButtonDirective, i2.MJPageHeaderComponent, i2.MJPageLayoutComponent, i2.MJPageBodyComponent, i2.MJPageSearchComponent, i2.MJStatBadgeComponent, i2.MJRefreshButtonComponent, i2.MJEmptyStateComponent], styles: ["\n\n\n\n\n\n\n\n\n\n\n.fp-loading[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 240px;\n width: 100%;\n}\n\n.fp-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n gap: 16px;\n padding: 4px;\n align-content: start;\n}\n\n.fp-card[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 10px);\n background: var(--mj-bg-surface-card);\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n}\n\n.fp-card[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-border-strong);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n}\n\n.fp-card--disabled[_ngcontent-%COMP%] {\n opacity: 0.65;\n}\n\n.fp-card__head[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n}\n\n.fp-card__icon[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--mj-radius-md, 8px);\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 16px;\n}\n\n.fp-card__title[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.fp-card__title[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.fp-card__worktype[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-muted);\n}\n\n.fp-card__subhead[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 2px;\n}\n\n.fp-type-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 1px 6px;\n border-radius: var(--mj-radius-sm, 4px);\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.02em;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n border: 1px solid var(--mj-border-default);\n}\n\n.fp-type-badge--decision[_ngcontent-%COMP%] {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n border-color: var(--mj-status-info-border);\n}\n\n.fp-card__desc[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 13px;\n line-height: 1.4;\n color: var(--mj-text-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n\n.fp-card__error[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 12px;\n line-height: 1.4;\n color: var(--mj-status-error-text);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.fp-card__meta[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 0;\n border-top: 1px solid var(--mj-border-subtle);\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.fp-meta-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 12px;\n font-size: 13px;\n}\n\n.fp-meta-row__k[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n color: var(--mj-text-muted);\n}\n\n.fp-meta-row__k[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n width: 14px;\n margin-right: 4px;\n}\n\n.fp-meta-row__v[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n text-align: right;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.fp-meta-row__sub[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 12px;\n}\n\n.fp-card__actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n margin-top: auto;\n}\n\n\n.fp-status[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n align-self: flex-start;\n padding: 2px 10px;\n border-radius: var(--mj-radius-full, 999px);\n font-size: 11px;\n font-weight: 600;\n white-space: nowrap;\n}\n\n.fp-status.status--never[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.fp-status.status--running[_ngcontent-%COMP%] {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n}\n\n.fp-status.status--completed[_ngcontent-%COMP%] {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.fp-status.status--failed[_ngcontent-%COMP%] {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n.fp-status.status--cancelled[_ngcontent-%COMP%] {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n}"] });
|
|
542
596
|
};
|
|
543
|
-
FeaturePipelinesResourceComponent = __decorate([
|
|
597
|
+
FeaturePipelinesResourceComponent = FeaturePipelinesResourceComponent_1 = __decorate([
|
|
544
598
|
RegisterClass(BaseResourceComponent, 'FeaturePipelinesResource')
|
|
545
599
|
], FeaturePipelinesResourceComponent);
|
|
546
600
|
export { FeaturePipelinesResourceComponent };
|
|
547
601
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FeaturePipelinesResourceComponent, [{
|
|
548
602
|
type: Component,
|
|
549
|
-
args: [{ standalone: false, selector: 'app-feature-pipelines-resource', template: "<mj-page-layout>\n <mj-page-header\n Title=\"Feature Pipelines\"\n Icon=\"fa-solid fa-diagram-project\"\n Subtitle=\"Derive feature attributes from your data and persist them back to entities\">\n <div meta>\n @if (FailedCount > 0) {\n <mj-stat-badge [Count]=\"FailedCount\" Label=\"last run failed\" Variant=\"error\"></mj-stat-badge>\n } @else {\n <mj-stat-badge\n [Count]=\"FilteredPipelines.length\"\n [Total]=\"AllPipelines.length\"\n Label=\"pipelines\">\n </mj-stat-badge>\n }\n </div>\n <div actions>\n <mj-refresh-button [Loading]=\"IsLoading\" (Clicked)=\"Refresh()\"></mj-refresh-button>\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"OnNewPipeline()\">\n <i class=\"fa-solid fa-plus\"></i> New Pipeline\n </button>\n </div>\n <div toolbar>\n <mj-page-search\n [Value]=\"SearchQuery\"\n Placeholder=\"Search by name, entity, or attribute\u2026\"\n (ValueChange)=\"SearchQuery = $event; OnSearchChanged()\">\n </mj-page-search>\n </div>\n </mj-page-header>\n\n <mj-page-body [Flex]=\"true\">\n @if (IsLoading) {\n <div class=\"fp-loading\">\n <mj-loading text=\"Loading feature pipelines\u2026\" size=\"medium\"></mj-loading>\n </div>\n } @else if (AllPipelines.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-diagram-project\"\n Title=\"No feature pipelines yet\"\n Message=\"A Feature Pipeline derives an attribute per record (e.g. an LLM summary or score) and writes it back to your data \u2014 reusable analytics, independent of any model.\"\n ActionText=\"New Pipeline\"\n ActionIcon=\"fa-solid fa-plus\"\n (Action)=\"OnNewPipeline()\">\n </mj-empty-state>\n } @else if (FilteredPipelines.length === 0) {\n <mj-empty-state\n Variant=\"no-results\"\n Icon=\"fa-solid fa-magnifying-glass\"\n Title=\"No matching pipelines\"\n Message=\"No feature pipelines match your search.\">\n </mj-empty-state>\n } @else {\n <div class=\"fp-grid\">\n @for (pipeline of FilteredPipelines; track pipeline.ID) {\n <div class=\"fp-card\" [class.fp-card--disabled]=\"pipeline.Status !== 'Active'\">\n <div class=\"fp-card__head\">\n <i class=\"fp-card__icon\" [class]=\"WorkTypeIcon(pipeline.WorkType)\"></i>\n <div class=\"fp-card__title\">\n <h3 [title]=\"pipeline.Name\">{{ pipeline.Name }}</h3>\n <div class=\"fp-card__subhead\">\n <span class=\"fp-card__worktype\">{{ pipeline.WorkType }}</span>\n <span class=\"fp-type-badge\" [class.fp-type-badge--decision]=\"pipeline.PipelineType === 'Decision'\">{{ pipeline.PipelineType }}</span>\n </div>\n </div>\n <span class=\"fp-status\" [class]=\"RunStatusClass(pipeline.LastRunStatus)\">\n {{ RunStatusLabel(pipeline.LastRunStatus) }}\n </span>\n </div>\n\n @if (pipeline.Description) {\n <p class=\"fp-card__desc\" [title]=\"pipeline.Description\">{{ pipeline.Description }}</p>\n }\n\n <div class=\"fp-card__meta\">\n <div class=\"fp-meta-row\">\n <span class=\"fp-meta-row__k\"><i class=\"fa-solid fa-table\"></i> Target</span>\n <span class=\"fp-meta-row__v\">{{ pipeline.TargetEntity }}</span>\n </div>\n <div class=\"fp-meta-row\">\n <span class=\"fp-meta-row__k\"><i class=\"fa-solid fa-pen-to-square\"></i> Writes</span>\n <span class=\"fp-meta-row__v\">{{ pipeline.OutputAttribute || '\u2014' }}</span>\n </div>\n <div class=\"fp-meta-row\">\n <span class=\"fp-meta-row__k\"><i class=\"fa-solid fa-clock-rotate-left\"></i> Last run</span>\n <span class=\"fp-meta-row__v\">\n {{ TimeAgo(pipeline.LastRunAt) }}\n @if (pipeline.LastRunProcessed != null) {\n <span class=\"fp-meta-row__sub\">\u00B7 {{ pipeline.LastRunSuccess }}/{{ pipeline.LastRunProcessed }} written</span>\n }\n </span>\n </div>\n </div>\n\n <div class=\"fp-card__actions\">\n <button\n mjButton\n variant=\"primary\"\n size=\"sm\"\n [disabled]=\"!pipeline.OnDemandEnabled || pipeline.Status !== 'Active' || IsRunning(pipeline.ID)\"\n (click)=\"OnRunPipeline(pipeline)\">\n @if (IsRunning(pipeline.ID)) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Running\u2026\n } @else {\n <i class=\"fa-solid fa-play\"></i> Run\n }\n </button>\n <button mjButton variant=\"secondary\" size=\"sm\" (click)=\"OnViewPipeline(pipeline)\">\n <i class=\"fa-solid fa-eye\"></i> View\n </button>\n </div>\n </div>\n }\n </div>\n }\n </mj-page-body>\n</mj-page-layout>\n", styles: ["/**\n * Feature Pipelines (Knowledge Hub) \u2014 card-grid layout.\n * All colors use semantic design tokens (dark-mode + white-label safe).\n *\n * NOTE: this package builds with bare `ngc` (no Sass step) \u2014 component styles\n * are embedded RAW into the compiled JS. Keep this file FLAT, plain CSS.\n * Sass nesting / BEM selector concatenation would be silently dropped by the\n * browser and the rules would be DEAD. See .github/scripts/check-ngc-scss.sh\n * (CI gate) and MemberJunction/MJ#3105.\n */\n\n.fp-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 240px;\n width: 100%;\n}\n\n.fp-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n gap: 16px;\n padding: 4px;\n align-content: start;\n}\n\n.fp-card {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 10px);\n background: var(--mj-bg-surface-card);\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n}\n\n.fp-card:hover {\n border-color: var(--mj-border-strong);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n}\n\n.fp-card--disabled {\n opacity: 0.65;\n}\n\n.fp-card__head {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n}\n\n.fp-card__icon {\n flex: 0 0 auto;\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--mj-radius-md, 8px);\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 16px;\n}\n\n.fp-card__title {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.fp-card__title h3 {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.fp-card__worktype {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-muted);\n}\n\n.fp-card__subhead {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 2px;\n}\n\n.fp-type-badge {\n display: inline-flex;\n align-items: center;\n padding: 1px 6px;\n border-radius: var(--mj-radius-sm, 4px);\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.02em;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n border: 1px solid var(--mj-border-default);\n}\n\n.fp-type-badge--decision {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n border-color: var(--mj-status-info-border);\n}\n\n.fp-card__desc {\n margin: 0;\n font-size: 13px;\n line-height: 1.4;\n color: var(--mj-text-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.fp-card__meta {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 0;\n border-top: 1px solid var(--mj-border-subtle);\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.fp-meta-row {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 12px;\n font-size: 13px;\n}\n\n.fp-meta-row__k {\n flex: 0 0 auto;\n color: var(--mj-text-muted);\n}\n\n.fp-meta-row__k i {\n width: 14px;\n margin-right: 4px;\n}\n\n.fp-meta-row__v {\n flex: 1 1 auto;\n text-align: right;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.fp-meta-row__sub {\n color: var(--mj-text-muted);\n font-size: 12px;\n}\n\n.fp-card__actions {\n display: flex;\n gap: 8px;\n margin-top: auto;\n}\n\n/* Run-status pill \u2014 semantic status tokens */\n.fp-status {\n flex: 0 0 auto;\n align-self: flex-start;\n padding: 2px 10px;\n border-radius: var(--mj-radius-full, 999px);\n font-size: 11px;\n font-weight: 600;\n white-space: nowrap;\n}\n\n.fp-status.status--never {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.fp-status.status--running {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n}\n\n.fp-status.status--completed {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.fp-status.status--failed {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n.fp-status.status--cancelled {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n}\n"] }]
|
|
603
|
+
args: [{ standalone: false, selector: 'app-feature-pipelines-resource', template: "<mj-page-layout>\n <mj-page-header\n Title=\"Feature Pipelines\"\n Icon=\"fa-solid fa-diagram-project\"\n Subtitle=\"Derive feature attributes from your data and persist them back to entities\">\n <div meta>\n @if (FailedCount > 0) {\n <mj-stat-badge [Count]=\"FailedCount\" Label=\"last run failed\" Variant=\"error\"></mj-stat-badge>\n } @else {\n <mj-stat-badge\n [Count]=\"FilteredPipelines.length\"\n [Total]=\"AllPipelines.length\"\n Label=\"pipelines\">\n </mj-stat-badge>\n }\n </div>\n <div actions>\n <mj-refresh-button [Loading]=\"IsLoading\" (Clicked)=\"Refresh()\"></mj-refresh-button>\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"OnNewPipeline()\">\n <i class=\"fa-solid fa-plus\"></i> New Pipeline\n </button>\n </div>\n <div toolbar>\n <mj-page-search\n [Value]=\"SearchQuery\"\n Placeholder=\"Search by name, entity, or attribute\u2026\"\n (ValueChange)=\"SearchQuery = $event; OnSearchChanged()\">\n </mj-page-search>\n </div>\n </mj-page-header>\n\n <mj-page-body [Flex]=\"true\">\n @if (IsLoading) {\n <div class=\"fp-loading\">\n <mj-loading text=\"Loading feature pipelines\u2026\" size=\"medium\"></mj-loading>\n </div>\n } @else if (AllPipelines.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-diagram-project\"\n Title=\"No feature pipelines yet\"\n Message=\"A Feature Pipeline derives an attribute per record (e.g. an LLM summary or score) and writes it back to your data \u2014 reusable analytics, independent of any model.\"\n ActionText=\"New Pipeline\"\n ActionIcon=\"fa-solid fa-plus\"\n (Action)=\"OnNewPipeline()\">\n </mj-empty-state>\n } @else if (FilteredPipelines.length === 0) {\n <mj-empty-state\n Variant=\"no-results\"\n Icon=\"fa-solid fa-magnifying-glass\"\n Title=\"No matching pipelines\"\n Message=\"No feature pipelines match your search.\">\n </mj-empty-state>\n } @else {\n <div class=\"fp-grid\">\n @for (pipeline of FilteredPipelines; track pipeline.ID) {\n <div class=\"fp-card\" [class.fp-card--disabled]=\"pipeline.Status !== 'Active'\">\n <div class=\"fp-card__head\">\n <i class=\"fp-card__icon\" [class]=\"WorkTypeIcon(pipeline.WorkType)\"></i>\n <div class=\"fp-card__title\">\n <h3 [title]=\"pipeline.Name\">{{ pipeline.Name }}</h3>\n <div class=\"fp-card__subhead\">\n <span class=\"fp-card__worktype\">{{ pipeline.WorkType }}</span>\n <span class=\"fp-type-badge\" [class.fp-type-badge--decision]=\"pipeline.PipelineType === 'Decision'\">{{ pipeline.PipelineType }}</span>\n </div>\n </div>\n <span class=\"fp-status\" [class]=\"RunStatusClass(pipeline.LastRunStatus)\">\n {{ RunStatusLabel(pipeline.LastRunStatus) }}\n </span>\n </div>\n\n @if (pipeline.Description) {\n <p class=\"fp-card__desc\" [title]=\"pipeline.Description\">{{ pipeline.Description }}</p>\n }\n\n <div class=\"fp-card__meta\">\n <div class=\"fp-meta-row\">\n <span class=\"fp-meta-row__k\"><i class=\"fa-solid fa-table\"></i> Target</span>\n <span class=\"fp-meta-row__v\">{{ pipeline.TargetEntity }}</span>\n </div>\n <div class=\"fp-meta-row\">\n <span class=\"fp-meta-row__k\"><i class=\"fa-solid fa-pen-to-square\"></i> Writes</span>\n <span class=\"fp-meta-row__v\">{{ pipeline.OutputAttribute || '\u2014' }}</span>\n </div>\n <div class=\"fp-meta-row\">\n <span class=\"fp-meta-row__k\"><i class=\"fa-solid fa-clock-rotate-left\"></i> Last run</span>\n <span class=\"fp-meta-row__v\">\n {{ TimeAgo(pipeline.LastRunAt) }}\n @if (pipeline.LastRunOutcomeText) {\n <span class=\"fp-meta-row__sub\">\u00B7 {{ pipeline.LastRunOutcomeText }}</span>\n } @else if (pipeline.LastRunProcessed != null) {\n <span class=\"fp-meta-row__sub\">\u00B7 {{ pipeline.LastRunSuccess }}/{{ pipeline.LastRunProcessed }} processed</span>\n }\n </span>\n </div>\n @if (pipeline.LastRunErrorMessage) {\n <p class=\"fp-card__error\" [title]=\"pipeline.LastRunErrorMessage\">\n <i class=\"fa-solid fa-circle-exclamation\"></i> {{ pipeline.LastRunErrorMessage }}\n </p>\n }\n </div>\n\n <div class=\"fp-card__actions\">\n <button\n mjButton\n variant=\"primary\"\n size=\"sm\"\n [disabled]=\"!pipeline.OnDemandEnabled || pipeline.Status !== 'Active' || IsRunning(pipeline.ID)\"\n (click)=\"OnRunPipeline(pipeline)\">\n @if (IsRunning(pipeline.ID)) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Running\u2026\n } @else {\n <i class=\"fa-solid fa-play\"></i> Run\n }\n </button>\n <button mjButton variant=\"secondary\" size=\"sm\" (click)=\"OnViewPipeline(pipeline)\">\n <i class=\"fa-solid fa-eye\"></i> View\n </button>\n </div>\n </div>\n }\n </div>\n }\n </mj-page-body>\n</mj-page-layout>\n", styles: ["/**\n * Feature Pipelines (Knowledge Hub) \u2014 card-grid layout.\n * All colors use semantic design tokens (dark-mode + white-label safe).\n *\n * NOTE: this package builds with bare `ngc` (no Sass step) \u2014 component styles\n * are embedded RAW into the compiled JS. Keep this file FLAT, plain CSS.\n * Sass nesting / BEM selector concatenation would be silently dropped by the\n * browser and the rules would be DEAD. See .github/scripts/check-ngc-scss.sh\n * (CI gate) and MemberJunction/MJ#3105.\n */\n\n.fp-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 240px;\n width: 100%;\n}\n\n.fp-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n gap: 16px;\n padding: 4px;\n align-content: start;\n}\n\n.fp-card {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 10px);\n background: var(--mj-bg-surface-card);\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n}\n\n.fp-card:hover {\n border-color: var(--mj-border-strong);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n}\n\n.fp-card--disabled {\n opacity: 0.65;\n}\n\n.fp-card__head {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n}\n\n.fp-card__icon {\n flex: 0 0 auto;\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--mj-radius-md, 8px);\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 16px;\n}\n\n.fp-card__title {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.fp-card__title h3 {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.fp-card__worktype {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-muted);\n}\n\n.fp-card__subhead {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 2px;\n}\n\n.fp-type-badge {\n display: inline-flex;\n align-items: center;\n padding: 1px 6px;\n border-radius: var(--mj-radius-sm, 4px);\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.02em;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n border: 1px solid var(--mj-border-default);\n}\n\n.fp-type-badge--decision {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n border-color: var(--mj-status-info-border);\n}\n\n.fp-card__desc {\n margin: 0;\n font-size: 13px;\n line-height: 1.4;\n color: var(--mj-text-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* A failed or refused last run's reason: two lines, the full text on hover */\n.fp-card__error {\n margin: 0;\n font-size: 12px;\n line-height: 1.4;\n color: var(--mj-status-error-text);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.fp-card__meta {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 0;\n border-top: 1px solid var(--mj-border-subtle);\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.fp-meta-row {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 12px;\n font-size: 13px;\n}\n\n.fp-meta-row__k {\n flex: 0 0 auto;\n color: var(--mj-text-muted);\n}\n\n.fp-meta-row__k i {\n width: 14px;\n margin-right: 4px;\n}\n\n.fp-meta-row__v {\n flex: 1 1 auto;\n text-align: right;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.fp-meta-row__sub {\n color: var(--mj-text-muted);\n font-size: 12px;\n}\n\n.fp-card__actions {\n display: flex;\n gap: 8px;\n margin-top: auto;\n}\n\n/* Run-status pill \u2014 semantic status tokens */\n.fp-status {\n flex: 0 0 auto;\n align-self: flex-start;\n padding: 2px 10px;\n border-radius: var(--mj-radius-full, 999px);\n font-size: 11px;\n font-weight: 600;\n white-space: nowrap;\n}\n\n.fp-status.status--never {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.fp-status.status--running {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n}\n\n.fp-status.status--completed {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.fp-status.status--failed {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n.fp-status.status--cancelled {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n}\n"] }]
|
|
550
604
|
}], null, null); })();
|
|
551
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FeaturePipelinesResourceComponent, { className: "FeaturePipelinesResourceComponent", filePath: "src/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.ts", lineNumber:
|
|
605
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FeaturePipelinesResourceComponent, { className: "FeaturePipelinesResourceComponent", filePath: "src/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.ts", lineNumber: 73 }); })();
|
|
552
606
|
/** Tree-shaking prevention — keeps the component in the bundle. */
|
|
553
607
|
export function LoadFeaturePipelinesResource() {
|
|
554
608
|
// Prevents tree-shaking of the component.
|