rax-flow-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/dist/cache/semantic-cache.d.ts +10 -0
  2. package/dist/cache/semantic-cache.d.ts.map +1 -0
  3. package/dist/cache/semantic-cache.js +50 -0
  4. package/dist/cache/semantic-cache.js.map +1 -0
  5. package/dist/governance/policies/pii-policy.d.ts +37 -0
  6. package/dist/governance/policies/pii-policy.d.ts.map +1 -0
  7. package/dist/governance/policies/pii-policy.js +177 -0
  8. package/dist/governance/policies/pii-policy.js.map +1 -0
  9. package/dist/graph/workflow-graph.d.ts +5 -0
  10. package/dist/graph/workflow-graph.d.ts.map +1 -0
  11. package/dist/graph/workflow-graph.js +38 -0
  12. package/dist/graph/workflow-graph.js.map +1 -0
  13. package/dist/index.d.ts +25 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +25 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/memory/graph-memory.d.ts +46 -0
  18. package/dist/memory/graph-memory.d.ts.map +1 -0
  19. package/dist/memory/graph-memory.js +122 -0
  20. package/dist/memory/graph-memory.js.map +1 -0
  21. package/dist/memory/local-vector-store.d.ts +36 -0
  22. package/dist/memory/local-vector-store.d.ts.map +1 -0
  23. package/dist/memory/local-vector-store.js +125 -0
  24. package/dist/memory/local-vector-store.js.map +1 -0
  25. package/dist/memory/memory-manager.d.ts +42 -0
  26. package/dist/memory/memory-manager.d.ts.map +1 -0
  27. package/dist/memory/memory-manager.js +98 -0
  28. package/dist/memory/memory-manager.js.map +1 -0
  29. package/dist/metrics/metrics-engine.d.ts +13 -0
  30. package/dist/metrics/metrics-engine.d.ts.map +1 -0
  31. package/dist/metrics/metrics-engine.js +27 -0
  32. package/dist/metrics/metrics-engine.js.map +1 -0
  33. package/dist/orchestrator/blueprint-committer.d.ts +69 -0
  34. package/dist/orchestrator/blueprint-committer.d.ts.map +1 -0
  35. package/dist/orchestrator/blueprint-committer.js +305 -0
  36. package/dist/orchestrator/blueprint-committer.js.map +1 -0
  37. package/dist/orchestrator/core-orchestrator.d.ts +168 -0
  38. package/dist/orchestrator/core-orchestrator.d.ts.map +1 -0
  39. package/dist/orchestrator/core-orchestrator.js +451 -0
  40. package/dist/orchestrator/core-orchestrator.js.map +1 -0
  41. package/dist/orchestrator/decomposition-engine.d.ts +41 -0
  42. package/dist/orchestrator/decomposition-engine.d.ts.map +1 -0
  43. package/dist/orchestrator/decomposition-engine.js +133 -0
  44. package/dist/orchestrator/decomposition-engine.js.map +1 -0
  45. package/dist/orchestrator/decomposition.d.ts +10 -0
  46. package/dist/orchestrator/decomposition.d.ts.map +1 -0
  47. package/dist/orchestrator/decomposition.js +15 -0
  48. package/dist/orchestrator/decomposition.js.map +1 -0
  49. package/dist/orchestrator/default-workflow.d.ts +59 -0
  50. package/dist/orchestrator/default-workflow.d.ts.map +1 -0
  51. package/dist/orchestrator/default-workflow.js +221 -0
  52. package/dist/orchestrator/default-workflow.js.map +1 -0
  53. package/dist/orchestrator/dynamic-planner.d.ts +46 -0
  54. package/dist/orchestrator/dynamic-planner.d.ts.map +1 -0
  55. package/dist/orchestrator/dynamic-planner.js +232 -0
  56. package/dist/orchestrator/dynamic-planner.js.map +1 -0
  57. package/dist/orchestrator/kernel-bridge.d.ts +57 -0
  58. package/dist/orchestrator/kernel-bridge.d.ts.map +1 -0
  59. package/dist/orchestrator/kernel-bridge.js +211 -0
  60. package/dist/orchestrator/kernel-bridge.js.map +1 -0
  61. package/dist/orchestrator/mutation-applier.d.ts +47 -0
  62. package/dist/orchestrator/mutation-applier.d.ts.map +1 -0
  63. package/dist/orchestrator/mutation-applier.js +253 -0
  64. package/dist/orchestrator/mutation-applier.js.map +1 -0
  65. package/dist/orchestrator/routing.d.ts +4 -0
  66. package/dist/orchestrator/routing.d.ts.map +1 -0
  67. package/dist/orchestrator/routing.js +41 -0
  68. package/dist/orchestrator/routing.js.map +1 -0
  69. package/dist/orchestrator/verify-fix.d.ts +79 -0
  70. package/dist/orchestrator/verify-fix.d.ts.map +1 -0
  71. package/dist/orchestrator/verify-fix.js +153 -0
  72. package/dist/orchestrator/verify-fix.js.map +1 -0
  73. package/dist/plugins/governance-plugin.d.ts +43 -0
  74. package/dist/plugins/governance-plugin.d.ts.map +1 -0
  75. package/dist/plugins/governance-plugin.js +88 -0
  76. package/dist/plugins/governance-plugin.js.map +1 -0
  77. package/dist/plugins/long-term-memory-plugin.d.ts +31 -0
  78. package/dist/plugins/long-term-memory-plugin.d.ts.map +1 -0
  79. package/dist/plugins/long-term-memory-plugin.js +67 -0
  80. package/dist/plugins/long-term-memory-plugin.js.map +1 -0
  81. package/dist/plugins/plugin-system.d.ts +29 -0
  82. package/dist/plugins/plugin-system.d.ts.map +1 -0
  83. package/dist/plugins/plugin-system.js +22 -0
  84. package/dist/plugins/plugin-system.js.map +1 -0
  85. package/dist/recovery/error-recovery.d.ts +15 -0
  86. package/dist/recovery/error-recovery.d.ts.map +1 -0
  87. package/dist/recovery/error-recovery.js +12 -0
  88. package/dist/recovery/error-recovery.js.map +1 -0
  89. package/dist/runtime/concurrency-scheduler.d.ts +2 -0
  90. package/dist/runtime/concurrency-scheduler.d.ts.map +1 -0
  91. package/dist/runtime/concurrency-scheduler.js +18 -0
  92. package/dist/runtime/concurrency-scheduler.js.map +1 -0
  93. package/dist/runtime/runtime-events.d.ts +59 -0
  94. package/dist/runtime/runtime-events.d.ts.map +1 -0
  95. package/dist/runtime/runtime-events.js +12 -0
  96. package/dist/runtime/runtime-events.js.map +1 -0
  97. package/dist/types/contracts.d.ts +127 -0
  98. package/dist/types/contracts.d.ts.map +1 -0
  99. package/dist/types/contracts.js +2 -0
  100. package/dist/types/contracts.js.map +1 -0
  101. package/dist/validator/structured-output-validator.d.ts +10 -0
  102. package/dist/validator/structured-output-validator.d.ts.map +1 -0
  103. package/dist/validator/structured-output-validator.js +19 -0
  104. package/dist/validator/structured-output-validator.js.map +1 -0
  105. package/package.json +18 -0
  106. package/src/cache/semantic-cache.ts +64 -0
  107. package/src/governance/policies/pii-policy.ts +213 -0
  108. package/src/graph/workflow-graph.ts +41 -0
  109. package/src/index.ts +25 -0
  110. package/src/memory/graph-memory.ts +150 -0
  111. package/src/memory/local-vector-store.ts +129 -0
  112. package/src/memory/memory-manager.ts +126 -0
  113. package/src/metrics/metrics-engine.ts +39 -0
  114. package/src/orchestrator/blueprint-committer.ts +351 -0
  115. package/src/orchestrator/core-orchestrator.ts +582 -0
  116. package/src/orchestrator/decomposition-engine.ts +165 -0
  117. package/src/orchestrator/decomposition.ts +25 -0
  118. package/src/orchestrator/default-workflow.ts +269 -0
  119. package/src/orchestrator/dynamic-planner.ts +277 -0
  120. package/src/orchestrator/kernel-bridge.ts +251 -0
  121. package/src/orchestrator/mutation-applier.ts +279 -0
  122. package/src/orchestrator/routing.ts +44 -0
  123. package/src/orchestrator/verify-fix.ts +218 -0
  124. package/src/plugins/governance-plugin.ts +106 -0
  125. package/src/plugins/long-term-memory-plugin.ts +72 -0
  126. package/src/plugins/plugin-system.ts +34 -0
  127. package/src/recovery/error-recovery.ts +23 -0
  128. package/src/runtime/concurrency-scheduler.ts +18 -0
  129. package/src/runtime/runtime-events.ts +27 -0
  130. package/src/types/contracts.ts +159 -0
  131. package/src/validator/structured-output-validator.ts +20 -0
  132. package/tsconfig.json +17 -0
