- Add comprehensive database migrations (001-024) for schema evolution - Enhance API schema with expanded type definitions and resolvers - Add new middleware: audit logging, rate limiting, MFA enforcement, security, tenant auth - Implement new services: AI optimization, billing, blockchain, compliance, marketplace - Add adapter layer for cloud integrations (Cloudflare, Kubernetes, Proxmox, storage) - Update Crossplane provider with enhanced VM management capabilities - Add comprehensive test suite for API endpoints and services - Update frontend components with improved GraphQL subscriptions and real-time updates - Enhance security configurations and headers (CSP, CORS, etc.) - Update documentation and configuration files - Add new CI/CD workflows and validation scripts - Implement design system improvements and UI enhancements
265 lines
8.0 KiB
TypeScript
265 lines
8.0 KiB
TypeScript
/**
|
|
* Fairness Orchestration Engine Client
|
|
* This is a client-side wrapper that would call the API in production
|
|
*/
|
|
|
|
export interface OutputType {
|
|
id: string;
|
|
name: string;
|
|
weight: number;
|
|
description: string;
|
|
}
|
|
|
|
export interface InputSpec {
|
|
dataset: string;
|
|
dateRange?: { start: string; end: string };
|
|
filters?: Record<string, any>;
|
|
sensitiveAttributes: string[];
|
|
estimatedSize?: number;
|
|
}
|
|
|
|
export interface TimelineSpec {
|
|
mode: 'now' | 'scheduled' | 'continuous';
|
|
sla?: string;
|
|
deadline?: string;
|
|
}
|
|
|
|
export interface OrchestrationRequest {
|
|
input: InputSpec;
|
|
outputs: string[];
|
|
timeline: TimelineSpec;
|
|
}
|
|
|
|
export interface OrchestrationResult {
|
|
totalLoad: number;
|
|
inputLoad: number;
|
|
outputLoad: number;
|
|
estimatedTime: number;
|
|
feasible: boolean;
|
|
warnings: string[];
|
|
suggestions: string[];
|
|
}
|
|
|
|
// Output type definitions
|
|
export const OUTPUT_TYPES: Record<string, OutputType> = {
|
|
'fairness-audit-pdf': {
|
|
id: 'fairness-audit-pdf',
|
|
name: 'Fairness Audit PDF',
|
|
weight: 2.5,
|
|
description: 'Comprehensive fairness audit report in PDF format'
|
|
},
|
|
'metrics-export': {
|
|
id: 'metrics-export',
|
|
name: 'Metrics Export (SPD, TPR, FPR)',
|
|
weight: 1.0,
|
|
description: 'Statistical parity difference, true positive rate, false positive rate metrics'
|
|
},
|
|
'flagged-cases-csv': {
|
|
id: 'flagged-cases-csv',
|
|
name: 'Flagged Cases CSV',
|
|
weight: 1.5,
|
|
description: 'Export of cases flagged for potential bias issues'
|
|
},
|
|
'exec-summary-slides': {
|
|
id: 'exec-summary-slides',
|
|
name: 'Executive Summary Slide Pack',
|
|
weight: 2.0,
|
|
description: 'Executive presentation slides with key findings'
|
|
},
|
|
'detailed-report-json': {
|
|
id: 'detailed-report-json',
|
|
name: 'Detailed Report (JSON)',
|
|
weight: 1.2,
|
|
description: 'Machine-readable detailed fairness analysis'
|
|
},
|
|
'alerts-config': {
|
|
id: 'alerts-config',
|
|
name: 'Alert Configuration',
|
|
weight: 0.8,
|
|
description: 'Automated alert rules for ongoing monitoring'
|
|
},
|
|
'dashboard-export': {
|
|
id: 'dashboard-export',
|
|
name: 'Dashboard Export',
|
|
weight: 1.8,
|
|
description: 'Interactive dashboard with fairness metrics'
|
|
},
|
|
'compliance-report': {
|
|
id: 'compliance-report',
|
|
name: 'Compliance Report',
|
|
weight: 2.2,
|
|
description: 'Regulatory compliance documentation'
|
|
}
|
|
};
|
|
|
|
const INPUT_PASS_MULTIPLIER = 2.0;
|
|
const TOTAL_LOAD_MULTIPLIER = 3.2;
|
|
const OUTPUT_TARGET_MULTIPLIER = 1.2;
|
|
const BASE_PROCESSING_RATE = 10;
|
|
const INPUT_PROCESSING_RATE = 15;
|
|
const OUTPUT_PROCESSING_RATE = 8;
|
|
|
|
export function calculateInputLoad(input: InputSpec): number {
|
|
if (input.estimatedSize) {
|
|
return input.estimatedSize;
|
|
}
|
|
|
|
let baseSize = 100;
|
|
baseSize += input.sensitiveAttributes.length * 20;
|
|
|
|
if (input.dateRange) {
|
|
const start = new Date(input.dateRange.start);
|
|
const end = new Date(input.dateRange.end);
|
|
const days = Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
|
|
baseSize += days * 5;
|
|
}
|
|
|
|
if (input.filters) {
|
|
baseSize += Object.keys(input.filters).length * 10;
|
|
}
|
|
|
|
return baseSize;
|
|
}
|
|
|
|
export function calculateOutputLoad(outputIds: string[]): number {
|
|
return outputIds.reduce((total, outputId) => {
|
|
const output = OUTPUT_TYPES[outputId];
|
|
if (!output) return total;
|
|
return total + output.weight;
|
|
}, 0);
|
|
}
|
|
|
|
export function calculateTotalLoad(inputLoad: number, outputLoad: number): number {
|
|
const inputPasses = inputLoad * INPUT_PASS_MULTIPLIER;
|
|
return outputLoad + inputPasses;
|
|
}
|
|
|
|
export function estimateProcessingTime(totalLoad: number): number {
|
|
const avgRate = (INPUT_PROCESSING_RATE + OUTPUT_PROCESSING_RATE) / 2;
|
|
return totalLoad / avgRate;
|
|
}
|
|
|
|
function parseSLAToSeconds(sla: string): number {
|
|
const match = sla.match(/(\d+)\s*(hour|hours|day|days|minute|minutes|min|mins|second|seconds|sec|secs)/i);
|
|
if (!match) return 3600;
|
|
|
|
const value = parseInt(match[1], 10);
|
|
const unit = match[2].toLowerCase();
|
|
|
|
const multipliers: Record<string, number> = {
|
|
second: 1, seconds: 1, sec: 1, secs: 1,
|
|
minute: 60, minutes: 60, min: 60, mins: 60,
|
|
hour: 3600, hours: 3600,
|
|
day: 86400, days: 86400
|
|
};
|
|
|
|
return value * (multipliers[unit] || 3600);
|
|
}
|
|
|
|
function formatTime(seconds: number): string {
|
|
if (seconds < 60) return `${Math.ceil(seconds)} seconds`;
|
|
if (seconds < 3600) return `${(seconds / 60).toFixed(1)} minutes`;
|
|
if (seconds < 86400) return `${(seconds / 3600).toFixed(1)} hours`;
|
|
return `${(seconds / 86400).toFixed(1)} days`;
|
|
}
|
|
|
|
export function checkFeasibility(
|
|
totalLoad: number,
|
|
estimatedTime: number,
|
|
timeline: TimelineSpec
|
|
): { feasible: boolean; warnings: string[]; suggestions: string[] } {
|
|
const warnings: string[] = [];
|
|
const suggestions: string[] = [];
|
|
|
|
let maxTimeSeconds: number | null = null;
|
|
if (timeline.sla) {
|
|
maxTimeSeconds = parseSLAToSeconds(timeline.sla);
|
|
} else if (timeline.deadline) {
|
|
const now = Date.now();
|
|
const deadline = new Date(timeline.deadline).getTime();
|
|
maxTimeSeconds = Math.max(0, (deadline - now) / 1000);
|
|
}
|
|
|
|
const inputLoad = totalLoad / TOTAL_LOAD_MULTIPLIER;
|
|
const outputLoad = totalLoad - (inputLoad * INPUT_PASS_MULTIPLIER);
|
|
const targetOutputLoad = inputLoad * OUTPUT_TARGET_MULTIPLIER;
|
|
|
|
if (outputLoad > targetOutputLoad * 1.5) {
|
|
warnings.push(
|
|
`Output complexity (${outputLoad.toFixed(1)} units) is significantly higher than recommended (${targetOutputLoad.toFixed(1)} units)`
|
|
);
|
|
suggestions.push('Consider reducing the number of outputs or simplifying output requirements');
|
|
}
|
|
|
|
if (maxTimeSeconds !== null) {
|
|
if (estimatedTime > maxTimeSeconds) {
|
|
warnings.push(
|
|
`Estimated processing time (${formatTime(estimatedTime)}) exceeds requested timeline (${formatTime(maxTimeSeconds)})`
|
|
);
|
|
suggestions.push(`Consider extending timeline to ${formatTime(estimatedTime * 1.2)} or reducing outputs`);
|
|
} else if (estimatedTime > maxTimeSeconds * 0.8) {
|
|
warnings.push(
|
|
`Estimated processing time (${formatTime(estimatedTime)}) is close to timeline limit (${formatTime(maxTimeSeconds)})`
|
|
);
|
|
suggestions.push('Consider adding buffer time or reducing outputs for safety');
|
|
}
|
|
}
|
|
|
|
const expectedTotalLoad = inputLoad * TOTAL_LOAD_MULTIPLIER;
|
|
if (totalLoad > expectedTotalLoad * 1.3) {
|
|
warnings.push(
|
|
`Total process load (${totalLoad.toFixed(1)} units) is higher than expected (${expectedTotalLoad.toFixed(1)} units)`
|
|
);
|
|
}
|
|
|
|
const feasible = warnings.length === 0 || warnings.every(w => !w.includes('exceeds'));
|
|
|
|
return { feasible, warnings, suggestions };
|
|
}
|
|
|
|
export function orchestrate(request: OrchestrationRequest): OrchestrationResult {
|
|
const inputLoad = calculateInputLoad(request.input);
|
|
const outputLoad = calculateOutputLoad(request.outputs);
|
|
const totalLoad = calculateTotalLoad(inputLoad, outputLoad);
|
|
const estimatedTime = estimateProcessingTime(totalLoad);
|
|
|
|
const { feasible, warnings, suggestions } = checkFeasibility(
|
|
totalLoad,
|
|
estimatedTime,
|
|
request.timeline
|
|
);
|
|
|
|
return {
|
|
totalLoad,
|
|
inputLoad,
|
|
outputLoad,
|
|
estimatedTime,
|
|
feasible,
|
|
warnings,
|
|
suggestions
|
|
};
|
|
}
|
|
|
|
export function getUserMessage(result: OrchestrationResult, request: OrchestrationRequest): string {
|
|
const { inputLoad, outputLoad, estimatedTime, feasible, warnings } = result;
|
|
|
|
if (feasible && warnings.length === 0) {
|
|
return `This fairness audit will process approximately ${inputLoad.toFixed(0)} input units and generate ${outputLoad.toFixed(1)} output units, taking approximately ${formatTime(estimatedTime)} to complete.`;
|
|
}
|
|
|
|
if (feasible) {
|
|
return `This audit is feasible but has some considerations: ${warnings.join('; ')}. Estimated time: ${formatTime(estimatedTime)}.`;
|
|
}
|
|
|
|
return `This audit configuration may not be feasible within the requested timeline. ${warnings.join('; ')}. Estimated time: ${formatTime(estimatedTime)}.`;
|
|
}
|
|
|
|
export function getAvailableOutputs(): OutputType[] {
|
|
return Object.values(OUTPUT_TYPES);
|
|
}
|
|
|
|
export function getOutputType(id: string): OutputType | undefined {
|
|
return OUTPUT_TYPES[id];
|
|
}
|
|
|