Files
trading_bot_v3/lib/ai-analysis.ts
mindesbunister 71e1a64b5d fix: correct entry prices and position sizing in trading system
- Fixed automation service to use real SOL price (~89) instead of hardcoded 00
- Updated position size calculation to properly convert USD investment to token amount
- Enhanced trade display to show separate entry/exit prices with price difference
- Added data quality warnings for trades with missing exit data
- Updated API to use current SOL price (189.50) and improved trade result determination
- Added detection and warnings for old trades with incorrect price data

Resolves issue where trades showed 9-100 entry prices instead of real SOL price of 89
and position sizes of 2.04 SOL instead of correct ~0.53 SOL for 00 investment
2025-07-21 09:26:48 +02:00

764 lines
26 KiB
TypeScript

import OpenAI from 'openai'
import fs from 'fs/promises'
import path from 'path'
import { enhancedScreenshotService, ScreenshotConfig } from './enhanced-screenshot'
import { TradingViewCredentials } from './tradingview-automation'
import { progressTracker } from './progress-tracker'
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
})
export interface AnalysisResult {
summary: string
marketSentiment: 'BULLISH' | 'BEARISH' | 'NEUTRAL'
keyLevels: {
support: number[]
resistance: number[]
}
recommendation: 'BUY' | 'SELL' | 'HOLD'
confidence: number // 0-100
reasoning: string
// Enhanced trading analysis (optional)
entry?: {
price: number
buffer?: string
rationale: string
}
stopLoss?: {
price: number
rationale: string
}
takeProfits?: {
tp1?: {
price: number
description: string
rsiExpectation?: string
obvExpectation?: string
}
tp2?: {
price: number
description: string
rsiExpectation?: string
obvExpectation?: string
}
}
riskToReward?: string
confirmationTrigger?: string
indicatorAnalysis?: {
rsi?: string
vwap?: string
obv?: string
macd?: string
crossLayoutConsensus?: string
}
layoutComparison?: {
aiLayout?: string
diyLayout?: string
consensus?: string
divergences?: string
}
timeframeRisk?: {
assessment?: string
positionSize?: string
leverageRecommendation?: string
}
alternatives?: {
tigherStopOption?: string
scaledEntry?: string
invalidationScenario?: string
}
}
export class AIAnalysisService {
async analyzeScreenshot(filenameOrPath: string): Promise<AnalysisResult | null> {
try {
let imagePath: string
// Check if it's already a full path or just a filename
if (path.isAbsolute(filenameOrPath)) {
// It's already a full path
imagePath = filenameOrPath
} else {
// It's just a filename, construct the full path
const screenshotsDir = path.join(process.cwd(), 'screenshots')
imagePath = path.join(screenshotsDir, filenameOrPath)
}
// Read image file
const imageBuffer = await fs.readFile(imagePath)
const base64Image = imageBuffer.toString('base64')
const prompt = `You are a professional trading assistant with expertise in technical analysis. You provide precise, actionable trading insights based on established technical analysis principles.
**TECHNICAL ANALYSIS FUNDAMENTALS:**
Before analyzing, understand these core indicator principles:
**RSI (Relative Strength Index):**
- Measures momentum on 0-100 scale
- OVERBOUGHT: Above 70 (potential sell signal)
- OVERSOLD: Below 30 (potential buy signal)
- NEUTRAL: 30-70 range
- ⚠️ CRITICAL: Read visual line position, not numerical value when they conflict
**MACD (Moving Average Convergence Divergence):**
- BULLISH CROSSOVER: MACD line crosses ABOVE signal line
- BEARISH CROSSOVER: MACD line crosses BELOW signal line
- HISTOGRAM: Green bars = bullish momentum, Red bars = bearish momentum
- ZERO LINE: Above = bullish trend, Below = bearish trend
**EMAs (Exponential Moving Averages):**
- EMA 9 (Yellow): Short-term trend
- EMA 20 (Orange): Medium-term trend
- EMA 50 (Blue): Intermediate trend
- EMA 200 (Red): Long-term trend
- BULLISH STACK: 9 > 20 > 50 > 200
- BEARISH STACK: 9 < 20 < 50 < 200
**Stochastic RSI:**
- OVERBOUGHT: Above 80
- OVERSOLD: Below 20
- BULLISH SIGNAL: %K crosses above %D in oversold territory
- BEARISH SIGNAL: %K crosses below %D in overbought territory
**VWAP (Volume Weighted Average Price):**
- Above VWAP = Bullish sentiment
- Below VWAP = Bearish sentiment
- RECLAIM: Price moves back above VWAP (bullish)
- REJECTION: Price fails at VWAP (bearish)
**OBV (On-Balance Volume):**
- Rising OBV = Volume supporting upward price movement
- Falling OBV = Volume supporting downward price movement
- DIVERGENCE: OBV direction differs from price (warning signal)
**LAYOUT IDENTIFICATION:**
**LAYOUT IDENTIFICATION:**
**AI Layout (RSI + MACD + EMAs):**
- TOP: RSI indicator (14-period momentum oscillator)
- MIDDLE: EMAs (9,20,50,200) + ATR Bands + SVP
- BOTTOM: MACD indicator with histogram
- Focus: Momentum + Trend Analysis
**DIY Layout (Stochastic RSI + VWAP + OBV):**
- TOP: Stochastic RSI (more sensitive momentum)
- MIDDLE: VWAP + Smart Money Concepts
- BOTTOM: OBV (volume flow analysis)
- Focus: Volume + Institutional Flow Analysis
**TECHNICAL ANALYSIS PROCESS:**
1. **MOMENTUM ANALYSIS:**
- AI Layout: Check RSI overbought/oversold conditions
- DIY Layout: Check Stochastic RSI %K/%D crossovers
- Look for momentum divergences with price
2. **TREND ANALYSIS:**
- AI Layout: EMA stack order and price position
- DIY Layout: VWAP position and smart money zones
- Identify trend direction and strength
3. **VOLUME CONFIRMATION:**
- AI Layout: Use MACD histogram for momentum confirmation
- DIY Layout: Use OBV for volume flow confirmation
- Volume should confirm price movements
4. **ENTRY/EXIT LEVELS:**
- Use confluence of multiple indicators
- Respect key technical levels (support/resistance)
- Consider risk/reward ratios
**TRADING SIGNALS:**
**BULLISH SIGNALS:**
- RSI oversold + MACD bullish crossover (AI Layout)
- Stoch RSI oversold crossover + VWAP reclaim (DIY Layout)
- Price above key EMAs in bullish stack
- OBV rising with price (volume confirmation)
**BEARISH SIGNALS:**
- RSI overbought + MACD bearish crossover (AI Layout)
- Stoch RSI overbought crossover + VWAP rejection (DIY Layout)
- Price below key EMAs in bearish stack
- OBV falling with price (volume confirmation)
**TIMEFRAME RISK ASSESSMENT:**
- **1m-15m**: High risk, 10x+ leverage, tight stops, scalping setups
- **1H-4H**: Medium risk, 3-5x leverage, moderate stops, swing setups
- **1D+**: Low risk, 1-2x leverage, wide stops, position setups
**ANALYSIS REQUIREMENTS:**
1. **IDENTIFY LAYOUT TYPE**: AI Layout (RSI/MACD/EMAs) or DIY Layout (Stoch RSI/VWAP/OBV)
2. **MOMENTUM ASSESSMENT**:
- Check primary momentum indicator (RSI or Stochastic RSI)
- Look for overbought/oversold conditions
- Identify momentum divergences
3. **TREND CONFIRMATION**:
- EMA alignment and price position (AI Layout)
- VWAP position and smart money zones (DIY Layout)
- Determine trend direction and strength
4. **VOLUME ANALYSIS**:
- MACD histogram momentum (AI Layout)
- OBV volume flow confirmation (DIY Layout)
- Volume should confirm price movements
5. **PRECISE TRADING LEVELS**:
- **ENTRY**: Exact price with ±buffer and technical rationale
- **STOP LOSS**: Exact level with clear reasoning
- **TAKE PROFITS**: TP1 (structure) and TP2 (extension) with indicator expectations
- **RISK/REWARD**: Calculate R:R ratio
6. **CONFIRMATION TRIGGERS**: Specific signals to wait for before entry
**ANALYZE THE CHART AND PROVIDE:**
- Current price action and trend direction
- Key support and resistance levels visible on the chart
- Technical indicator readings based on layout type
- Chart patterns or formations
- Market structure elements
Provide your analysis in this exact JSON format:
{
"layoutDetected": "AI Layout|DIY Layout",
"summary": "Comprehensive technical analysis with layout-specific indicator interpretation",
"marketSentiment": "BULLISH|BEARISH|NEUTRAL",
"keyLevels": {
"support": [visible support price levels as numbers],
"resistance": [visible resistance price levels as numbers]
},
"recommendation": "BUY|SELL|HOLD",
"confidence": 85,
"reasoning": "Technical analysis reasoning based on established TA principles and indicator confluence",
"momentumAnalysis": {
"primary": "RSI OVERBOUGHT/OVERSOLD/NEUTRAL (AI Layout) or Stoch RSI status (DIY Layout)",
"divergence": "Any momentum divergence with price action",
"strength": "Momentum strength assessment"
},
"trendAnalysis": {
"direction": "BULLISH|BEARISH|NEUTRAL",
"emaAlignment": "EMA stack order and price position (AI Layout)",
"vwapPosition": "VWAP relationship to price (DIY Layout)",
"strength": "Trend strength assessment"
},
"volumeAnalysis": {
"macdHistogram": "MACD momentum confirmation (AI Layout)",
"obvFlow": "OBV volume flow analysis (DIY Layout)",
"confirmation": "Volume confirming or diverging from price"
},
"entry": {
"price": 150.50,
"buffer": "±0.25",
"rationale": "Specific technical reasoning based on indicator confluence"
},
"stopLoss": {
"price": 148.00,
"rationale": "Exact reasoning based on technical levels"
},
"takeProfits": {
"tp1": {
"price": 152.00,
"description": "First target based on structure",
"indicatorExpectation": "Expected indicator behavior at TP1"
},
"tp2": {
"price": 154.00,
"description": "Extended target for momentum continuation",
"indicatorExpectation": "Expected indicator behavior at TP2"
}
},
"riskToReward": "1:2.5",
"confirmationTrigger": "Specific signal to wait for before entry",
"timeframeRisk": {
"assessment": "Risk level based on timeframe",
"positionSize": "Position sizing recommendation",
"leverageRecommendation": "Leverage suggestion based on timeframe"
},
"alternatives": {
"tighterStop": "Alternative setup with tighter stop if needed",
"scaledEntry": "Scaled entry approach if available",
"invalidation": "What would invalidate this setup"
}
}
Return only the JSON object with your technical analysis.`
const response = await openai.chat.completions.create({
model: "gpt-4o-mini", // Cost-effective vision model
messages: [
{
role: "user",
content: [
{ type: "text", text: prompt },
{
type: "image_url",
image_url: {
url: `data:image/png;base64,${base64Image}`,
detail: "low" // Reduce token usage
}
}
]
}
],
max_tokens: 1024,
temperature: 0.1
})
const content = response.choices[0]?.message?.content
if (!content) return null
console.log('AI response content:', content)
// Extract JSON from response
const match = content.match(/\{[\s\S]*\}/)
if (!match) {
console.error('No JSON found in response. Full content:', content)
return null
}
const json = match[0]
console.log('Raw JSON from AI:', json)
let result
try {
result = JSON.parse(json)
console.log('Parsed result:', result)
} catch (parseError) {
console.error('Failed to parse JSON:', parseError)
console.error('Raw JSON that failed:', json)
return null
}
// Sanitize the result to ensure no nested objects cause React issues
const sanitizedResult = {
summary: typeof result.summary === 'string' ? result.summary : String(result.summary || ''),
marketSentiment: result.marketSentiment || 'NEUTRAL',
keyLevels: {
support: Array.isArray(result.keyLevels?.support) ? result.keyLevels.support : [],
resistance: Array.isArray(result.keyLevels?.resistance) ? result.keyLevels.resistance : []
},
recommendation: result.recommendation || 'HOLD',
confidence: typeof result.confidence === 'number' ? result.confidence : 0,
reasoning: typeof result.reasoning === 'string' ? result.reasoning : String(result.reasoning || 'Basic technical analysis'),
...(result.entry && { entry: result.entry }),
...(result.stopLoss && { stopLoss: result.stopLoss }),
...(result.takeProfits && { takeProfits: result.takeProfits }),
...(result.riskToReward && { riskToReward: String(result.riskToReward) }),
...(result.confirmationTrigger && { confirmationTrigger: String(result.confirmationTrigger) }),
...(result.indicatorAnalysis && { indicatorAnalysis: result.indicatorAnalysis })
}
// Optionally: validate result structure here
return sanitizedResult as AnalysisResult
} catch (e) {
console.error('AI analysis error:', e)
return null
}
}
async analyzeMultipleScreenshots(filenamesOrPaths: string[]): Promise<AnalysisResult | null> {
try {
// Read all image files and convert to base64
const images = await Promise.all(
filenamesOrPaths.map(async (filenameOrPath) => {
let imagePath: string
// Check if it's already a full path or just a filename
if (path.isAbsolute(filenameOrPath)) {
// It's already a full path
imagePath = filenameOrPath
} else {
// It's just a filename, construct the full path
const screenshotsDir = path.join(process.cwd(), 'screenshots')
imagePath = path.join(screenshotsDir, filenameOrPath)
}
const imageBuffer = await fs.readFile(imagePath)
const base64Image = imageBuffer.toString('base64')
return {
type: "image_url" as const,
image_url: {
url: `data:image/png;base64,${base64Image}`,
detail: "high" as const
}
}
})
)
const layoutInfo = filenamesOrPaths.map(f => {
const filename = path.basename(f) // Extract filename from path
if (filename.includes('_ai_')) return 'AI Layout'
if (f.includes('_diy_') || f.includes('_Diy module_')) return 'DIY Module Layout'
return 'Unknown Layout'
}).join(' and ')
const prompt = `You are a professional trading assistant with expertise in technical analysis. You provide precise, actionable trading insights based on established technical analysis principles.
I'm providing you with ${filenamesOrPaths.length} TradingView chart screenshots from different layouts: ${layoutInfo}.
**TECHNICAL ANALYSIS FUNDAMENTALS:**
**RSI (Relative Strength Index):**
- OVERBOUGHT: Above 70 (potential sell signal)
- OVERSOLD: Below 30 (potential buy signal)
- NEUTRAL: 30-70 range
- ⚠️ CRITICAL: Read visual line position, not numerical value when they conflict
**MACD (Moving Average Convergence Divergence):**
- BULLISH CROSSOVER: MACD line crosses ABOVE signal line
- BEARISH CROSSOVER: MACD line crosses BELOW signal line
- HISTOGRAM: Green bars = bullish momentum, Red bars = bearish momentum
- ZERO LINE: Above = bullish trend, Below = bearish trend
**EMAs (Exponential Moving Averages):**
- EMA 9 (Yellow): Short-term trend
- EMA 20 (Orange): Medium-term trend
- EMA 50 (Blue): Intermediate trend
- EMA 200 (Red): Long-term trend
- BULLISH STACK: 9 > 20 > 50 > 200
- BEARISH STACK: 9 < 20 < 50 < 200
**Stochastic RSI:**
- OVERBOUGHT: Above 80
- OVERSOLD: Below 20
- BULLISH SIGNAL: %K crosses above %D in oversold territory
- BEARISH SIGNAL: %K crosses below %D in overbought territory
**VWAP (Volume Weighted Average Price):**
- Above VWAP = Bullish sentiment
- Below VWAP = Bearish sentiment
- RECLAIM: Price moves back above VWAP (bullish)
- REJECTION: Price fails at VWAP (bearish)
**OBV (On-Balance Volume):**
- Rising OBV = Volume supporting upward price movement
- Falling OBV = Volume supporting downward price movement
- DIVERGENCE: OBV direction differs from price (warning signal)
**MULTI-LAYOUT ANALYSIS:**
**MULTI-LAYOUT ANALYSIS:**
**AI Layout (RSI + MACD + EMAs):**
- Focus: Momentum + Trend Analysis
- Primary indicators: RSI, MACD, EMAs
- Use for: Trend direction, momentum signals, entry/exit timing
**DIY Layout (Stochastic RSI + VWAP + OBV):**
- Focus: Volume + Institutional Flow Analysis
- Primary indicators: Stochastic RSI, VWAP, OBV
- Use for: Volume confirmation, institutional sentiment, fair value
**ANALYSIS PROCESS:**
1. **Identify Layout Types**: Determine which layouts are provided
2. **Momentum Assessment**: Check primary momentum indicators
3. **Trend Confirmation**: Analyze trend direction and strength
4. **Volume Analysis**: Confirm with volume indicators
5. **Cross-Layout Consensus**: Compare insights for confirmation
6. **Risk Assessment**: Adjust for timeframe and volatility
**TIMEFRAME RISK ASSESSMENT:**
- **1m-15m**: High risk, 10x+ leverage, tight stops, scalping setups
- **1H-4H**: Medium risk, 3-5x leverage, moderate stops, swing setups
- **1D+**: Low risk, 1-2x leverage, wide stops, position setups
**PROVIDE PRECISE TRADING LEVELS:**
- **ENTRY**: Exact price with ±buffer and technical rationale
- **STOP LOSS**: Exact level with clear reasoning
- **TAKE PROFITS**: TP1 (structure) and TP2 (extension) with indicator expectations
- **RISK/REWARD**: Calculate R:R ratio
- **CONFIRMATION TRIGGERS**: Specific signals to wait for
**ANALYZE THE CHARTS AND PROVIDE:**
- Current price action and trend direction
- Key support and resistance levels
- Technical indicator readings based on layout types
- Cross-layout consensus and divergences
- Market structure elements
**Response Format** (return only valid JSON):
{
"summary": "Comprehensive multi-layout analysis with cross-layout consensus and TA fundamentals",
"marketSentiment": "BULLISH|BEARISH|NEUTRAL",
"keyLevels": {
"support": [array of support levels from all charts],
"resistance": [array of resistance levels from all charts]
},
"recommendation": "BUY|SELL|HOLD",
"confidence": 85,
"reasoning": "Multi-layout technical analysis with cross-layout confirmation",
"layoutsAnalyzed": ["AI Layout", "DIY Layout"],
"momentumAnalysis": {
"aiLayout": "RSI analysis from AI layout",
"diyLayout": "Stochastic RSI analysis from DIY layout",
"consensus": "Momentum consensus between layouts",
"divergence": "Any momentum divergences detected"
},
"trendAnalysis": {
"aiLayout": "EMA alignment and trend from AI layout",
"diyLayout": "VWAP position and smart money from DIY layout",
"consensus": "Trend consensus between layouts",
"direction": "BULLISH|BEARISH|NEUTRAL"
},
"volumeAnalysis": {
"aiLayout": "MACD histogram momentum from AI layout",
"diyLayout": "OBV volume flow from DIY layout",
"consensus": "Volume consensus between layouts",
"confirmation": "Volume confirming or diverging from price"
},
"entry": {
"price": 150.50,
"buffer": "±0.25",
"rationale": "Cross-layout confluence supporting entry level"
},
"stopLoss": {
"price": 148.00,
"rationale": "Technical level confirmed by multiple layouts"
},
"takeProfits": {
"tp1": {
"price": 152.00,
"description": "First target with multi-layout support",
"indicatorExpectation": "Expected behavior across both layouts"
},
"tp2": {
"price": 154.00,
"description": "Extended target for momentum continuation",
"indicatorExpectation": "Extended momentum expectations"
}
},
"riskToReward": "1:2.5",
"confirmationTrigger": "Specific cross-layout signal to wait for",
"layoutComparison": {
"aiLayoutEdge": "Specific advantage of AI layout analysis",
"diyLayoutEdge": "Specific advantage of DIY layout analysis",
"consensus": "Areas where both layouts strongly agree",
"divergences": "Areas where layouts disagree and resolution"
},
"timeframeRisk": {
"assessment": "Risk level based on timeframe",
"positionSize": "Position sizing recommendation",
"leverageRecommendation": "Leverage suggestion for timeframe"
},
"alternatives": {
"tighterStop": "Alternative with tighter stop if needed",
"scaledEntry": "Scaled entry approach if available",
"invalidation": "What would invalidate this setup"
}
}
Analyze all provided screenshots comprehensively and return only the JSON response.`
const messages = [
{
role: "user" as const,
content: [
{ type: "text" as const, text: prompt },
...images
]
}
]
console.log(`🤖 Sending ${filenamesOrPaths.length} screenshots to OpenAI for multi-layout analysis...`)
const response = await openai.chat.completions.create({
model: "gpt-4o-mini", // Cost-effective model with vision capabilities
messages,
max_tokens: 2000,
temperature: 0.1
})
const content = response.choices[0]?.message?.content
if (!content) {
throw new Error('No response from OpenAI')
}
console.log('🔍 Raw OpenAI response:', content.substring(0, 200) + '...')
// Parse JSON response
const jsonMatch = content.match(/\{[\s\S]*\}/)
if (!jsonMatch) {
throw new Error('No JSON found in response')
}
const analysis = JSON.parse(jsonMatch[0])
console.log('✅ Multi-layout analysis parsed successfully')
return analysis as AnalysisResult
} catch (error: any) {
console.error('❌ Multi-screenshot AI analysis failed:', error.message)
console.error('Full error:', error)
return null
}
}
async captureAndAnalyze(
symbol: string,
timeframe: string,
credentials: TradingViewCredentials
): Promise<AnalysisResult | null> {
try {
console.log(`Starting automated capture and analysis for ${symbol} ${timeframe}`)
// Capture screenshot using automation
const screenshot = await enhancedScreenshotService.captureQuick(symbol, timeframe, credentials)
if (!screenshot) {
throw new Error('Failed to capture screenshot')
}
console.log(`Screenshot captured: ${screenshot}`)
// Analyze the captured screenshot
const analysis = await this.analyzeScreenshot(screenshot)
if (!analysis) {
throw new Error('Failed to analyze screenshot')
}
console.log(`Analysis completed for ${symbol} ${timeframe}`)
return analysis
} catch (error) {
console.error('Automated capture and analysis failed:', error)
return null
}
}
async captureAndAnalyzeMultiple(
symbols: string[],
timeframes: string[],
credentials: TradingViewCredentials
): Promise<Array<{ symbol: string; timeframe: string; analysis: AnalysisResult | null }>> {
const results: Array<{ symbol: string; timeframe: string; analysis: AnalysisResult | null }> = []
for (const symbol of symbols) {
for (const timeframe of timeframes) {
try {
console.log(`Processing ${symbol} ${timeframe}...`)
const analysis = await this.captureAndAnalyze(symbol, timeframe, credentials)
results.push({
symbol,
timeframe,
analysis
})
// Small delay between captures to avoid overwhelming the system
await new Promise(resolve => setTimeout(resolve, 2000))
} catch (error) {
console.error(`Failed to process ${symbol} ${timeframe}:`, error)
results.push({
symbol,
timeframe,
analysis: null
})
}
}
}
return results
}
async captureAndAnalyzeWithConfig(config: ScreenshotConfig): Promise<{
screenshots: string[]
analysis: AnalysisResult | null
}> {
const { sessionId } = config
try {
console.log(`Starting automated capture with config for ${config.symbol} ${config.timeframe}`)
// Capture screenshots using enhanced service (this will handle its own progress)
const screenshots = await enhancedScreenshotService.captureWithLogin(config)
if (screenshots.length === 0) {
throw new Error('No screenshots captured')
}
console.log(`${screenshots.length} screenshot(s) captured`)
// Add AI analysis step to progress if sessionId exists
if (sessionId) {
progressTracker.updateStep(sessionId, 'analysis', 'active', 'Running AI analysis on screenshots...')
}
let analysis: AnalysisResult | null = null
if (screenshots.length === 1) {
// Single screenshot analysis
analysis = await this.analyzeScreenshot(screenshots[0])
} else {
// Multiple screenshots analysis
analysis = await this.analyzeMultipleScreenshots(screenshots)
}
if (!analysis) {
if (sessionId) {
progressTracker.updateStep(sessionId, 'analysis', 'error', 'AI analysis failed to generate results')
}
throw new Error('Failed to analyze screenshots')
}
console.log(`Analysis completed for ${config.symbol} ${config.timeframe}`)
if (sessionId) {
progressTracker.updateStep(sessionId, 'analysis', 'completed', 'AI analysis completed successfully!')
// Mark session as complete
setTimeout(() => progressTracker.deleteSession(sessionId), 1000)
}
// Trigger post-analysis cleanup in development mode
if (process.env.NODE_ENV === 'development') {
try {
// Dynamic import to avoid circular dependencies
const aggressiveCleanupModule = await import('./aggressive-cleanup')
const aggressiveCleanup = aggressiveCleanupModule.default
// Run cleanup in background, don't block the response
aggressiveCleanup.runPostAnalysisCleanup().catch(console.error)
} catch (cleanupError) {
console.error('Error triggering post-analysis cleanup:', cleanupError)
}
}
return {
screenshots,
analysis
}
} catch (error) {
console.error('Automated capture and analysis with config failed:', error)
if (sessionId) {
// Find the active step and mark it as error
const progress = progressTracker.getProgress(sessionId)
if (progress) {
const activeStep = progress.steps.find(step => step.status === 'active')
if (activeStep) {
progressTracker.updateStep(sessionId, activeStep.id, 'error', error instanceof Error ? error.message : 'Unknown error')
}
}
// Clean up session
setTimeout(() => progressTracker.deleteSession(sessionId), 5000)
}
return {
screenshots: [],
analysis: null
}
}
}
}
export const aiAnalysisService = new AIAnalysisService()