# src/services/claude_service.py import base64 from anthropic import Anthropic import streamlit as st from config.settings import ANTHROPIC_API_KEY, CLAUDE_MODEL class ClaudeService: def __init__(self): """Initialize Claude service with API key from HuggingFace secrets""" if not ANTHROPIC_API_KEY: raise ValueError("Anthropic API key not found in HuggingFace secrets. Please ensure ANTHROPIC_API_KEY is set in your space's secrets.") self.client = Anthropic(api_key=ANTHROPIC_API_KEY) self.model = CLAUDE_MODEL def analyze_image(self, image_data, prompt): """Analyze image using Claude Vision""" try: encoded_image = base64.b64encode(image_data).decode('utf-8') message = self.client.messages.create( model=self.model, max_tokens=1000, messages=[{ "role": "user", "content": [ { "type": "text", "text": prompt }, { "type": "image", "source": { "type": "base64", "media_type": "image/jpeg", "data": encoded_image } } ] }] ) return message.content[0].text except Exception as e: st.error(f"Error in image analysis: {str(e)}") return None def analyze_multiple_images(self, image_data_list, prompt): """Analyze multiple images together using Claude Vision""" try: # Create content list with prompt and all images content = [{"type": "text", "text": prompt}] # Add all images to the content for idx, image_data in enumerate(image_data_list): encoded_image = base64.b64encode(image_data).decode('utf-8') content.append({ "type": "image", "source": { "type": "base64", "media_type": "image/jpeg", "data": encoded_image } }) message = self.client.messages.create( model=self.model, max_tokens=1500, # Increased for multiple image analysis messages=[{ "role": "user", "content": content }] ) return message.content[0].text except Exception as e: st.error(f"Error in multiple image analysis: {str(e)}") return None def detect_chart_type(self, image_data): """Detect chart type from image""" prompt = """What type of financial chart is this? Choose from: Candlestick, Line, OHLC, Area, or Other. Just respond with one word.""" result = self.analyze_image(image_data, prompt) return result.strip() if result else "Other" def generate_diagram(self, prompt): """Generate an SVG diagram using Claude""" try: message = self.client.messages.create( model=self.model, max_tokens=1000, messages=[{ "role": "user", "content": f"""Please create an SVG diagram based on this request: {prompt} Make the diagram clean, professional, and educational. Use standard SVG elements and ensure the viewBox is properly set. Keep colors accessible and include clear labels.""" }] ) response = message.content[0].text # Extract SVG content if present if '' in response: svg_start = response.find('') + 6 return response[svg_start:svg_end] return None except Exception as e: st.error(f"Error generating diagram: {str(e)}") return None def generate_educational_content(self, prompt): """Generate educational content using Claude""" try: message = self.client.messages.create( model=self.model, max_tokens=1500, messages=[{ "role": "user", "content": prompt }] ) return message.content[0].text except Exception as e: st.error(f"Error generating educational content: {str(e)}") return None def get_educational_content(self, concept): """Get educational content about trading concepts""" try: message = self.client.messages.create( model=self.model, max_tokens=1000, messages=[{ "role": "user", "content": f"""Please explain the trading concept '{concept}' in a clear, educational way. Include: 1. Basic Definition 2. How it Works 3. Key Characteristics 4. When to Look for It 5. Trading Implications 6. Common Mistakes to Avoid 7. Real-World Example""" }] ) return message.content[0].text except Exception as e: st.error(f"Error getting educational content: {str(e)}") return None def continue_analysis(self, question, previous_analysis, image_data=None): """Continue analysis based on follow-up question""" try: content = [ { "type": "text", "text": f"""Previous analysis: {previous_analysis} User's follow-up question: {question} Please provide a detailed answer to the follow-up question, maintaining the context of the previous analysis.""" } ] if image_data: encoded_image = base64.b64encode(image_data).decode('utf-8') content.append({ "type": "image", "source": { "type": "base64", "media_type": "image/jpeg", "data": encoded_image } }) message = self.client.messages.create( model=self.model, max_tokens=1000, messages=[{"role": "user", "content": content}] ) return message.content[0].text except Exception as e: st.error(f"Error in follow-up analysis: {str(e)}") return None