@@ -0,0 +1,150 @@
1
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
2
+ import path from "node:path";
3
+
4
+ export interface GraphNode {
5
+ id: string;
6
+ type: "action" | "artifact" | "concept" | "agent";
7
+ content: string;
8
+ metadata: Record<string, any>;
9
+ timestamp: number;
10
+ }
11
+
12
+ export interface GraphEdge {
13
+ from: string;
14
+ to: string;
15
+ type: "produced" | "triggered_by" | "depends_on" | "relates_to";
16
+ metadata?: Record<string, any>;
17
+ }
18
+
19
+ /**
20
+ * Quantum Semantic Graph Memory (QSGM) Core.
21
+ * Manages relationships between actions, artifacts, and concepts.
22
+ */
23
+ export class GraphMemory {
24
+ private nodes: Map<string, GraphNode> = new Map();
25
+ private edges: GraphEdge[] = [];
26
+
27
+ constructor(private persistPath?: string) { }
28
+
29
+ public addNode(node: GraphNode): void {
30
+ this.nodes.set(node.id, node);
31
+ }
32
+
33
+ public removeNode(nodeId: string): boolean {
34
+ if (!this.nodes.has(nodeId)) return false;
35
+ this.nodes.delete(nodeId);
36
+ this.edges = this.edges.filter(e => e.from !== nodeId && e.to !== nodeId);
37
+ return true;
38
+ }
39
+
40
+ public addEdge(edge: GraphEdge): void {
41
+ this.edges.push(edge);
42
+ }
43
+
44
+ /**
45
+ * Traverses the graph to find nodes related to a starting point.
46
+ * Useful for finding "ripple effects" or deep context.
47
+ */
48
+ public traverse(startNodeId: string, maxDepth: number = 2): GraphNode[] {
49
+ const visited = new Set<string>();
50
+ const results: GraphNode[] = [];
51
+ const queue: { id: string; depth: number }[] = [{ id: startNodeId, depth: 0 }];
52
+
53
+ while (queue.length > 0) {
54
+ const { id, depth } = queue.shift()!;
55
+ if (visited.has(id) || depth > maxDepth) continue;
56
+
57
+ visited.add(id);
58
+ const node = this.nodes.get(id);
59
+ if (node) {
60
+ results.push(node);
61
+ }
62
+
63
+ // Find neighbors
64
+ const neighbors = this.edges
65
+ .filter(e => e.from === id)
66
+ .map(e => ({ id: e.to, depth: depth + 1 }));
67
+
68
+ const reverseNeighbors = this.edges
69
+ .filter(e => e.to === id)
70
+ .map(e => ({ id: e.from, depth: depth + 1 }));
71
+
72
+ queue.push(...neighbors, ...reverseNeighbors);
73
+ }
74
+
75
+ return results;
76
+ }
77
+
78
+ public async load(): Promise<void> {
79
+ if (!this.persistPath) return;
80
+ try {
81
+ const raw = await readFile(this.persistPath, "utf8");
82
+ const data = JSON.parse(raw);
83
+ this.nodes = new Map(Object.entries(data.nodes || {}));
84
+ this.edges = data.edges || [];
85
+ } catch {
86
+ this.nodes = new Map();
87
+ this.edges = [];
88
+ }
89
+ }
90
+
91
+ public async save(): Promise<void> {
92
+ if (!this.persistPath) return;
93
+ try {
94
+ await mkdir(path.dirname(this.persistPath), { recursive: true });
95
+ await writeFile(this.persistPath, JSON.stringify({
96
+ nodes: Object.fromEntries(this.nodes),
97
+ edges: this.edges
98
+ }, null, 2));
99
+ } catch (err) {
100
+ console.error("Failed to persist graph memory:", err);
101
+ }
102
+ }
103
+
104
+ public getNodes(): GraphNode[] {
105
+ return Array.from(this.nodes.values());
106
+ }
107
+
108
+ public getGraphData(): { nodes: Record<string, GraphNode>; edges: GraphEdge[] } {
109
+ return {
110
+ nodes: Object.fromEntries(this.nodes),
111
+ edges: this.edges
112
+ };
113
+ }
114
+
115
+ public findByType(type: string): GraphNode[] {
116
+ return Array.from(this.nodes.values()).filter(n => n.type === type);
117
+ }
118
+
119
+ public search(query: string, limit: number = 5): GraphNode[] {
120
+ return Array.from(this.nodes.values())
121
+ .filter(n => n.content?.includes(query) || JSON.stringify(n.metadata).includes(query))
122
+ .slice(0, limit);
123
+ }
124
+
125
+ public getConnections(nodeId: string): { incoming: GraphNode[]; outgoing: GraphNode[] } {
126
+ const incoming = this.edges
127
+ .filter(e => e.to === nodeId)
128
+ .map(e => this.nodes.get(e.from)!)
129
+ .filter(Boolean);
130
+
131
+ const outgoing = this.edges
132
+ .filter(e => e.from === nodeId)
133
+ .map(e => this.nodes.get(e.to)!)
134
+ .filter(Boolean);
135
+
136
+ return { incoming, outgoing };
137
+ }
138
+
139
+ public getStats(): any {
140
+ return {
141
+ nodeCount: this.nodes.size,
142
+ edgeCount: this.edges.length,
143
+ nodesByType: Array.from(this.nodes.values()).reduce((acc: Record<string, number>, n) => {
144
+ acc[n.type] = (acc[n.type] || 0) + 1;
145
+ return acc;
146
+ }, {})
147
+ };
148
+ }
149
+ }
150
+
@@ -0,0 +1,129 @@
1
+ import { IVectorStore, MemoryDocument, SearchResult } from "../types/contracts.js";
2
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
3
+ import path from "node:path";
4
+
5
+ /**
6
+ * A lightweight, local vector store using Cosine Similarity.
7
+ * Suitable for small-to-medium datasets (thousands of records).
8
+ * Persists data to a simple JSON structure.
9
+ */
10
+ export class LocalVectorStore implements IVectorStore {
11
+ private documents: MemoryDocument[] = [];
12
+ private vectors: number[][] = [];
13
+ private metadata: Map<string, Record<string, unknown>> = new Map();
14
+
15
+ constructor(private persistPath?: string) { }
16
+
17
+ async add(documents: MemoryDocument[], vectors: number[][]): Promise<void> {
18
+ if (documents.length !== vectors.length) {
19
+ throw new Error("Mismatched documents and vectors count.");
20
+ }
21
+ this.documents.push(...documents);
22
+ this.vectors.push(...vectors);
23
+ if (this.persistPath) await this.save();
24
+ }
25
+
26
+ async search(queryVector: number[], limit: number = 5): Promise<SearchResult[]> {
27
+ if (this.vectors.length === 0) return [];
28
+
29
+ const results: SearchResult[] = this.vectors.map((vec, i) => {
30
+ const score = this.cosineSimilarity(queryVector, vec);
31
+ return { ...this.documents[i], score };
32
+ });
33
+
34
+ return results
35
+ .sort((a, b) => b.score - a.score)
36
+ .slice(0, limit);
37
+ }
38
+
39
+ async clear(): Promise<void> {
40
+ this.documents = [];
41
+ this.vectors = [];
42
+ this.metadata.clear();
43
+ if (this.persistPath) await this.save();
44
+ }
45
+
46
+ async delete(docIds: string[]): Promise<void> {
47
+ for (const id of docIds) {
48
+ const idx = this.documents.findIndex(d => d.id === id);
49
+ if (idx >= 0) {
50
+ this.documents.splice(idx, 1);
51
+ this.vectors.splice(idx, 1);
52
+ this.metadata.delete(id);
53
+ }
54
+ }
55
+ if (this.persistPath) await this.save();
56
+ }
57
+
58
+ async searchByMetadata(metadata: Record<string, unknown>): Promise<SearchResult[]> {
59
+ return this.documents.filter(doc => {
60
+ const docMeta = this.metadata.get(doc.id) || {};
61
+ return Object.entries(metadata).every(([key, value]) => docMeta[key] === value);
62
+ }).map(doc => ({ ...doc, score: 1.0 }));
63
+ }
64
+
65
+ public count(): number {
66
+ return this.documents.length;
67
+ }
68
+
69
+ public getMetrics() {
70
+ return {
71
+ documentCount: this.documents.length,
72
+ vectorDimensions: this.vectors[0]?.length || 0
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Calculates cosine similarity between two numeric vectors.
78
+ */
79
+ private cosineSimilarity(vecA: number[], vecB: number[]): number {
80
+ let dotProduct = 0;
81
+ let normA = 0;
82
+ let normB = 0;
83
+
84
+ // Using common length. Error check in a production environment.
85
+ const len = Math.min(vecA.length, vecB.length);
86
+ for (let i = 0; i < len; i++) {
87
+ dotProduct += vecA[i] * vecB[i];
88
+ normA += vecA[i] * vecA[i];
89
+ normB += vecB[i] * vecB[i];
90
+ }
91
+
92
+ const denominator = Math.sqrt(normA) * Math.sqrt(normB);
93
+ if (denominator === 0) return 0;
94
+ return dotProduct / denominator;
95
+ }
96
+
97
+ /**
98
+ * Manually triggers a load from the persistence file.
99
+ */
100
+ async load(): Promise<void> {
101
+ if (!this.persistPath) return;
102
+ try {
103
+ const raw = await readFile(this.persistPath, "utf8");
104
+ const data = JSON.parse(raw);
105
+ this.documents = data.documents || [];
106
+ this.vectors = data.vectors || [];
107
+ } catch {
108
+ // File might not exist yet, ignore
109
+ this.documents = [];
110
+ this.vectors = [];
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Internal save to disk.
116
+ */
117
+ private async save(): Promise<void> {
118
+ if (!this.persistPath) return;
119
+ try {
120
+ await mkdir(path.dirname(this.persistPath), { recursive: true });
121
+ await writeFile(this.persistPath, JSON.stringify({
122
+ documents: this.documents,
123
+ vectors: this.vectors
124
+ }));
125
+ } catch (err) {
126
+ console.error("Failed to persist vector store:", err);
127
+ }
128
+ }
129
+ }
@@ -0,0 +1,126 @@
1
+ import { IVectorStore, MemoryDocument, SearchResult } from "../types/contracts.js";
2
+ import { GraphMemory, GraphNode, GraphEdge } from "./graph-memory.js";
3
+
4
+ export interface MemoryQuery {
5
+ text?: string;
6
+ vector?: number[];
7
+ metadata?: Record<string, unknown>;
8
+ limit?: number;
9
+ }
10
+
11
+ export interface MemoryStatistics {
12
+ totalDocuments: number;
13
+ totalNodes: number;
14
+ totalEdges: number;
15
+ lastUpdated: string;
16
+ }
17
+
18
+ /**
19
+ * MemoryManager coordinates Vector search and Graph traversal.
20
+ * This is the heart of QSGM, allowing for "logical navigation" of context.
21
+ */
22
+ export class MemoryManager {
23
+ private stats: MemoryStatistics = {
24
+ totalDocuments: 0,
25
+ totalNodes: 0,
26
+ totalEdges: 0,
27
+ lastUpdated: new Date().toISOString()
28
+ };
29
+
30
+ constructor(
31
+ private vectorStore: IVectorStore,
32
+ private graphMemory: GraphMemory
33
+ ) { }
34
+
35
+ /**
36
+ * Retrieves context using a hybrid approach:
37
+ * 1. Find the most semantically relevant node via Vector Search.
38
+ * 2. Traverse the Graph from that node to find related concepts/actions/artifacts.
39
+ */
40
+ public async getQuantumContext(queryVector: number[], limit: number = 3): Promise<string> {
41
+ const vectorResults = await this.vectorStore.search(queryVector, 1);
42
+
43
+ if (vectorResults.length === 0) return "";
44
+
45
+ const seedNodeId = vectorResults[0].id;
46
+ const relatedNodes = this.graphMemory.traverse(seedNodeId, 2);
47
+
48
+ return relatedNodes
49
+ .map(node => `[QSGM_KNOWLEDGE] (${node.type.toUpperCase()})\n${node.content}`)
50
+ .join("\n\n");
51
+ }
52
+
53
+ /**
54
+ * Records a new interaction in both stores.
55
+ */
56
+ public async remember(doc: MemoryDocument, vector: number[], relatedToId?: string): Promise<void> {
57
+ // 1. Add to Vector Store for semantic search
58
+ await this.vectorStore.add([doc], [vector]);
59
+
60
+ // 2. Add to Graph Memory for logical navigation
61
+ this.graphMemory.addNode({
62
+ id: doc.id,
63
+ type: "artifact",
64
+ content: doc.text,
65
+ metadata: doc.metadata,
66
+ timestamp: doc.timestamp
67
+ });
68
+
69
+ if (relatedToId) {
70
+ this.graphMemory.addEdge({
71
+ from: relatedToId,
72
+ to: doc.id,
73
+ type: "produced"
74
+ });
75
+ }
76
+
77
+ await this.graphMemory.save();
78
+ this.stats.totalDocuments++;
79
+ this.stats.totalNodes++;
80
+ this.stats.lastUpdated = new Date().toISOString();
81
+ }
82
+
83
+ public async search(query: MemoryQuery): Promise<SearchResult[]> {
84
+ if (!query.vector) return [];
85
+ return this.vectorStore.search(query.vector, query.limit || 5);
86
+ }
87
+
88
+ public async forget(docId: string): Promise<void> {
89
+ if (this.vectorStore.delete) {
90
+ await this.vectorStore.delete([docId]);
91
+ }
92
+ this.graphMemory.removeNode(docId);
93
+ this.stats.totalDocuments = Math.max(0, this.stats.totalDocuments - 1);
94
+ }
95
+
96
+ public async clear(): Promise<void> {
97
+ this.stats = {
98
+ totalDocuments: 0,
99
+ totalNodes: 0,
100
+ totalEdges: 0,
101
+ lastUpdated: new Date().toISOString()
102
+ };
103
+ }
104
+
105
+ public getGraphData(): any {
106
+ return this.graphMemory.getGraphData();
107
+ }
108
+
109
+ public getStatistics(): MemoryStatistics {
110
+ return { ...this.stats };
111
+ }
112
+
113
+ public async batchRemember(docs: MemoryDocument[], vectors: number[][]): Promise<void> {
114
+ for (let i = 0; i < docs.length; i++) {
115
+ await this.remember(docs[i], vectors[i]);
116
+ }
117
+ }
118
+
119
+ public async export(): Promise<any> {
120
+ return {
121
+ graphs: this.getGraphData(),
122
+ timestamp: new Date().toISOString(),
123
+ stats: this.stats
124
+ };
125
+ }
126
+ }
@@ -0,0 +1,39 @@
1
+ import { ExecutionMetrics } from "../types/contracts.js";
2
+
3
+ export class MetricsEngine {
4
+ private readonly runs: ExecutionMetrics[] = [];
5
+
6
+ push(metrics: ExecutionMetrics): void {
7
+ this.runs.push(metrics);
8
+ }
9
+
10
+ summary(): {
11
+ totalRuns: number;
12
+ avgConfidence: number;
13
+ avgLatencyMs: number;
14
+ totalCostUsd: number;
15
+ interventionRate: number;
16
+ } {
17
+ if (this.runs.length === 0) {
18
+ return { totalRuns: 0, avgConfidence: 0, avgLatencyMs: 0, totalCostUsd: 0, interventionRate: 0 };
19
+ }
20
+ const total = this.runs.length;
21
+ const sums = this.runs.reduce(
22
+ (acc, run) => {
23
+ acc.conf += run.confidence;
24
+ acc.lat += run.totalLatencyMs;
25
+ acc.cost += run.totalCostUsd;
26
+ acc.human += run.humanInterventionRequired ? 1 : 0;
27
+ return acc;
28
+ },
29
+ { conf: 0, lat: 0, cost: 0, human: 0 }
30
+ );
31
+ return {
32
+ totalRuns: total,
33
+ avgConfidence: sums.conf / total,
34
+ avgLatencyMs: sums.lat / total,
35
+ totalCostUsd: Number(sums.cost.toFixed(4)),
36
+ interventionRate: sums.human / total
37
+ };
38
+ }
39
+ }