File size: 11,261 Bytes
ce180e5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
"""
CFA AI Agent - LangChain Agent Setup
This module sets up the LangChain agent with Finance-Llama-8B model and financial tools.
"""

import os
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
from langchain_community.llms import HuggingFacePipeline
from langchain.agents import initialize_agent, AgentType
from langchain.memory import ConversationBufferMemory
from langchain.schema import SystemMessage
from langchain.prompts import MessagesPlaceholder
from typing import List, Any, Optional

# Import our custom tools
from tools.finance_tools import (
    calculate_dcf,
    calculate_sharpe_ratio,
    compare_pe_ratios,
    calculate_beta,
    calculate_wacc,
    financial_ratios_analysis
)
from tools.data_fetcher import (
    get_stock_price,
    get_historical_data,
    get_company_info,
    get_financial_statements,
    get_market_indices,
    compare_stocks
)


class CFAAgent:
    """
    CFA AI Agent that combines Finance-Llama-8B model with financial analysis tools.
    """

    def __init__(self, model_name: str = "tarun7r/Finance-Llama-8B"):
        """
        Initialize the CFA Agent with model and tools.

        Args:
            model_name: Hugging Face model name for financial analysis
        """
        self.model_name = model_name
        self.tokenizer = None
        self.model = None
        self.llm = None
        self.agent = None
        self.memory = None
        self._setup_model()
        self._setup_tools()
        self._setup_agent()

    def _setup_model(self):
        """Load and setup the Finance-Llama-8B model."""
        try:
            print(f"Loading model: {self.model_name}")

            # Check if CUDA is available
            device = "cuda" if torch.cuda.is_available() else "cpu"
            print(f"Using device: {device}")

            # Load tokenizer
            self.tokenizer = AutoTokenizer.from_pretrained(
                self.model_name,
                trust_remote_code=True
            )

            # Add pad token if not present
            if self.tokenizer.pad_token is None:
                self.tokenizer.pad_token = self.tokenizer.eos_token

            # Load model with appropriate settings and memory optimization
            if device == "cuda":
                self.model = AutoModelForCausalLM.from_pretrained(
                    self.model_name,
                    torch_dtype=torch.float16,
                    device_map="auto",
                    trust_remote_code=True,
                    low_cpu_mem_usage=True,
                    load_in_8bit=True,  # Enable 8-bit quantization for memory efficiency
                    max_memory={0: "6GB"}  # Limit GPU memory usage
                )
            else:
                # For CPU, use aggressive memory optimization
                self.model = AutoModelForCausalLM.from_pretrained(
                    self.model_name,
                    trust_remote_code=True,
                    low_cpu_mem_usage=True,
                    torch_dtype=torch.float32,
                    device_map="cpu",
                    max_memory={"cpu": "8GB"}  # Limit CPU memory usage
                )

            # Create pipeline
            pipe = pipeline(
                "text-generation",
                model=self.model,
                tokenizer=self.tokenizer,
                max_new_tokens=512,
                temperature=0.1,
                do_sample=True,
                pad_token_id=self.tokenizer.eos_token_id,
                repetition_penalty=1.1
            )

            # Wrap in LangChain
            self.llm = HuggingFacePipeline(pipeline=pipe)
            print("βœ… Model loaded successfully")

        except Exception as e:
            print(f"❌ Error loading model: {str(e)}")
            # Fallback to a smaller model or OpenAI if Finance-Llama-8B fails
            self._setup_fallback_model()

    def _setup_fallback_model(self):
        """Setup a fallback model if Finance-Llama-8B fails to load."""
        try:
            print("Setting up fallback model...")
            from langchain_community.llms import OpenAI

            # Check for OpenAI API key
            if os.getenv("OPENAI_API_KEY"):
                self.llm = OpenAI(
                    temperature=0.1,
                    model_name="gpt-3.5-turbo-instruct",
                    max_tokens=512
                )
                print("βœ… Using OpenAI GPT-3.5 as fallback")
            else:
                raise ValueError("No OpenAI API key found")

        except Exception as e:
            print(f"❌ Fallback model failed: {str(e)}")
            # Last resort: use a very small local model
            try:
                pipe = pipeline(
                    "text-generation",
                    model="distilgpt2",
                    max_new_tokens=256,
                    temperature=0.7
                )
                self.llm = HuggingFacePipeline(pipeline=pipe)
                print("βœ… Using DistilGPT2 as emergency fallback")
            except Exception as final_e:
                raise RuntimeError(f"All model loading attempts failed: {final_e}")

    def _setup_tools(self):
        """Setup all available financial analysis tools."""
        self.tools = [
            # Finance calculation tools
            calculate_dcf,
            calculate_sharpe_ratio,
            compare_pe_ratios,
            calculate_beta,
            calculate_wacc,
            financial_ratios_analysis,

            # Data fetching tools
            get_stock_price,
            get_historical_data,
            get_company_info,
            get_financial_statements,
            get_market_indices,
            compare_stocks
        ]
        print(f"βœ… Loaded {len(self.tools)} financial analysis tools")

    def _setup_agent(self):
        """Setup the LangChain agent with memory and tools."""
        try:
            # Setup conversation memory
            self.memory = ConversationBufferMemory(
                memory_key="chat_history",
                return_messages=True,
                output_key="output"
            )

            # Initialize agent
            self.agent = initialize_agent(
                tools=self.tools,
                llm=self.llm,
                agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
                memory=self.memory,
                verbose=True,
                handle_parsing_errors=True,
                max_iterations=3,
                early_stopping_method="generate"
            )

            # Add system message for financial context
            system_message = """You are a CFA (Chartered Financial Analyst) AI assistant specialized in financial analysis, investment valuation, and portfolio management.

Your expertise includes:
- Financial statement analysis and ratio calculations
- Valuation models (DCF, comparable company analysis, etc.)
- Risk assessment and portfolio theory
- Market analysis and economic indicators
- Investment recommendations based on fundamental analysis

When answering questions:
1. Use the available financial tools to fetch real data when needed
2. Provide clear, professional explanations suitable for CFA-level analysis
3. Show your calculations and reasoning
4. Consider both quantitative and qualitative factors
5. Acknowledge limitations and assumptions in your analysis

You have access to real-time financial data and calculation tools. Use them to provide accurate, data-driven insights."""

            # Store system message for context
            self.system_message = system_message
            print("βœ… Agent initialized successfully")

        except Exception as e:
            print(f"❌ Error setting up agent: {str(e)}")
            raise

    def query(self, question: str) -> str:
        """
        Process a financial query using the CFA agent.

        Args:
            question: User's financial question or request

        Returns:
            Agent's response with analysis and recommendations
        """
        try:
            # Enhance the question with context
            enhanced_question = f"""As a CFA analyst, please help with the following:

{question}

Please provide a thorough analysis using available data and financial tools. Show your work and explain your reasoning."""

            # Get response from agent
            response = self.agent.run(enhanced_question)
            return response

        except Exception as e:
            error_msg = f"Error processing query: {str(e)}"
            print(error_msg)
            return error_msg

    def get_conversation_history(self) -> List[Any]:
        """Get the current conversation history."""
        if self.memory:
            return self.memory.chat_memory.messages
        return []

    def clear_memory(self):
        """Clear the conversation memory."""
        if self.memory:
            self.memory.clear()
            print("βœ… Conversation memory cleared")

    def get_available_tools(self) -> List[str]:
        """Get list of available tool names."""
        return [tool.name for tool in self.tools]

    def health_check(self) -> dict:
        """Perform a health check of the agent components."""
        status = {
            "model_loaded": self.model is not None,
            "llm_ready": self.llm is not None,
            "agent_ready": self.agent is not None,
            "memory_ready": self.memory is not None,
            "tools_count": len(self.tools),
            "device": "cuda" if torch.cuda.is_available() else "cpu"
        }
        return status


