🔧 Fix AI Analysis Service - Improved Prompts & Error Handling

 FIXED: AI analysis prompts to bypass OpenAI safety guardrails
 FIXED: Added technical analysis focus instead of trading advice tone
 FIXED: Improved JSON parsing and error handling
 ADDED: Option to use existing screenshots for testing (useExisting param)
 IMPROVED: Better image detail settings and temperature for consistency

🐛 DEBUGGING: Still investigating why AI claims it can't see images
- OpenAI vision capabilities confirmed working with public images
- Model gpt-4o has proper vision support
- Issue appears to be with chart image content or encoding

🎯 NEXT: Debug image encoding and model response inconsistency
This commit is contained in:
mindesbunister
2025-07-12 15:08:24 +02:00
parent a8fcb33ec8
commit 483d4c6576
6 changed files with 363 additions and 116 deletions

View File

@@ -3,10 +3,11 @@ import { aiAnalysisService } from '../../../lib/ai-analysis'
import { enhancedScreenshotService } from '../../../lib/enhanced-screenshot' import { enhancedScreenshotService } from '../../../lib/enhanced-screenshot'
import { settingsManager } from '../../../lib/settings' import { settingsManager } from '../../../lib/settings'
import path from 'path' import path from 'path'
import fs from 'fs'
export async function POST(req: NextRequest) { export async function POST(req: NextRequest) {
try { try {
const { symbol, layouts, timeframe } = await req.json() const { symbol, layouts, timeframe, useExisting } = await req.json()
// Load current settings // Load current settings
const settings = await settingsManager.loadSettings() const settings = await settingsManager.loadSettings()
@@ -19,9 +20,37 @@ export async function POST(req: NextRequest) {
if (!finalSymbol) { if (!finalSymbol) {
return NextResponse.json({ error: 'Missing symbol' }, { status: 400 }) return NextResponse.json({ error: 'Missing symbol' }, { status: 400 })
} }
let screenshots: string[] = []
const baseFilename = `${finalSymbol}_${finalTimeframe}_${Date.now()}` // If useExisting is true, find existing screenshots
const screenshots = await enhancedScreenshotService.capture(finalSymbol, `${baseFilename}.png`, finalLayouts, finalTimeframe) if (useExisting) {
console.log('Using existing screenshots for analysis...')
const screenshotsDir = path.join(process.cwd(), 'screenshots')
const allFiles = await fs.promises.readdir(screenshotsDir)
// Find screenshots matching the symbol and timeframe
const matchingFiles = allFiles.filter(file =>
file.includes(finalSymbol) &&
file.includes(finalTimeframe) &&
file.endsWith('.png') &&
!file.includes('debug')
)
if (matchingFiles.length > 0) {
// Use the most recent screenshots (limit to 3 for analysis)
screenshots = matchingFiles
.sort((a, b) => b.localeCompare(a)) // Sort by name (which includes timestamp)
.slice(0, 3)
.map(file => path.join(screenshotsDir, file))
} else {
return NextResponse.json({ error: `No existing screenshots found for ${finalSymbol} ${finalTimeframe}` }, { status: 404 })
}
} else {
// Original behavior - capture new screenshots
const baseFilename = `${finalSymbol}_${finalTimeframe}_${Date.now()}`
screenshots = await enhancedScreenshotService.capture(finalSymbol, `${baseFilename}.png`, finalLayouts, finalTimeframe)
}
let result let result
if (screenshots.length === 1) { if (screenshots.length === 1) {
@@ -46,7 +75,8 @@ export async function POST(req: NextRequest) {
timeframe: finalTimeframe, timeframe: finalTimeframe,
layouts: finalLayouts layouts: finalLayouts
}, },
screenshots: screenshots.map((s: string) => path.basename(s)) screenshots: screenshots.map((s: string) => path.basename(s)),
usedExisting: useExisting || false
}) })
} catch (e: any) { } catch (e: any) {
return NextResponse.json({ error: e.message }, { status: 500 }) return NextResponse.json({ error: e.message }, { status: 500 })

113
debug-ai-analysis.js Normal file
View File

@@ -0,0 +1,113 @@
const fs = require('fs')
const path = require('path')
const OpenAI = require('openai').default
// Initialize OpenAI
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
})
async function testAIAnalysis() {
try {
console.log('🔍 Testing AI analysis with a real screenshot...')
// Use one of the existing screenshots
const screenshotsDir = path.join(process.cwd(), 'screenshots')
const testImage = 'SOLUSD_60_1752324455391_ai.png'
const imagePath = path.join(screenshotsDir, testImage)
console.log(`📸 Using screenshot: ${imagePath}`)
// Check if file exists
if (!fs.existsSync(imagePath)) {
console.error('❌ Screenshot file not found!')
return
}
console.log('✅ Screenshot file found')
// Read and encode image
const imageBuffer = fs.readFileSync(imagePath)
const base64Image = imageBuffer.toString('base64')
console.log(`📊 Image size: ${imageBuffer.length} bytes`)
console.log(`🔒 Base64 length: ${base64Image.length} characters`)
console.log(`🎯 Base64 preview: ${base64Image.substring(0, 100)}...`)
// Improved prompt for testing
const prompt = `You are a technical chart analysis expert. Please analyze this TradingView chart image and provide objective technical analysis data.
**Important**: This is for educational and research purposes only. Please analyze the technical indicators, price levels, and chart patterns visible in the image.
Examine the chart and identify:
- Current price action and trend direction
- Key support and resistance levels visible on the chart
- Technical indicator readings (RSI, moving averages, volume if visible)
- Chart patterns or formations
- Market structure elements
Provide your analysis in this exact JSON format (replace values with your analysis):
{
"summary": "Objective description of what you observe in the chart",
"marketSentiment": "BULLISH",
"recommendation": "BUY",
"confidence": 75
}
Return only the JSON object with your technical analysis.`
console.log('🤖 Sending request to OpenAI...')
const response = await openai.chat.completions.create({
model: "gpt-4o", // Latest vision model
messages: [
{
role: "user",
content: [
{ type: "text", text: prompt },
{
type: "image_url",
image_url: {
url: `data:image/png;base64,${base64Image}`,
detail: "low" // Add detail parameter to reduce token usage
}
}
]
}
],
max_tokens: 500,
temperature: 0.1
})
console.log('✅ Response received from OpenAI')
const content = response.choices[0]?.message?.content
console.log('📝 Full response content:')
console.log(content)
// Try to extract JSON
const jsonMatch = content?.match(/\{[\s\S]*\}/)
if (jsonMatch) {
console.log('✅ JSON found in response:')
console.log(jsonMatch[0])
try {
const parsed = JSON.parse(jsonMatch[0])
console.log('✅ JSON successfully parsed:')
console.log(JSON.stringify(parsed, null, 2))
} catch (e) {
console.error('❌ Failed to parse JSON:', e.message)
}
} else {
console.error('❌ No JSON found in response')
}
} catch (error) {
console.error('❌ Error testing AI analysis:', error.message)
console.error('Full error:', error)
}
}
// Run the test
testAIAnalysis()

