import { z } from 'zod';
import { createLogger } from '../utils/logger.js';
/**
* Response Adapter Service
*
* Adapts responses based on AI agent preferences, format requirements,
* and interaction patterns for optimal consumption.
*/
// Format schemas
export const ResponseFormatSchema = z.enum([
'full_json',
'executive_only',
'narrative',
'structured_summary',
'conversational',
'markdown_report',
'csv_data',
'visual_ready'
]);
export const AgentPreferencesSchema = z.object({
preferred_format: ResponseFormatSchema,
detail_level: z.enum(['minimal', 'standard', 'comprehensive']),
language_style: z.enum(['formal', 'casual', 'technical', 'executive']),
include_visuals: z.boolean().default(false),
max_response_tokens: z.number().optional(),
focus_areas: z.array(z.string()).optional(),
skip_sections: z.array(z.string()).optional()
});
export const AdaptedResponseSchema = z.object({
format: ResponseFormatSchema,
content: z.any(),
metadata: z.object({
original_format: z.string(),
adaptation_notes: z.array(z.string()),
tokens_saved: z.number().optional(),
quality_score: z.number().min(0).max(1)
})
});
export type ResponseFormat = z.infer<typeof ResponseFormatSchema>;
export type AgentPreferences = z.infer<typeof AgentPreferencesSchema>;
export type AdaptedResponse = z.infer<typeof AdaptedResponseSchema>;
export class ResponseAdapter {
private logger = createLogger({ component: 'ResponseAdapter' });
// Default preferences for different agent types
private readonly AGENT_PROFILES = {
executive: {
preferred_format: 'executive_only' as ResponseFormat,
detail_level: 'minimal' as const,
language_style: 'executive' as const,
include_visuals: true,
max_response_tokens: 500
},
analyst: {
preferred_format: 'structured_summary' as ResponseFormat,
detail_level: 'comprehensive' as const,
language_style: 'technical' as const,
include_visuals: true,
focus_areas: ['financial_metrics', 'risk_analysis']
},
conversational: {
preferred_format: 'conversational' as ResponseFormat,
detail_level: 'standard' as const,
language_style: 'casual' as const,
include_visuals: false,
max_response_tokens: 1000
}
};
/**
* Adapt response based on detected or specified preferences
*/
async adaptResponse(
response: any,
preferences?: AgentPreferences,
context?: any
): Promise<AdaptedResponse> {
this.logger.debug('Adapting response', {
format: preferences?.preferred_format,
detail_level: preferences?.detail_level
});
// Detect preferences if not provided
const effectivePreferences = preferences || this.detectPreferences(context);
// Adapt based on format
let adaptedContent: any;
switch (effectivePreferences.preferred_format) {
case 'executive_only':
adaptedContent = this.formatExecutiveOnly(response, effectivePreferences);
break;
case 'narrative':
adaptedContent = this.formatNarrative(response, effectivePreferences);
break;
case 'structured_summary':
adaptedContent = this.formatStructuredSummary(response, effectivePreferences);
break;
case 'conversational':
adaptedContent = this.formatConversational(response, effectivePreferences);
break;
case 'markdown_report':
adaptedContent = this.formatMarkdownReport(response, effectivePreferences);
break;
case 'csv_data':
adaptedContent = this.formatCSVData(response);
break;
case 'visual_ready':
adaptedContent = this.formatVisualReady(response);
break;
default:
adaptedContent = response; // Full JSON
}
// Apply token limits if specified
if (effectivePreferences.max_response_tokens) {
adaptedContent = this.applyTokenLimit(adaptedContent, effectivePreferences.max_response_tokens);
}
// Calculate adaptation metadata
const metadata = this.generateAdaptationMetadata(
response,
adaptedContent,
effectivePreferences
);
return {
format: effectivePreferences.preferred_format,
content: adaptedContent,
metadata
};
}
/**
* Detect agent type and preferences from interaction patterns
*/
async detectAgentType(
queryHistory: any[],
currentQuery: any
): Promise<{
agent_type: string;
confidence: number;
detected_patterns: string[];
recommended_preferences: AgentPreferences;
}> {
this.logger.debug('Detecting agent type from patterns');
const patterns = this.analyzeInteractionPatterns(queryHistory, currentQuery);
const agentType = this.classifyAgentType(patterns);
const confidence = this.calculateTypeConfidence(patterns, agentType);
return {
agent_type: agentType,
confidence,
detected_patterns: patterns.detected,
recommended_preferences: this.AGENT_PROFILES[agentType as keyof typeof this.AGENT_PROFILES] ||
this.AGENT_PROFILES.analyst
};
}
/**
* Optimize response for specific use cases
*/
async optimizeForUseCase(
response: any,
useCase: 'decision_making' | 'exploration' | 'reporting' | 'monitoring'
): Promise<any> {
this.logger.debug('Optimizing for use case', { useCase });
switch (useCase) {
case 'decision_making':
return this.optimizeForDecisionMaking(response);
case 'exploration':
return this.optimizeForExploration(response);
case 'reporting':
return this.optimizeForReporting(response);
case 'monitoring':
return this.optimizeForMonitoring(response);
default:
return response;
}
}
/**
* Convert response to specific output formats
*/
async convertToFormat(
response: any,
targetFormat: 'json' | 'yaml' | 'xml' | 'html' | 'pdf_ready'
): Promise<string> {
this.logger.debug('Converting to format', { targetFormat });
switch (targetFormat) {
case 'json':
return JSON.stringify(response, null, 2);
case 'yaml':
return this.convertToYAML(response);
case 'xml':
return this.convertToXML(response);
case 'html':
return this.convertToHTML(response);
case 'pdf_ready':
return this.convertToPDFReady(response);
default:
return JSON.stringify(response);
}
}
// Private formatting methods
private formatExecutiveOnly(response: any, preferences: AgentPreferences): any {
const executive = {
headline: response.executive_summary?.headline || 'Analysis Complete',
key_metrics: this.extractKeyMetrics(response),
decision_required: this.extractDecisionPoints(response),
recommendations: this.extractTopRecommendations(response, 3),
risks: this.extractTopRisks(response, 2),
next_steps: response.recommendations?.next_action || 'Review detailed analysis'
};
// Apply language style
if (preferences.language_style === 'executive') {
executive.headline = this.makeExecutiveLanguage(executive.headline);
}
return executive;
}
private formatNarrative(response: any, preferences: AgentPreferences): string {
let narrative = '';
// Opening
narrative += response.executive_summary?.headline || 'Analysis Results';
narrative += '\n\n';
// Context
if (response.narrative?.context) {
narrative += response.narrative.context + '\n\n';
}
// Key insights
narrative += 'Key Findings:\n';
const insights = response.insights?.primary || [];
insights.forEach((insight: string, i: number) => {
narrative += `${i + 1}. ${insight}\n`;
});
narrative += '\n';
// Recommendations
if (response.recommendations) {
narrative += `Recommended Action: ${response.recommendations.next_action}\n`;
narrative += `Timeline: ${response.recommendations.timeline}\n\n`;
}
// Conclusion
if (response.narrative?.conclusion) {
narrative += response.narrative.conclusion;
}
return narrative;
}
private formatStructuredSummary(response: any, preferences: AgentPreferences): any {
const summary: any = {
overview: {
status: response.executive_summary?.confidence || 'complete',
summary: response.executive_summary?.headline,
key_metric: response.executive_summary?.primary_metric
},
insights: {},
recommendations: {},
data_points: {}
};
// Structure insights by category
if (response.insights) {
summary.insights = {
opportunities: response.insights.opportunities?.slice(0, 3),
risks: response.insights.risks?.slice(0, 3),
patterns: response.insights.patterns?.slice(0, 2)
};
}
// Structure recommendations
if (response.recommendations) {
summary.recommendations = {
immediate: response.recommendations.next_action,
timeline: response.recommendations.timeline,
prerequisites: response.recommendations.prerequisites?.slice(0, 3)
};
}
// Key data points based on focus areas
if (preferences.focus_areas) {
summary.data_points = this.extractFocusAreaData(response, preferences.focus_areas);
}
return summary;
}
private formatConversational(response: any, preferences: AgentPreferences): string {
let conversation = '';
// Friendly opening
const confidence = response.executive_summary?.confidence;
if (confidence === 'high') {
conversation += "Great news! I've completed the analysis with high confidence. ";
} else {
conversation += "I've finished analyzing the data. ";
}
// Main insight in conversational tone
conversation += response.executive_summary?.key_insight ||
"Here's what I found.";
conversation += '\n\n';
// Key points
conversation += "The main things you should know:\n";
const points = [
response.executive_summary?.primary_metric,
response.insights?.primary?.[0],
response.recommendations?.next_action
].filter(Boolean);
points.forEach((point, i) => {
conversation += `• ${point}\n`;
});
// Closing with next steps
conversation += '\n';
if (response.recommendations?.timeline) {
conversation += `I'd recommend ${response.recommendations.timeline.toLowerCase()}. `;
}
conversation += "Would you like me to dive deeper into any specific area?";
return conversation;
}
private formatMarkdownReport(response: any, preferences: AgentPreferences): string {
let markdown = '';
// Title
markdown += `# ${response.executive_summary?.headline || 'Analysis Report'}\n\n`;
// Executive Summary
markdown += '## Executive Summary\n\n';
markdown += `**Confidence Level:** ${response.executive_summary?.confidence || 'N/A'}\n\n`;
markdown += `**Key Insight:** ${response.executive_summary?.key_insight || 'N/A'}\n\n`;
if (response.executive_summary?.primary_metric) {
markdown += `**Primary Metric:** ${response.executive_summary.primary_metric}\n\n`;
}
// Insights Section
if (response.insights) {
markdown += '## Key Insights\n\n';
if (response.insights.primary?.length > 0) {
markdown += '### Primary Findings\n';
response.insights.primary.forEach((insight: string) => {
markdown += `- ${insight}\n`;
});
markdown += '\n';
}
if (response.insights.risks?.length > 0) {
markdown += '### Risk Factors\n';
response.insights.risks.forEach((risk: string) => {
markdown += `- ⚠️ ${risk}\n`;
});
markdown += '\n';
}
if (response.insights.opportunities?.length > 0) {
markdown += '### Opportunities\n';
response.insights.opportunities.forEach((opp: string) => {
markdown += `- 💡 ${opp}\n`;
});
markdown += '\n';
}
}
// Recommendations
if (response.recommendations) {
markdown += '## Recommendations\n\n';
markdown += `**Next Action:** ${response.recommendations.next_action}\n\n`;
markdown += `**Timeline:** ${response.recommendations.timeline}\n\n`;
if (response.recommendations.success_criteria) {
markdown += '**Success Criteria:**\n';
response.recommendations.success_criteria.forEach((criteria: string) => {
markdown += `- ${criteria}\n`;
});
markdown += '\n';
}
}
// Data Quality
if (response.metadata) {
markdown += '## Metadata\n\n';
markdown += `- **Data Quality:** ${response.metadata.data_quality}\n`;
markdown += `- **Confidence Score:** ${(response.metadata.confidence_score * 100).toFixed(0)}%\n`;
markdown += `- **Generated:** ${new Date(response.metadata.generated_at).toLocaleString()}\n`;
}
return markdown;
}
private formatCSVData(response: any): string {
const rows = [];
// Header row
rows.push(['Metric', 'Value', 'Category', 'Confidence']);
// Extract flat data
if (response.summary) {
Object.entries(response.summary).forEach(([key, value]) => {
rows.push([key, String(value), 'Summary', 'High']);
});
}
// Financial metrics
if (response.financial_metrics?.expected) {
Object.entries(response.financial_metrics.expected).forEach(([key, value]) => {
rows.push([key, String(value), 'Financial', 'Expected']);
});
}
// Convert to CSV
return rows.map(row => row.map(cell =>
cell.includes(',') ? `"${cell}"` : cell
).join(',')).join('\n');
}
private formatVisualReady(response: any): any {
return {
charts: [
{
type: 'bar',
title: 'ROI Comparison',
data: this.prepareROIChartData(response),
options: {
responsive: true,
maintainAspectRatio: false
}
},
{
type: 'timeline',
title: 'Implementation Roadmap',
data: this.prepareTimelineData(response),
options: {
responsive: true
}
},
{
type: 'pie',
title: 'Value Distribution',
data: this.prepareValueDistribution(response),
options: {
responsive: true
}
}
],
tables: [
{
title: 'Key Metrics',
headers: ['Metric', 'Value', 'Target', 'Status'],
rows: this.prepareMetricsTable(response)
}
],
kpis: this.prepareKPIs(response)
};
}
// Private helper methods
private detectPreferences(context: any): AgentPreferences {
// Simple heuristic-based detection
if (context?.query?.includes('executive') || context?.query?.includes('summary only')) {
return this.AGENT_PROFILES.executive;
}
if (context?.query?.includes('tell me') || context?.query?.includes('explain')) {
return this.AGENT_PROFILES.conversational;
}
// Default to analyst profile
return this.AGENT_PROFILES.analyst;
}
private extractKeyMetrics(response: any): any {
return {
roi: response.summary?.expected_roi || response.executive_summary?.primary_metric,
payback: response.summary?.payback_period_months,
investment: response.summary?.total_investment,
confidence: response.metadata?.confidence_score
};
}
private extractDecisionPoints(response: any): string[] {
const decisions = [];
if (response.recommendations?.next_action) {
decisions.push(`Approve: ${response.recommendations.next_action}`);
}
if (response.summary?.total_investment > 1000000) {
decisions.push('Authorize budget allocation');
}
return decisions;
}
private extractTopRecommendations(response: any, limit: number): string[] {
const recommendations = [];
if (response.recommendations?.next_action) {
recommendations.push(response.recommendations.next_action);
}
if (response.insights?.opportunities) {
recommendations.push(...response.insights.opportunities.slice(0, limit - 1));
}
return recommendations.slice(0, limit);
}
private extractTopRisks(response: any, limit: number): string[] {
if (response.insights?.risks) {
return response.insights.risks.slice(0, limit);
}
return [];
}
private makeExecutiveLanguage(text: string): string {
// Simple transformations for executive communication
return text
.replace(/We recommend/g, 'Recommendation:')
.replace(/You should/g, 'Action required:')
.replace(/It is suggested/g, 'Consider:');
}
private applyTokenLimit(content: any, maxTokens: number): any {
const stringified = JSON.stringify(content);
const estimatedTokens = stringified.length / 4; // Rough estimate
if (estimatedTokens <= maxTokens) {
return content;
}
// Progressively remove less important sections
if (content.detailed_analysis) {
delete content.detailed_analysis;
}
if (content.metadata && estimatedTokens > maxTokens) {
content.metadata = {
note: 'Full metadata available on request'
};
}
return content;
}
private generateAdaptationMetadata(
original: any,
adapted: any,
preferences: AgentPreferences
): AdaptedResponse['metadata'] {
const originalSize = JSON.stringify(original).length;
const adaptedSize = JSON.stringify(adapted).length;
const notes = [];
if (preferences.detail_level === 'minimal') {
notes.push('Reduced to essential information only');
}
if (preferences.skip_sections?.length) {
notes.push(`Skipped sections: ${preferences.skip_sections.join(', ')}`);
}
if (preferences.max_response_tokens) {
notes.push(`Applied token limit: ${preferences.max_response_tokens}`);
}
return {
original_format: 'full_json',
adaptation_notes: notes,
tokens_saved: Math.max(0, Math.floor((originalSize - adaptedSize) / 4)),
quality_score: this.assessAdaptationQuality(original, adapted, preferences)
};
}
private assessAdaptationQuality(original: any, adapted: any, preferences: AgentPreferences): number {
let score = 1.0;
// Penalize if critical information is missing
if (!adapted.executive_summary && !adapted.headline) {
score -= 0.2;
}
// Reward if format matches preference
if (preferences.preferred_format === 'executive_only' && !adapted.detailed_analysis) {
score += 0.1;
}
// Check completeness based on detail level
if (preferences.detail_level === 'comprehensive' && !adapted.metadata) {
score -= 0.1;
}
return Math.max(0, Math.min(1, score));
}
private analyzeInteractionPatterns(queryHistory: any[], currentQuery: any): any {
const patterns = {
detected: [] as string[],
query_complexity: 'medium',
detail_requests: 0,
summary_requests: 0,
visual_requests: 0
};
// Analyze current query
const queryLower = currentQuery?.toLowerCase() || '';
if (queryLower.includes('summary') || queryLower.includes('brief')) {
patterns.summary_requests++;
patterns.detected.push('Prefers summaries');
}
if (queryLower.includes('detail') || queryLower.includes('comprehensive')) {
patterns.detail_requests++;
patterns.detected.push('Requests detailed analysis');
}
if (queryLower.includes('chart') || queryLower.includes('visual')) {
patterns.visual_requests++;
patterns.detected.push('Prefers visual representations');
}
// Analyze history
if (queryHistory.length > 0) {
const avgQueryLength = queryHistory.reduce((sum, q) => sum + (q.query?.length || 0), 0) / queryHistory.length;
if (avgQueryLength < 50) {
patterns.detected.push('Short, direct queries');
patterns.query_complexity = 'simple';
} else if (avgQueryLength > 200) {
patterns.detected.push('Detailed, complex queries');
patterns.query_complexity = 'complex';
}
}
return patterns;
}
private classifyAgentType(patterns: any): string {
if (patterns.summary_requests > patterns.detail_requests) {
return 'executive';
}
if (patterns.visual_requests > 0 || patterns.query_complexity === 'complex') {
return 'analyst';
}
if (patterns.query_complexity === 'simple' && patterns.detected.includes('Short, direct queries')) {
return 'conversational';
}
return 'analyst'; // Default
}
private calculateTypeConfidence(patterns: any, agentType: string): number {
let confidence = 0.5; // Base confidence
// Increase confidence based on pattern matches
if (agentType === 'executive' && patterns.summary_requests > 2) {
confidence += 0.3;
}
if (agentType === 'analyst' && patterns.detail_requests > 1) {
confidence += 0.2;
}
if (patterns.detected.length > 3) {
confidence += 0.1;
}
return Math.min(0.95, confidence);
}
private extractFocusAreaData(response: any, focusAreas: string[]): any {
const data: any = {};
focusAreas.forEach(area => {
switch (area) {
case 'financial_metrics':
data.financial = {
roi: response.summary?.expected_roi,
npv: response.summary?.net_present_value,
payback: response.summary?.payback_period_months
};
break;
case 'risk_analysis':
data.risks = {
factors: response.insights?.risks,
mitigation: response.recommendations?.prerequisites
};
break;
case 'implementation':
data.implementation = {
timeline: response.recommendations?.timeline,
next_steps: response.recommendations?.next_action
};
break;
}
});
return data;
}
private optimizeForDecisionMaking(response: any): any {
return {
decision_summary: {
recommendation: response.recommendations?.next_action || 'Proceed with analysis',
confidence: response.executive_summary?.confidence || 'medium',
key_factors: [
response.executive_summary?.key_insight,
response.executive_summary?.primary_metric
].filter(Boolean)
},
supporting_data: {
pros: response.insights?.opportunities?.slice(0, 3) || [],
cons: response.insights?.risks?.slice(0, 3) || [],
alternatives: response.recommendations?.alternatives || []
},
decision_criteria: {
roi_threshold_met: (response.summary?.expected_roi || 0) > 50,
risk_acceptable: response.metadata?.assumptions_impact !== 'high',
timeline_feasible: (response.summary?.payback_period_months || 24) <= 24
}
};
}
private optimizeForExploration(response: any): any {
return {
overview: response.executive_summary,
explore_areas: {
insights: {
title: 'Key Insights',
items: response.insights?.primary || [],
drill_down_available: true
},
opportunities: {
title: 'Growth Opportunities',
items: response.insights?.opportunities || [],
drill_down_available: true
},
risks: {
title: 'Risk Factors',
items: response.insights?.risks || [],
drill_down_available: true
}
},
suggested_next_queries: [
'Show me detailed financial projections',
'What are the implementation requirements?',
'Compare with industry benchmarks'
]
};
}
private optimizeForReporting(response: any): any {
return {
report_metadata: {
generated_at: response.metadata?.generated_at || new Date().toISOString(),
report_type: 'ROI Analysis',
confidence_level: response.metadata?.confidence_score || 0.85
},
executive_summary: response.executive_summary,
detailed_findings: {
financial_analysis: response.summary,
insights_and_patterns: response.insights,
recommendations: response.recommendations
},
appendices: {
methodology: response.metadata?.calculation_context?.methodology,
assumptions: response.metadata?.assumptions,
data_quality: response.metadata?.data_quality
}
};
}
private optimizeForMonitoring(response: any): any {
const metrics = response.summary || {};
const previousMetrics = response.previous_period || {};
return {
current_status: {
health_score: this.calculateHealthScore(response),
key_metrics: {
roi: metrics.expected_roi,
payback: metrics.payback_period_months,
confidence: response.metadata?.confidence_score
}
},
changes: {
roi_change: this.calculateChange(metrics.expected_roi, previousMetrics.expected_roi),
risk_change: response.insights?.risks?.length || 0,
new_opportunities: response.insights?.opportunities?.length || 0
},
alerts: this.generateAlerts(response),
trend_indicators: {
roi_trend: 'stable', // Would calculate from history
risk_trend: 'increasing',
timeline_trend: 'on_track'
}
};
}
private calculateHealthScore(response: any): number {
let score = 0.5; // Base score
// Positive factors
if (response.summary?.expected_roi > 100) score += 0.2;
if (response.summary?.payback_period_months < 12) score += 0.1;
if (response.executive_summary?.confidence === 'high') score += 0.1;
// Negative factors
if (response.insights?.risks?.length > 3) score -= 0.1;
if (response.metadata?.data_quality === 'low') score -= 0.1;
return Math.max(0, Math.min(1, score));
}
private calculateChange(current: number | undefined, previous: number | undefined): string {
if (!current || !previous) return 'N/A';
const change = ((current - previous) / previous) * 100;
const direction = change > 0 ? '↑' : change < 0 ? '↓' : '→';
return `${direction} ${Math.abs(change).toFixed(1)}%`;
}
private generateAlerts(response: any): string[] {
const alerts = [];
if (response.insights?.risks?.some((r: string) => r.toLowerCase().includes('critical'))) {
alerts.push('⚠️ Critical risk identified');
}
if (response.summary?.payback_period_months > 24) {
alerts.push('📊 Extended payback period requires attention');
}
if (response.metadata?.confidence_score < 0.7) {
alerts.push('📉 Low confidence in projections');
}
return alerts;
}
// Format conversion methods
private convertToYAML(response: any): string {
// Simplified YAML conversion
const yaml = this.objectToYAML(response, 0);
return yaml;
}
private objectToYAML(obj: any, indent: number): string {
let yaml = '';
const indentStr = ' '.repeat(indent);
for (const [key, value] of Object.entries(obj)) {
if (value === null || value === undefined) {
yaml += `${indentStr}${key}: null\n`;
} else if (typeof value === 'object' && !Array.isArray(value)) {
yaml += `${indentStr}${key}:\n${this.objectToYAML(value, indent + 1)}`;
} else if (Array.isArray(value)) {
yaml += `${indentStr}${key}:\n`;
value.forEach(item => {
yaml += `${indentStr}- ${typeof item === 'object' ? '\n' + this.objectToYAML(item, indent + 2) : item}\n`;
});
} else {
yaml += `${indentStr}${key}: ${value}\n`;
}
}
return yaml;
}
private convertToXML(response: any): string {
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
xml += '<response>\n';
xml += this.objectToXML(response, 1);
xml += '</response>';
return xml;
}
private objectToXML(obj: any, indent: number): string {
let xml = '';
const indentStr = ' '.repeat(indent);
for (const [key, value] of Object.entries(obj)) {
const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, '_');
if (value === null || value === undefined) {
xml += `${indentStr}<${safeKey}/>\n`;
} else if (typeof value === 'object' && !Array.isArray(value)) {
xml += `${indentStr}<${safeKey}>\n${this.objectToXML(value, indent + 1)}${indentStr}</${safeKey}>\n`;
} else if (Array.isArray(value)) {
value.forEach(item => {
xml += `${indentStr}<${safeKey}>${typeof item === 'object' ? '\n' + this.objectToXML(item, indent + 1) + indentStr : item}</${safeKey}>\n`;
});
} else {
xml += `${indentStr}<${safeKey}>${this.escapeXML(String(value))}</${safeKey}>\n`;
}
}
return xml;
}
private escapeXML(str: string): string {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
private convertToHTML(response: any): string {
const markdown = this.formatMarkdownReport(response, this.AGENT_PROFILES.analyst);
// Simple markdown to HTML conversion
return markdown
.replace(/^# (.+)$/gm, '<h1>$1</h1>')
.replace(/^## (.+)$/gm, '<h2>$1</h2>')
.replace(/^### (.+)$/gm, '<h3>$1</h3>')
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
.replace(/^- (.+)$/gm, '<li>$1</li>')
.replace(/\n\n/g, '</p><p>')
.replace(/^/, '<p>')
.replace(/$/, '</p>');
}
private convertToPDFReady(response: any): string {
// Structure optimized for PDF generation
return JSON.stringify({
document: {
title: response.executive_summary?.headline || 'ROI Analysis Report',
metadata: {
created: new Date().toISOString(),
author: 'MCP Server ROI',
subject: 'AI Investment Analysis'
},
sections: [
{
type: 'cover',
content: {
title: response.executive_summary?.headline,
subtitle: response.executive_summary?.key_insight,
date: new Date().toLocaleDateString()
}
},
{
type: 'summary',
title: 'Executive Summary',
content: this.formatExecutiveOnly(response, this.AGENT_PROFILES.executive)
},
{
type: 'details',
title: 'Detailed Analysis',
content: response
}
]
}
}, null, 2);
}
// Chart data preparation methods
private prepareROIChartData(response: any): any {
const scenarios = ['Conservative', 'Expected', 'Optimistic'];
const values = [
response.financial_metrics?.conservative?.total_monthly_benefit || 0,
response.financial_metrics?.expected?.total_monthly_benefit || 0,
response.financial_metrics?.optimistic?.total_monthly_benefit || 0
];
return {
labels: scenarios,
datasets: [{
label: 'Monthly Benefit',
data: values,
backgroundColor: ['#FF6384', '#36A2EB', '#4BC0C0']
}]
};
}
private prepareTimelineData(response: any): any {
const milestones = [];
if (response.recommendations?.timeline) {
milestones.push({
date: new Date().toISOString(),
label: 'Start',
description: 'Project initiation'
});
// Add milestones based on timeline
const months = parseInt(response.summary?.payback_period_months || '12');
milestones.push({
date: new Date(Date.now() + months * 30 * 24 * 60 * 60 * 1000).toISOString(),
label: 'Payback',
description: 'Expected payback achieved'
});
}
return milestones;
}
private prepareValueDistribution(response: any): any {
const categories: Record<string, number> = {};
response.use_cases?.forEach((uc: any) => {
const category = uc.category as string;
if (!categories[category]) {
categories[category] = 0;
}
categories[category] += uc.monthly_benefit || 0;
});
return {
labels: Object.keys(categories),
datasets: [{
data: Object.values(categories),
backgroundColor: [
'#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF'
]
}]
};
}
private prepareMetricsTable(response: any): any[] {
const rows = [];
if (response.summary) {
rows.push([
'ROI',
`${response.summary.expected_roi}%`,
'100%',
response.summary.expected_roi > 100 ? '✅' : '⚠️'
]);
rows.push([
'Payback Period',
`${response.summary.payback_period_months} months`,
'18 months',
response.summary.payback_period_months <= 18 ? '✅' : '⚠️'
]);
rows.push([
'NPV',
`$${(response.summary.net_present_value / 1000).toFixed(0)}K`,
'>$0',
response.summary.net_present_value > 0 ? '✅' : '❌'
]);
}
return rows;
}
private prepareKPIs(response: any): any[] {
return [
{
label: 'Expected ROI',
value: `${response.summary?.expected_roi || 0}%`,
trend: 'up',
color: 'success'
},
{
label: 'Payback Period',
value: `${response.summary?.payback_period_months || 0}mo`,
trend: 'stable',
color: 'warning'
},
{
label: 'Confidence',
value: `${((response.metadata?.confidence_score || 0) * 100).toFixed(0)}%`,
trend: 'up',
color: 'info'
}
];
}
}
// Export singleton instance
export const responseAdapter = new ResponseAdapter();