File size: 6,629 Bytes
30c4182
a1ef945
 
 
35acd3c
c7a28ea
ceb9625
a88d6ed
eac8dde
 
 
 
6e9bd28
35acd3c
eac8dde
 
ceb9625
0ff54a0
 
ca1d38d
30c4182
ca1d38d
 
 
35acd3c
17c0709
30c4182
 
98d6352
30c4182
 
 
17c0709
ca1d38d
 
98d6352
30c4182
ca1d38d
 
a88d6ed
35acd3c
ca1d38d
35acd3c
 
17c0709
 
9e42b37
17c0709
9e42b37
17c0709
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9e42b37
17c0709
9e42b37
17c0709
9e42b37
 
17c0709
98d6352
17c0709
 
 
9e42b37
17c0709
 
9e42b37
17c0709
 
 
 
 
 
 
 
98d6352
17c0709
9e42b37
98d6352
a88d6ed
17c0709
 
a772146
17c0709
a772146
9e42b37
98d6352
17c0709
 
 
 
9e42b37
bc8be1d
17c0709
 
9e42b37
 
30c4182
98d6352
30c4182
f95f954
17c0709
9e42b37
 
 
 
 
17c0709
98d6352
9e42b37
98d6352
17c0709
 
 
 
 
98d6352
a88d6ed
9e42b37
 
 
98d6352
a772146
17c0709
 
9e42b37
17c0709
 
 
98d6352
9e42b37
98d6352
 
9e42b37
a88d6ed
ca1d38d
a1ef945
195e9d5
9e42b37
a88d6ed
17c0709
 
9e42b37
 
 
17c0709
 
 
 
 
9e42b37
17c0709
9e42b37
17c0709
 
 
9e42b37
 
a1ef945
9e42b37
195e9d5
eac8dde
9e42b37
17c0709
 
9e42b37
f4bbd39
f95f954
a1ef945
ca1d38d
98d6352
17c0709
 
98d6352
4682c30
17c0709
 
 
9e42b37
17c0709
a1ef945
 
17c0709
4682c30
9e42b37
 
 
 
 
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
import os
import gradio as gr
import pandas as pd
import torch
import logging
from transformers import pipeline

# Setup logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
logger.info(f"Using device: {DEVICE}")

class FinancialAnalyzer:
    def __init__(self):
        self.analysis_model = None
        self.sentiment_model = None
        self.load_models()

    def load_models(self):
        try:
            logger.info("Loading TinyLlama model...")
            self.analysis_model = pipeline(
                "text-generation",
                model="TinyLlama/TinyLlama-1.1B-Chat-v1.0",
                torch_dtype=torch.float16 if DEVICE == "cuda" else torch.float32
            )
            
            logger.info("Loading FinBERT model...")
            self.sentiment_model = pipeline(
                "text-classification",
                model="ProsusAI/finbert",
                torch_dtype=torch.float16 if DEVICE == "cuda" else torch.float32
            )
            
            logger.info("Models loaded successfully")
        except Exception as e:
            logger.error(f"Error loading models: {str(e)}")
            raise

    def extract_and_analyze(self, statement_text, statement_type):
        """Extract information from financial statement text"""
        try:
            prompt = f"""[INST] As a financial analyst, analyze this {statement_type}:

{statement_text}

Extract and summarize:
1. Key financial numbers for 2025
2. Notable trends
3. Important metrics

Focus on the most recent year (2025) and key financial indicators.
[/INST]"""

            response = self.analysis_model(
                prompt,
                max_new_tokens=300,
                temperature=0.3,
                num_return_sequences=1,
                truncation=True
            )
            
            return response[0]['generated_text']
        except Exception as e:
            logger.error(f"Error extracting data from {statement_type}: {str(e)}")
            raise

    def analyze_financials(self, income_text, balance_text):
        try:
            # First, extract key information from each statement
            logger.info("Analyzing Income Statement...")
            income_analysis = self.extract_and_analyze(income_text, "Income Statement")
            
            logger.info("Analyzing Balance Sheet...")
            balance_analysis = self.extract_and_analyze(balance_text, "Balance Sheet")
            
            # Combine the analyses
            combined_analysis = f"""Income Statement Analysis:
{income_analysis}

Balance Sheet Analysis:
{balance_analysis}"""

            # Get sentiment
            sentiment = self.sentiment_model(
                combined_analysis[:512],
                truncation=True
            )[0]
            
            # Generate final analysis
            final_prompt = f"""[INST] Based on this financial analysis:

{combined_analysis}

Market Sentiment: {sentiment['label']} ({sentiment['score']:.2%})

Provide a concise analysis with:
1. Overall Financial Health (2-3 key points)
2. Main Business Insights (2-3 insights)
3. Key Recommendations (2-3 recommendations)
[/INST]"""

            final_response = self.analysis_model(
                final_prompt,
                max_new_tokens=500,
                temperature=0.7,
                num_return_sequences=1,
                truncation=True
            )

            return self.format_response(final_response[0]['generated_text'], sentiment, combined_analysis)
            
        except Exception as e:
            logger.error(f"Analysis error: {str(e)}")
            return f"Error in analysis: {str(e)}"

    def format_response(self, analysis_text, sentiment, raw_analysis):
        try:
            sections = [
                "# Financial Analysis Report\n\n",
                f"## Market Sentiment: {sentiment['label'].upper()} ({sentiment['score']:.2%})\n\n",
                "## Extracted Financial Data\n```\n",
                raw_analysis,
                "\n```\n\n",
                "## Analysis\n\n"
            ]
            
            for line in analysis_text.split('\n'):
                line = line.strip()
                if not line:
                    continue
                
                if any(header in line for header in ["Financial Health", "Business Insights", "Recommendations"]):
                    sections.append(f"\n### {line}\n")
                elif line:
                    if not line.startswith('-'):
                        line = f"- {line}"
                    sections.append(f"{line}\n")
            
            return "".join(sections)
        except Exception as e:
            logger.error(f"Error formatting response: {str(e)}")
            return "Error formatting analysis"

def analyze_statements(income_statement, balance_sheet):
    try:
        if not income_statement or not balance_sheet:
            return "Please upload both financial statements."

        logger.info("Reading financial statements...")
        # Read files as text
        income_df = pd.read_csv(income_statement)
        balance_df = pd.read_csv(balance_sheet)
        
        # Convert to string while preserving format
        income_text = income_df.to_string(index=False)
        balance_text = balance_df.to_string(index=False)
        
        logger.info("Initializing analysis...")
        analyzer = FinancialAnalyzer()
        result = analyzer.analyze_financials(income_text, balance_text)
        
        if DEVICE == "cuda":
            torch.cuda.empty_cache()
            
        return result
        
    except Exception as e:
        logger.error(f"Error: {str(e)}")
        return f"""Analysis Error: {str(e)}
        
Please check:
1. Files are readable CSV files
2. Files contain financial data
3. Files are not corrupted"""

# Create Gradio interface
iface = gr.Interface(
    fn=analyze_statements,
    inputs=[
        gr.File(label="Income Statement (CSV)", file_types=[".csv"]),
        gr.File(label="Balance Sheet (CSV)", file_types=[".csv"])
    ],
    outputs=gr.Markdown(),
    title="AI Financial Statement Analyzer",
    description="""Upload your financial statements for AI analysis.
The model will extract and analyze key financial information automatically.""",
    theme="default",
    flagging_mode="never"
)

# Launch
if __name__ == "__main__":
    iface.launch(
        server_name="0.0.0.0",
        server_port=7860,
        share=False
    )