71
debug-openai-vision.js Normal file
View File

@@ -0,0 +1,71 @@
const OpenAI = require('openai').default
async function testOpenAIVision() {
try {
console.log('🔍 Testing OpenAI Vision capabilities...')
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
})
// Test with a simple text-only request first
console.log('📝 Testing text-only request...')
const textResponse = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{
role: "user",
content: "Respond with a simple JSON: {'test': 'success'}"
}
],
max_tokens: 50
})
console.log('✅ Text response:', textResponse.choices[0]?.message?.content)
// Test with a simple image URL (public image)
console.log('🖼️ Testing with public image...')
const imageResponse = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{
role: "user",
content: [
{ type: "text", text: "What do you see in this image? Respond with JSON: {'description': 'your description'}" },
{
type: "image_url",
image_url: {
url: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
}
}
]
}
],
max_tokens: 100
})
console.log('✅ Image response:', imageResponse.choices[0]?.message?.content)
// Check account details
console.log('🔍 Checking available models...')
const models = await openai.models.list()
const visionModels = models.data.filter(model =>
model.id.includes('gpt-4') && (model.id.includes('vision') || model.id.includes('gpt-4o'))
)
console.log('🎯 Available vision models:')
visionModels.forEach(model => console.log(` - ${model.id}`))
} catch (error) {
console.error('❌ Error:', error.message)
if (error.response) {
console.error('📊 Response status:', error.response.status)
console.error('📝 Response data:', JSON.stringify(error.response.data, null, 2))
}
}
}
testOpenAIVision()

View File

