Files
Sankofa/portal/src/lib/fairness-orchestration.ts
defiQUG 9daf1fd378 Apply Composer changes: comprehensive API updates, migrations, middleware, and infrastructure improvements
- 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
2025-12-12 18:01:35 -08:00

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];
}