def create_cfa_agent(model_name: str = "tarun7r/Finance-Llama-8B") -> CFAAgent:
    """
    Factory function to create and return a CFA Agent instance.

    Args:
        model_name: Hugging Face model name for financial analysis

    Returns:
        Initialized CFAAgent instance
    """
    try:
        agent = CFAAgent(model_name=model_name)
        print("🎯 CFA Agent created successfully")
        return agent
    except Exception as e:
        print(f"❌ Failed to create CFA Agent: {str(e)}")
        raise


# Example usage and testing
if __name__ == "__main__":
    print("πŸš€ Initializing CFA AI Agent...")

    try:
        # Create agent
        cfa_agent = create_cfa_agent()

        # Health check
        health = cfa_agent.health_check()
        print("πŸ“Š Health Check Results:")
        for key, value in health.items():
            status = "βœ…" if value else "❌"
            print(f"  {status} {key}: {value}")

        # Test queries
        test_queries = [
            "What is the current stock price of Apple (AAPL)?",
            "Calculate the PE ratio comparison between Apple and Microsoft",
            "Explain the CAPM model in simple terms"
        ]

        print("\nπŸ§ͺ Running test queries...")
        for i, query in enumerate(test_queries, 1):
            print(f"\n--- Test Query {i} ---")
            print(f"Q: {query}")
            try:
                response = cfa_agent.query(query)
                print(f"A: {response}")
            except Exception as e:
                print(f"❌ Query failed: {str(e)}")

    except Exception as e:
        print(f"❌ CFA Agent initialization failed: {str(e)}")