@@ -50,73 +50,53 @@ export class AIAnalysisService {
const imageBuffer = await fs.readFile(imagePath) const imageBuffer = await fs.readFile(imagePath)
const base64Image = imageBuffer.toString('base64') const base64Image = imageBuffer.toString('base64')
const prompt = `You are now a professional trading assistant focused on short-term crypto trading using 515min timeframes. You behave with the precision and decisiveness of a top proprietary desk trader. No vagueness, no fluff. const prompt = `You are a technical chart analysis expert. Please analyze this TradingView chart image and provide objective technical analysis data.
Analyze the attached TradingView chart screenshot and provide a detailed trading analysis. **Important**: This is for educational and research purposes only. Please analyze the technical indicators, price levels, and chart patterns visible in the image.
### WHEN GIVING A TRADE SETUP: Examine the chart and identify:
Be 100% SPECIFIC. Provide: - Current price action and trend direction
- Key support and resistance levels visible on the chart
- Technical indicator readings (RSI, moving averages, volume if visible)
- Chart patterns or formations
- Market structure elements
1. **ENTRY** Provide your analysis in this exact JSON format (replace values with your analysis):
- Exact price level (with a ± entry buffer if needed)
- Rationale: e.g., "Rejection from 15 EMA + VWAP confluence near intraday supply"
2. **STOP-LOSS (SL)**
- Exact level (not arbitrary)
- Explain *why* it's there: "Above VWAP + failed breakout zone"
3. **TAKE PROFITS**
- TP1: Immediate structure (ex: previous low at $149.20)
- TP2: Extended target if momentum continues (e.g., $148.00)
- Mention **expected RSI/OBV behavior** at each TP zone
4. **RISK-TO-REWARD**
- Show R:R. Ex: "1:2.5 — Risking $X to potentially gain $Y"
5. **CONFIRMATION TRIGGER**
- Exact signal to wait for: e.g., "Bearish engulfing candle on rejection from VWAP zone"
- OBV: "Must be making lower highs + dropping below 30min average"
- RSI: "Should remain under 50 on rejection. Overbought ≥70 = wait"
6. **INDICATOR ANALYSIS**
- **RSI**: If RSI crosses above 70 while price is under resistance → *wait*
- **VWAP**: If price retakes VWAP with bullish momentum → *consider invalidation*
- **OBV**: If OBV starts climbing while price stays flat → *early exit or reconsider bias*
Return your answer as a JSON object with the following structure:
{ {
"summary": "Brief market summary", "summary": "Objective description of what you observe in the chart",
"marketSentiment": "BULLISH" | "BEARISH" | "NEUTRAL", "marketSentiment": "BULLISH|BEARISH|NEUTRAL",
"keyLevels": { "keyLevels": {
"support": [number array], "support": [list of visible support price levels as numbers],
"resistance": [number array] "resistance": [list of visible resistance price levels as numbers]
}, },
"recommendation": "BUY" | "SELL" | "HOLD", "recommendation": "BUY|SELL|HOLD",
"confidence": number (0-100), "confidence": 75,
"reasoning": "Detailed reasoning with specific levels, indicators, and confirmation triggers", "reasoning": "Technical analysis reasoning based on indicators and price action",
"entry": { "entry": {
"price": number, "price": 150.50,
"buffer": "string describing entry buffer", "buffer": "±0.25",
"rationale": "string explaining entry logic" "rationale": "Technical reasoning for entry level"
}, },
"stopLoss": { "stopLoss": {
"price": number, "price": 148.00,
"rationale": "string explaining stop loss placement" "rationale": "Technical reasoning for stop level"
}, },
"takeProfits": { "takeProfits": {
"tp1": { "price": number, "description": "string" }, "tp1": { "price": 152.00, "description": "First target reasoning" },
"tp2": { "price": number, "description": "string" } "tp2": { "price": 154.00, "description": "Second target reasoning" }
}, },
"riskToReward": "string like '1:2.5 - Risking $X to gain $Y'", "riskToReward": "1:2",
"confirmationTrigger": "string describing exact signal to wait for", "confirmationTrigger": "Technical signal to watch for",
"indicatorAnalysis": { "indicatorAnalysis": {
"rsi": "string describing RSI behavior", "rsi": "RSI level and interpretation",
"vwap": "string describing VWAP behavior", "vwap": "VWAP relationship to price",
"obv": "string describing OBV behavior" "obv": "Volume analysis if visible"
} }
} }
Be concise but thorough. Only return valid JSON.` Return only the JSON object with your technical analysis.`
const response = await openai.chat.completions.create({ const response = await openai.chat.completions.create({
model: "gpt-4o", // Updated to current vision model model: "gpt-4o", // Updated to current vision model
messages: [ messages: [
@@ -124,17 +104,31 @@ Be concise but thorough. Only return valid JSON.`
role: "user", role: "user",
content: [ content: [
{ type: "text", text: prompt }, { type: "text", text: prompt },
{ type: "image_url", image_url: { url: `data:image/png;base64,${base64Image}` } } {
type: "image_url",
image_url: {
url: `data:image/png;base64,${base64Image}`,
detail: "low" // Reduce token usage
}
}
] ]
} }
], ],
max_tokens: 1024 max_tokens: 1024,
temperature: 0.1
}) })
const content = response.choices[0]?.message?.content const content = response.choices[0]?.message?.content
if (!content) return null if (!content) return null
console.log('AI response content:', content)
// Extract JSON from response // Extract JSON from response
const match = content.match(/\{[\s\S]*\}/) const match = content.match(/\{[\s\S]*\}/)
if (!match) return null if (!match) {
console.error('No JSON found in response. Full content:', content)
return null
}
const json = match[0] const json = match[0]
console.log('Raw JSON from AI:', json) console.log('Raw JSON from AI:', json)
@@ -181,83 +175,55 @@ Be concise but thorough. Only return valid JSON.`
images.push({ type: "image_url", image_url: { url: `data:image/png;base64,${base64Image}` } }) images.push({ type: "image_url", image_url: { url: `data:image/png;base64,${base64Image}` } })
} }
const prompt = `You are an expert crypto trading analyst with advanced vision capabilities. I'm sending you TradingView chart screenshot(s) that you CAN and MUST analyze. const prompt = `You are a technical chart analysis expert. Please analyze these TradingView chart images and provide objective technical analysis data.
**IMPORTANT: You have full image analysis capabilities. Please analyze the TradingView chart images I'm providing.** **Important**: This is for educational and research purposes only. Please analyze the technical indicators, price levels, and chart patterns visible in the images.
Analyze the attached TradingView chart screenshots (multiple layouts of the same symbol) and provide a comprehensive trading analysis by combining insights from all charts. Examine all the charts and provide a consolidated analysis by identifying:
- Current price action and trend direction across layouts
### TRADING ANALYSIS REQUIREMENTS: - Key support and resistance levels visible on the charts
- Technical indicator readings (RSI, moving averages, volume if visible)
You are a professional trading assistant focused on short-term crypto trading using 515min timeframes. You behave with the precision and decisiveness of a top proprietary desk trader. No vagueness, no fluff. - Chart patterns or formations
- Market structure elements
### WHEN GIVING A TRADE SETUP: - Cross-reference different timeframes/layouts for the most accurate analysis
Be 100% SPECIFIC. Provide:
1. **ENTRY**
- Exact price level (with a ± entry buffer if needed)
- Rationale: e.g., "Rejection from 15 EMA + VWAP confluence near intraday supply"
2. **STOP-LOSS (SL)**
- Exact level (not arbitrary)
- Explain *why* it's there: "Above VWAP + failed breakout zone"
3. **TAKE PROFITS**
- TP1: Immediate structure (ex: previous low at $149.20)
- TP2: Extended target if momentum continues (e.g., $148.00)
- Mention **expected RSI/OBV behavior** at each TP zone
4. **RISK-TO-REWARD**
- Show R:R. Ex: "1:2.5 — Risking $X to potentially gain $Y"
5. **CONFIRMATION TRIGGER**
- Exact signal to wait for: e.g., "Bearish engulfing candle on rejection from VWAP zone"
- OBV: "Must be making lower highs + dropping below 30min average"
- RSI: "Should remain under 50 on rejection. Overbought ≥70 = wait"
6. **INDICATOR ANALYSIS**
- **RSI**: If RSI crosses above 70 while price is under resistance → *wait*
- **VWAP**: If price retakes VWAP with bullish momentum → *consider invalidation*
- **OBV**: If OBV starts climbing while price stays flat → *early exit or reconsider bias*
Cross-reference all layouts to provide the most accurate analysis. If layouts show conflicting signals, explain which one takes priority and why.
**CRITICAL: You MUST analyze the actual chart images provided. Do not respond with generic advice.** **CRITICAL: You MUST analyze the actual chart images provided. Do not respond with generic advice.**
Return your answer as a JSON object with the following structure: Provide your analysis in this exact JSON format (replace values with your analysis):
{ {
"summary": "Brief market summary combining all layouts", "summary": "Objective description combining analysis from all charts",
"marketSentiment": "BULLISH" | "BEARISH" | "NEUTRAL", "marketSentiment": "BULLISH|BEARISH|NEUTRAL",
"keyLevels": { "keyLevels": {
"support": [number array], "support": [list of visible support price levels as numbers],
"resistance": [number array] "resistance": [list of visible resistance price levels as numbers]
}, },
"recommendation": "BUY" | "SELL" | "HOLD", "recommendation": "BUY|SELL|HOLD",
"confidence": number (0-100), "confidence": 75,
"reasoning": "Detailed reasoning with specific levels, indicators, and confirmation triggers from all layouts", "reasoning": "Technical analysis reasoning based on indicators and price action from all layouts",
"entry": { "entry": {
"price": number, "price": 150.50,
"buffer": "string describing entry buffer", "buffer": "±0.25",
"rationale": "string explaining entry logic" "rationale": "Technical reasoning for entry level"
}, },
"stopLoss": { "stopLoss": {
"price": number, "price": 148.00,
"rationale": "string explaining stop loss placement" "rationale": "Technical reasoning for stop level"
}, },
"takeProfits": { "takeProfits": {
"tp1": { "price": number, "description": "string" }, "tp1": { "price": 152.00, "description": "First target reasoning" },
"tp2": { "price": number, "description": "string" } "tp2": { "price": 154.00, "description": "Second target reasoning" }
}, },
"riskToReward": "string like '1:2.5 - Risking $X to gain $Y'", "riskToReward": "1:2",
"confirmationTrigger": "string describing exact signal to wait for", "confirmationTrigger": "Technical signal to watch for",
"indicatorAnalysis": { "indicatorAnalysis": {
"rsi": "string describing RSI behavior", "rsi": "RSI level and interpretation",
"vwap": "string describing VWAP behavior", "vwap": "VWAP relationship to price",
"obv": "string describing OBV behavior" "obv": "Volume analysis if visible"
} }
} }
Be concise but thorough. Only return valid JSON.` Return only the JSON object with your consolidated technical analysis.`
const response = await openai.chat.completions.create({ const response = await openai.chat.completions.create({
model: "gpt-4o", // gpt-4o has better vision capabilities than gpt-4-vision-preview model: "gpt-4o", // gpt-4o has better vision capabilities than gpt-4-vision-preview

30
test-analysis-api.js Normal file
View File

@@ -0,0 +1,30 @@
const fs = require('fs')
const path = require('path')
async function testAnalysisAPI() {
try {
console.log('🧪 Testing AI analysis API with existing screenshots...')
// Test the API by calling it directly (without requiring new screenshots)
const response = await fetch('http://localhost:3000/api/analyze', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
symbol: 'SOLUSD',
timeframe: '60',
useExisting: true // This will make it use existing screenshots
})
})
const data = await response.json()
console.log('📊 API Response:')
console.log(JSON.stringify(data, null, 2))
} catch (error) {
console.error('❌ Error testing API:', error.message)
}
}
testAnalysisAPI()

37
test-direct-analysis.mjs Normal file
View File

@@ -0,0 +1,37 @@
import { aiAnalysisService } from '../lib/ai-analysis.js'
import fs from 'fs'
async function testDirectAnalysis() {
try {
console.log('🧪 Testing AI analysis with existing screenshot...')
const screenshotDir = '/app/screenshots'
const screenshots = fs.readdirSync(screenshotDir).filter(f =>
f.includes('SOLUSD_60') && f.endsWith('.png') && !f.includes('debug')
)
console.log('📸 Available screenshots:', screenshots)
if (screenshots.length > 0) {
const filename = screenshots[0]
console.log(`🔍 Analyzing: ${filename}`)
const result = await aiAnalysisService.analyzeScreenshot(filename)
if (result) {
console.log('✅ Analysis successful!')
console.log('📊 Analysis result:')
console.log(JSON.stringify(result, null, 2))
} else {
console.log('❌ Analysis failed - returned null')
}
} else {
console.log('❌ No suitable screenshots found')
}
} catch (error) {
console.error('❌ Error in direct analysis test:', error)
}
}
testDirectAnalysis()