Spaces:
Sleeping
Sleeping
Sahil Garg
commited on
Commit
·
a9ec4f6
1
Parent(s):
2d701b7
agent added, files name changed
Browse files- .dockerignore +64 -0
- .env.example +28 -0
- Dockerfile +18 -16
- README.md +57 -0
- agents/__init__.py +1 -0
- agents/base_config.py +106 -0
- agents/simple_agent.py +69 -0
- agents/simple_tools.py +395 -0
- app.py +64 -23
- bs/{csv_json_bs.py → balance_sheet_csv_to_json_converter.py} +0 -0
- bs/{sircodebs.py → balance_sheet_data_extractor.py} +0 -0
- bs/{bl_llm.py → balance_sheet_generator.py} +0 -0
- bs/{temp_bl.py → balance_sheet_template_handler.py} +0 -0
- cf/{csv_json_cf.py → cash_flow_csv_to_json_converter.py} +0 -0
- cf/{sircodecf.py → cash_flow_data_extractor.py} +0 -0
- cf/{cf_middlestep.py → cash_flow_data_processor.py} +0 -0
- cf/{cf_generation.py → cash_flow_statement_generator.py} +0 -0
- docker-compose.yml +28 -5
- pnl/{csv_json_pnl.py → profit_loss_csv_to_json_converter.py} +0 -0
- pnl/{sircodepnl.py → profit_loss_data_extractor.py} +0 -0
- pnl/{pnl_note.py → profit_loss_statement_generator.py} +0 -0
- requirements.txt +8 -1
.dockerignore
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Python
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
*.so
|
| 6 |
+
.Python
|
| 7 |
+
env/
|
| 8 |
+
venv/
|
| 9 |
+
.venv/
|
| 10 |
+
pip-log.txt
|
| 11 |
+
pip-delete-this-directory.txt
|
| 12 |
+
.tox/
|
| 13 |
+
.coverage
|
| 14 |
+
.pytest_cache/
|
| 15 |
+
|
| 16 |
+
# IDEs
|
| 17 |
+
.vscode/
|
| 18 |
+
.idea/
|
| 19 |
+
*.swp
|
| 20 |
+
*.swo
|
| 21 |
+
*~
|
| 22 |
+
|
| 23 |
+
# OS
|
| 24 |
+
.DS_Store
|
| 25 |
+
.DS_Store?
|
| 26 |
+
._*
|
| 27 |
+
.Spotlight-V100
|
| 28 |
+
.Trashes
|
| 29 |
+
ehthumbs.db
|
| 30 |
+
Thumbs.db
|
| 31 |
+
|
| 32 |
+
# Git
|
| 33 |
+
.git/
|
| 34 |
+
.gitignore
|
| 35 |
+
|
| 36 |
+
# Documentation
|
| 37 |
+
README.md
|
| 38 |
+
*.md
|
| 39 |
+
docs/
|
| 40 |
+
|
| 41 |
+
# Environment files (these should be mounted or provided separately)
|
| 42 |
+
.env
|
| 43 |
+
.env.local
|
| 44 |
+
.env.example
|
| 45 |
+
|
| 46 |
+
# Data directories (these should be mounted as volumes)
|
| 47 |
+
data/input/*
|
| 48 |
+
data/output*/*
|
| 49 |
+
data/csv_notes_*/*
|
| 50 |
+
data/generated_notes*/*
|
| 51 |
+
|
| 52 |
+
# Logs
|
| 53 |
+
*.log
|
| 54 |
+
logs/
|
| 55 |
+
|
| 56 |
+
# Docker
|
| 57 |
+
Dockerfile
|
| 58 |
+
docker-compose.yml
|
| 59 |
+
.dockerignore
|
| 60 |
+
|
| 61 |
+
# Development
|
| 62 |
+
.pytest_cache/
|
| 63 |
+
.coverage
|
| 64 |
+
htmlcov/
|
.env.example
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# FinRyver Environment Configuration
|
| 2 |
+
# Copy this file to .env and fill in your actual values
|
| 3 |
+
HFTOKEN=
|
| 4 |
+
OPENROUTER_API_KEY=
|
| 5 |
+
PROJECT_ROOT=
|
| 6 |
+
|
| 7 |
+
# OpenAI API Configuration (required for LangChain agents)
|
| 8 |
+
OPENAI_API_KEY=your_openai_api_key_here
|
| 9 |
+
|
| 10 |
+
# LangChain Configuration
|
| 11 |
+
LANGCHAIN_TRACING_V2=false
|
| 12 |
+
LANGCHAIN_API_KEY=your_langsmith_api_key_here
|
| 13 |
+
LANGCHAIN_PROJECT=finryver-agents
|
| 14 |
+
|
| 15 |
+
# Agent Configuration
|
| 16 |
+
AGENT_MODEL=gpt-4
|
| 17 |
+
AGENT_TEMPERATURE=0.1
|
| 18 |
+
AGENT_MAX_TOKENS=2000
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
# Application Configuration
|
| 22 |
+
DEBUG=false
|
| 23 |
+
LOG_LEVEL=INFO
|
| 24 |
+
|
| 25 |
+
# File Paths (optional - defaults will be used if not specified)
|
| 26 |
+
DATA_INPUT_PATH=data/input
|
| 27 |
+
DATA_OUTPUT_PATH=data/output
|
| 28 |
+
TEMP_PATH=temp
|
Dockerfile
CHANGED
|
@@ -1,49 +1,51 @@
|
|
| 1 |
# Use Python 3.11 as base image
|
| 2 |
FROM python:3.11-slim
|
| 3 |
|
| 4 |
-
# -------------------------------
|
| 5 |
# Set working directory
|
| 6 |
WORKDIR /app
|
| 7 |
-
COPY . .
|
| 8 |
|
| 9 |
-
# -------------------------------
|
| 10 |
# Install system dependencies
|
| 11 |
-
RUN ls -l /app
|
| 12 |
RUN apt-get update && apt-get install -y \
|
| 13 |
build-essential \
|
| 14 |
curl \
|
| 15 |
git \
|
| 16 |
&& rm -rf /var/lib/apt/lists/*
|
| 17 |
|
| 18 |
-
#
|
| 19 |
-
# Copy and install Python dependencies
|
| 20 |
COPY requirements.txt .
|
| 21 |
RUN pip install --no-cache-dir -r requirements.txt
|
| 22 |
|
| 23 |
-
#
|
| 24 |
-
|
|
|
|
|
|
|
| 25 |
RUN mkdir -p /app/data/input \
|
|
|
|
| 26 |
/app/data/output1 \
|
| 27 |
/app/data/output2 \
|
| 28 |
/app/data/output3 \
|
| 29 |
/app/data/csv_notes_bs \
|
| 30 |
/app/data/csv_notes_cfs \
|
| 31 |
/app/data/csv_notes_pnl \
|
| 32 |
-
/app/data/
|
| 33 |
-
/app/data/
|
| 34 |
-
/app/data/output2 \
|
| 35 |
-
/app/data/output3 \
|
| 36 |
&& chmod -R 777 /app/data
|
| 37 |
|
| 38 |
-
# -------------------------------
|
| 39 |
# Set environment variables
|
| 40 |
ENV PYTHONPATH=/app
|
| 41 |
ENV PYTHONUNBUFFERED=1
|
| 42 |
|
| 43 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
# Expose the port
|
| 45 |
EXPOSE 8000
|
| 46 |
|
| 47 |
-
#
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
| 49 |
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
|
|
| 1 |
# Use Python 3.11 as base image
|
| 2 |
FROM python:3.11-slim
|
| 3 |
|
|
|
|
| 4 |
# Set working directory
|
| 5 |
WORKDIR /app
|
|
|
|
| 6 |
|
|
|
|
| 7 |
# Install system dependencies
|
|
|
|
| 8 |
RUN apt-get update && apt-get install -y \
|
| 9 |
build-essential \
|
| 10 |
curl \
|
| 11 |
git \
|
| 12 |
&& rm -rf /var/lib/apt/lists/*
|
| 13 |
|
| 14 |
+
# Copy and install Python dependencies first (better caching)
|
|
|
|
| 15 |
COPY requirements.txt .
|
| 16 |
RUN pip install --no-cache-dir -r requirements.txt
|
| 17 |
|
| 18 |
+
# Copy application code
|
| 19 |
+
COPY . .
|
| 20 |
+
|
| 21 |
+
# Create necessary data directories for financial processing
|
| 22 |
RUN mkdir -p /app/data/input \
|
| 23 |
+
/app/data/output \
|
| 24 |
/app/data/output1 \
|
| 25 |
/app/data/output2 \
|
| 26 |
/app/data/output3 \
|
| 27 |
/app/data/csv_notes_bs \
|
| 28 |
/app/data/csv_notes_cfs \
|
| 29 |
/app/data/csv_notes_pnl \
|
| 30 |
+
/app/data/generated_notes \
|
| 31 |
+
/app/data/generated_notes_excel \
|
|
|
|
|
|
|
| 32 |
&& chmod -R 777 /app/data
|
| 33 |
|
|
|
|
| 34 |
# Set environment variables
|
| 35 |
ENV PYTHONPATH=/app
|
| 36 |
ENV PYTHONUNBUFFERED=1
|
| 37 |
|
| 38 |
+
# Default agent configuration (can be overridden by .env)
|
| 39 |
+
ENV AGENT_MODEL=gpt-3.5-turbo
|
| 40 |
+
ENV AGENT_TEMPERATURE=0.1
|
| 41 |
+
ENV AGENT_MAX_TOKENS=2000
|
| 42 |
+
|
| 43 |
# Expose the port
|
| 44 |
EXPOSE 8000
|
| 45 |
|
| 46 |
+
# Health check for the API
|
| 47 |
+
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
|
| 48 |
+
CMD curl -f http://localhost:8000/docs || exit 1
|
| 49 |
+
|
| 50 |
+
# Start FastAPI app
|
| 51 |
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
|
README.md
CHANGED
|
@@ -4,14 +4,18 @@
|
|
| 4 |
|
| 5 |
FinRyver is an AI-powered financial statement generation platform that automatically converts trial balance data into comprehensive financial reports including balance sheets, cash flow statements, and profit & loss statements. Built with FastAPI and leveraging Large Language Models (LLMs), it streamlines the financial reporting process for accountants, auditors, and financial professionals by automating the generation of detailed financial notes and statements from structured trial balance inputs.
|
| 6 |
|
|
|
|
|
|
|
| 7 |
## 🎯 Key Features
|
| 8 |
|
| 9 |
- **Automated Trial Balance Processing**: Upload Excel files containing trial balance data and automatically extract structured financial information
|
| 10 |
- **AI-Powered Financial Notes Generation**: Utilize LLMs to generate comprehensive financial notes with detailed explanations and context
|
|
|
|
| 11 |
- **Multi-Statement Support**: Generate Balance Sheets, Cash Flow Statements, and Profit & Loss statements from the same data source
|
| 12 |
- **Excel Output Generation**: Export all generated reports and notes to professional Excel formats
|
| 13 |
- **RESTful API Architecture**: Easy integration with existing financial systems through well-documented REST endpoints
|
| 14 |
- **Flexible Note Selection**: Generate specific financial notes by number or create comprehensive reports covering all relevant sections
|
|
|
|
| 15 |
|
| 16 |
## 🏗️ Project Architecture
|
| 17 |
|
|
@@ -22,6 +26,11 @@ FinRyver/
|
|
| 22 |
├── Dockerfile # Container configuration
|
| 23 |
├── docker-compose.yml # Multi-container orchestration
|
| 24 |
├──
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
├── bs/ # Balance Sheet processing modules
|
| 26 |
│ ├── bl_llm.py # Balance sheet LLM integration
|
| 27 |
│ ├── csv_json_bs.py # Balance sheet data conversion
|
|
@@ -80,6 +89,8 @@ Trial Balance Upload → Data Extraction → AI Processing → Financial Stateme
|
|
| 80 |
|
| 81 |
### AI/ML Integration
|
| 82 |
- **Large Language Models (LLMs)**: For intelligent financial note generation and analysis
|
|
|
|
|
|
|
| 83 |
- **Custom AI Pipelines**: Specialized processing for financial data interpretation
|
| 84 |
|
| 85 |
### Infrastructure
|
|
@@ -128,6 +139,25 @@ Trial Balance Upload → Data Extraction → AI Processing → Financial Stateme
|
|
| 128 |
| `/bs_from_notes` | POST | Generate balance sheet from existing notes |
|
| 129 |
| `/pnl_from_notes` | POST | Generate P&L statement from existing notes |
|
| 130 |
| `/cf_from_notes` | POST | Generate cash flow statement from existing notes |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
|
| 132 |
## 📊 Results & Examples
|
| 133 |
|
|
@@ -189,6 +219,27 @@ docker run -p 8000:8000 finryver
|
|
| 189 |
2. **Business Rules**: Modify `config/rules1.json` for custom validation rules
|
| 190 |
3. **Account Mapping**: Update `config/mapping1.json` for account categorization
|
| 191 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
### Usage Examples
|
| 193 |
|
| 194 |
#### Generate Complete Financial Report
|
|
@@ -234,12 +285,18 @@ python -m pytest tests/ --verbose
|
|
| 234 |
|
| 235 |
## 🔮 Future Roadmap
|
| 236 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
### Planned Features
|
| 238 |
- **Multi-Currency Support**: Handle international financial statements
|
| 239 |
- **Advanced AI Models**: Integration with latest financial AI models
|
| 240 |
- **Real-time Processing**: WebSocket support for live data updates
|
| 241 |
- **Audit Trail**: Comprehensive logging and change tracking
|
| 242 |
- **Custom Templates**: User-defined financial statement templates
|
|
|
|
| 243 |
|
| 244 |
### Known Limitations
|
| 245 |
- Currently supports Excel input formats only
|
|
|
|
| 4 |
|
| 5 |
FinRyver is an AI-powered financial statement generation platform that automatically converts trial balance data into comprehensive financial reports including balance sheets, cash flow statements, and profit & loss statements. Built with FastAPI and leveraging Large Language Models (LLMs), it streamlines the financial reporting process for accountants, auditors, and financial professionals by automating the generation of detailed financial notes and statements from structured trial balance inputs.
|
| 6 |
|
| 7 |
+
**New in 2025**: FinRyver now features an intelligent **agentic system** powered by LangChain that provides AI-driven automation, natural language processing, and intelligent decision-making for financial statement generation.
|
| 8 |
+
|
| 9 |
## 🎯 Key Features
|
| 10 |
|
| 11 |
- **Automated Trial Balance Processing**: Upload Excel files containing trial balance data and automatically extract structured financial information
|
| 12 |
- **AI-Powered Financial Notes Generation**: Utilize LLMs to generate comprehensive financial notes with detailed explanations and context
|
| 13 |
+
- **Intelligent Agentic System**: LangChain-powered agents that understand natural language instructions and automate complex financial workflows
|
| 14 |
- **Multi-Statement Support**: Generate Balance Sheets, Cash Flow Statements, and Profit & Loss statements from the same data source
|
| 15 |
- **Excel Output Generation**: Export all generated reports and notes to professional Excel formats
|
| 16 |
- **RESTful API Architecture**: Easy integration with existing financial systems through well-documented REST endpoints
|
| 17 |
- **Flexible Note Selection**: Generate specific financial notes by number or create comprehensive reports covering all relevant sections
|
| 18 |
+
- **Unified Agent Interface**: Single `/agent/generate` endpoint for intelligent financial statement generation
|
| 19 |
|
| 20 |
## 🏗️ Project Architecture
|
| 21 |
|
|
|
|
| 26 |
├── Dockerfile # Container configuration
|
| 27 |
├── docker-compose.yml # Multi-container orchestration
|
| 28 |
├──
|
| 29 |
+
├── agents/ # Agentic system (LangChain)
|
| 30 |
+
│ ├── base_config.py # Agent configuration and utilities
|
| 31 |
+
│ ├── simple_agent.py # Financial statement agent
|
| 32 |
+
│ └── simple_tools.py # LangChain tools for financial processing
|
| 33 |
+
├──
|
| 34 |
├── bs/ # Balance Sheet processing modules
|
| 35 |
│ ├── bl_llm.py # Balance sheet LLM integration
|
| 36 |
│ ├── csv_json_bs.py # Balance sheet data conversion
|
|
|
|
| 89 |
|
| 90 |
### AI/ML Integration
|
| 91 |
- **Large Language Models (LLMs)**: For intelligent financial note generation and analysis
|
| 92 |
+
- **LangChain Framework**: Agentic system for intelligent financial statement processing
|
| 93 |
+
- **OpenRouter API**: Flexible LLM provider integration for AI-powered analysis
|
| 94 |
- **Custom AI Pipelines**: Specialized processing for financial data interpretation
|
| 95 |
|
| 96 |
### Infrastructure
|
|
|
|
| 139 |
| `/bs_from_notes` | POST | Generate balance sheet from existing notes |
|
| 140 |
| `/pnl_from_notes` | POST | Generate P&L statement from existing notes |
|
| 141 |
| `/cf_from_notes` | POST | Generate cash flow statement from existing notes |
|
| 142 |
+
| `/agent/generate` | POST | **NEW**: Intelligent agent-based financial statement generation |
|
| 143 |
+
|
| 144 |
+
#### Agentic System Endpoint
|
| 145 |
+
|
| 146 |
+
**`POST /agent/generate`** - Unified intelligent financial statement generation
|
| 147 |
+
|
| 148 |
+
**Parameters:**
|
| 149 |
+
- `file`: Trial balance Excel file
|
| 150 |
+
- `note_numbers`: Optional comma-separated note numbers (empty = all notes)
|
| 151 |
+
- `statement_type`: "all", "notes", "balance_sheet", "pnl", or "cash_flow"
|
| 152 |
+
|
| 153 |
+
**Example Usage:**
|
| 154 |
+
```bash
|
| 155 |
+
curl -X POST "http://localhost:8000/agent/generate" \
|
| 156 |
+
-H "Content-Type: multipart/form-data" \
|
| 157 |
+
-F "file=@trial_balance.xlsx" \
|
| 158 |
+
-F "note_numbers=2,3,4,5" \
|
| 159 |
+
-F "statement_type=all"
|
| 160 |
+
```
|
| 161 |
|
| 162 |
## 📊 Results & Examples
|
| 163 |
|
|
|
|
| 219 |
2. **Business Rules**: Modify `config/rules1.json` for custom validation rules
|
| 220 |
3. **Account Mapping**: Update `config/mapping1.json` for account categorization
|
| 221 |
|
| 222 |
+
#### Agentic System Configuration
|
| 223 |
+
|
| 224 |
+
For the intelligent agent features, ensure you have your LLM API key configured:
|
| 225 |
+
|
| 226 |
+
```env
|
| 227 |
+
# Required for agentic system
|
| 228 |
+
OPENROUTER_API_KEY=your_openrouter_api_key_here
|
| 229 |
+
|
| 230 |
+
# Optional agent configuration
|
| 231 |
+
AGENT_MODEL=gpt-3.5-turbo
|
| 232 |
+
AGENT_TEMPERATURE=0.1
|
| 233 |
+
AGENT_MAX_TOKENS=2000
|
| 234 |
+
```
|
| 235 |
+
|
| 236 |
+
**Agent Benefits:**
|
| 237 |
+
- Natural language understanding for financial tasks
|
| 238 |
+
- Intelligent workflow orchestration
|
| 239 |
+
- Unified interface for all financial statements
|
| 240 |
+
- Error recovery and retry logic
|
| 241 |
+
- Contextual financial analysis
|
| 242 |
+
|
| 243 |
### Usage Examples
|
| 244 |
|
| 245 |
#### Generate Complete Financial Report
|
|
|
|
| 285 |
|
| 286 |
## 🔮 Future Roadmap
|
| 287 |
|
| 288 |
+
### Completed Features ✅
|
| 289 |
+
- **Intelligent Agentic System**: LangChain-powered agents with natural language processing
|
| 290 |
+
- **Unified Agent Interface**: Single endpoint for all financial statement generation
|
| 291 |
+
- **Optional Note Numbers**: Flexible note generation (specific or all notes)
|
| 292 |
+
|
| 293 |
### Planned Features
|
| 294 |
- **Multi-Currency Support**: Handle international financial statements
|
| 295 |
- **Advanced AI Models**: Integration with latest financial AI models
|
| 296 |
- **Real-time Processing**: WebSocket support for live data updates
|
| 297 |
- **Audit Trail**: Comprehensive logging and change tracking
|
| 298 |
- **Custom Templates**: User-defined financial statement templates
|
| 299 |
+
- **Agent Conversation History**: Multi-turn conversations with financial agents
|
| 300 |
|
| 301 |
### Known Limitations
|
| 302 |
- Currently supports Excel input formats only
|
agents/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
# Agent framework for FinRyver
|
agents/base_config.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Base configuration and utilities for LangChain agents in FinRyver
|
| 3 |
+
"""
|
| 4 |
+
import os
|
| 5 |
+
from typing import Optional
|
| 6 |
+
from langchain_openai import ChatOpenAI
|
| 7 |
+
from langchain.agents import AgentExecutor, create_openai_tools_agent
|
| 8 |
+
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
| 9 |
+
from langchain.tools import Tool
|
| 10 |
+
from langchain_core.messages import HumanMessage, SystemMessage
|
| 11 |
+
import logging
|
| 12 |
+
|
| 13 |
+
# Load environment variables
|
| 14 |
+
try:
|
| 15 |
+
from dotenv import load_dotenv
|
| 16 |
+
load_dotenv()
|
| 17 |
+
except ImportError:
|
| 18 |
+
pass # dotenv is optional
|
| 19 |
+
|
| 20 |
+
logger = logging.getLogger(__name__)
|
| 21 |
+
|
| 22 |
+
class FinRyverAgentConfig:
|
| 23 |
+
"""Configuration class for FinRyver agents"""
|
| 24 |
+
|
| 25 |
+
def __init__(self):
|
| 26 |
+
self.openrouter_api_key = os.getenv("OPENROUTER_API_KEY")
|
| 27 |
+
self.model_name = os.getenv("AGENT_MODEL", "gpt-3.5-turbo")
|
| 28 |
+
self.temperature = float(os.getenv("AGENT_TEMPERATURE", "0.1"))
|
| 29 |
+
self.max_tokens = int(os.getenv("AGENT_MAX_TOKENS", "2000"))
|
| 30 |
+
|
| 31 |
+
def get_llm(self) -> ChatOpenAI:
|
| 32 |
+
"""Get configured LLM instance"""
|
| 33 |
+
if not self.openrouter_api_key:
|
| 34 |
+
logger.warning("OPENROUTER_API_KEY not found. Agent functionality may be limited.")
|
| 35 |
+
return None
|
| 36 |
+
|
| 37 |
+
return ChatOpenAI(
|
| 38 |
+
api_key=self.openrouter_api_key,
|
| 39 |
+
base_url="https://openrouter.ai/api/v1",
|
| 40 |
+
model=self.model_name,
|
| 41 |
+
temperature=self.temperature,
|
| 42 |
+
max_tokens=self.max_tokens
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
def create_financial_agent_prompt(role: str, goal: str, context: str = "") -> ChatPromptTemplate:
|
| 46 |
+
"""Create a standardized prompt template for financial agents"""
|
| 47 |
+
|
| 48 |
+
system_message = f"""
|
| 49 |
+
You are a {role} working with financial data analysis and reporting.
|
| 50 |
+
|
| 51 |
+
Your primary goal: {goal}
|
| 52 |
+
|
| 53 |
+
Context: {context}
|
| 54 |
+
|
| 55 |
+
You have access to various tools to help you analyze financial data, generate reports,
|
| 56 |
+
and ensure compliance with accounting standards. Always provide clear, accurate, and
|
| 57 |
+
professional responses.
|
| 58 |
+
|
| 59 |
+
When working with financial data:
|
| 60 |
+
- Ensure all calculations are accurate
|
| 61 |
+
- Follow GAAP/IFRS standards where applicable
|
| 62 |
+
- Provide clear explanations for your analysis
|
| 63 |
+
- Flag any anomalies or inconsistencies
|
| 64 |
+
- Always validate data before proceeding
|
| 65 |
+
|
| 66 |
+
If you need clarification or additional information, ask specific questions.
|
| 67 |
+
"""
|
| 68 |
+
|
| 69 |
+
return ChatPromptTemplate.from_messages([
|
| 70 |
+
("system", system_message),
|
| 71 |
+
MessagesPlaceholder(variable_name="chat_history", optional=True),
|
| 72 |
+
("human", "{input}"),
|
| 73 |
+
MessagesPlaceholder(variable_name="agent_scratchpad")
|
| 74 |
+
])
|
| 75 |
+
|
| 76 |
+
def create_agent_executor(
|
| 77 |
+
role: str,
|
| 78 |
+
goal: str,
|
| 79 |
+
tools: list,
|
| 80 |
+
context: str = "",
|
| 81 |
+
llm: Optional[ChatOpenAI] = None
|
| 82 |
+
) -> AgentExecutor:
|
| 83 |
+
"""Create a configured agent executor"""
|
| 84 |
+
|
| 85 |
+
if llm is None:
|
| 86 |
+
config = FinRyverAgentConfig()
|
| 87 |
+
llm = config.get_llm()
|
| 88 |
+
|
| 89 |
+
if llm is None:
|
| 90 |
+
raise ValueError("LLM configuration failed. Check your API keys.")
|
| 91 |
+
|
| 92 |
+
prompt = create_financial_agent_prompt(role, goal, context)
|
| 93 |
+
|
| 94 |
+
agent = create_openai_tools_agent(
|
| 95 |
+
llm=llm,
|
| 96 |
+
tools=tools,
|
| 97 |
+
prompt=prompt
|
| 98 |
+
)
|
| 99 |
+
|
| 100 |
+
return AgentExecutor(
|
| 101 |
+
agent=agent,
|
| 102 |
+
tools=tools,
|
| 103 |
+
verbose=True,
|
| 104 |
+
max_iterations=5,
|
| 105 |
+
early_stopping_method="generate"
|
| 106 |
+
)
|
agents/simple_agent.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Simplified financial agent focused only on generating financial statements
|
| 3 |
+
"""
|
| 4 |
+
from typing import Dict, Any, Optional
|
| 5 |
+
import logging
|
| 6 |
+
from .base_config import FinRyverAgentConfig, create_agent_executor
|
| 7 |
+
from .simple_tools import FINANCIAL_TOOLS
|
| 8 |
+
|
| 9 |
+
logger = logging.getLogger(__name__)
|
| 10 |
+
|
| 11 |
+
class FinancialStatementAgent:
|
| 12 |
+
"""
|
| 13 |
+
Simple agent that generates: Notes, Balance Sheet, P&L, Cash Flow
|
| 14 |
+
"""
|
| 15 |
+
|
| 16 |
+
def __init__(self, config: Optional[FinRyverAgentConfig] = None):
|
| 17 |
+
self.config = config or FinRyverAgentConfig()
|
| 18 |
+
self.llm = self.config.get_llm()
|
| 19 |
+
|
| 20 |
+
# Create agent with financial statement tools
|
| 21 |
+
self.agent_executor = create_agent_executor(
|
| 22 |
+
role="Financial Statement Generator",
|
| 23 |
+
goal="Generate financial statements (notes, balance sheet, P&L, cash flow) from trial balance data",
|
| 24 |
+
tools=FINANCIAL_TOOLS,
|
| 25 |
+
context="You generate financial statements from trial balance files. Available outputs: Notes, Balance Sheet, P&L Statement, Cash Flow Statement.",
|
| 26 |
+
llm=self.llm
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
async def generate_all_statements(self, file_path: str, note_numbers: str) -> Dict[str, Any]:
|
| 30 |
+
"""Generate all financial statements from trial balance file"""
|
| 31 |
+
try:
|
| 32 |
+
prompt = f"""
|
| 33 |
+
Generate all financial statements from the trial balance file: {file_path}
|
| 34 |
+
Note numbers to include: {note_numbers}
|
| 35 |
+
|
| 36 |
+
Please generate in this order:
|
| 37 |
+
1. Financial notes for note numbers: {note_numbers}
|
| 38 |
+
2. Balance sheet
|
| 39 |
+
3. P&L statement
|
| 40 |
+
4. Cash flow statement
|
| 41 |
+
|
| 42 |
+
Use the available tools to generate each statement and provide a summary of what was created.
|
| 43 |
+
"""
|
| 44 |
+
|
| 45 |
+
result = await self.agent_executor.ainvoke({"input": prompt})
|
| 46 |
+
return {"status": "success", "result": result["output"]}
|
| 47 |
+
|
| 48 |
+
except Exception as e:
|
| 49 |
+
logger.error(f"Error generating statements: {e}")
|
| 50 |
+
return {"status": "error", "error": str(e)}
|
| 51 |
+
|
| 52 |
+
async def generate_specific_statement(self, file_path: str, statement_type: str, note_numbers: str) -> Dict[str, Any]:
|
| 53 |
+
"""Generate a specific financial statement"""
|
| 54 |
+
try:
|
| 55 |
+
prompts = {
|
| 56 |
+
"notes": f"Generate financial notes for note numbers {note_numbers} from {file_path}",
|
| 57 |
+
"balance_sheet": f"Generate balance sheet from {file_path} using note numbers {note_numbers}",
|
| 58 |
+
"pnl": f"Generate P&L statement from {file_path} using note numbers {note_numbers}",
|
| 59 |
+
"cash_flow": f"Generate cash flow statement from {file_path} using note numbers {note_numbers}"
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
prompt = prompts.get(statement_type, prompts["notes"])
|
| 63 |
+
|
| 64 |
+
result = await self.agent_executor.ainvoke({"input": prompt})
|
| 65 |
+
return {"status": "success", "result": result["output"]}
|
| 66 |
+
|
| 67 |
+
except Exception as e:
|
| 68 |
+
logger.error(f"Error generating {statement_type}: {e}")
|
| 69 |
+
return {"status": "error", "error": str(e)}
|
agents/simple_tools.py
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Simplified LangChain tools for FinRyver financial statement generation
|
| 3 |
+
Focus: Notes, Balance Sheet, P&L, Cash Flow generation only
|
| 4 |
+
"""
|
| 5 |
+
from langchain_core.tools import tool
|
| 6 |
+
import os
|
| 7 |
+
import subprocess
|
| 8 |
+
import json
|
| 9 |
+
import shutil
|
| 10 |
+
import time
|
| 11 |
+
import uuid
|
| 12 |
+
from typing import Dict, Any
|
| 13 |
+
import logging
|
| 14 |
+
|
| 15 |
+
logger = logging.getLogger(__name__)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@tool
|
| 19 |
+
def generate_notes_from_trial_balance(file_path: str, note_numbers: str = "") -> Dict[str, Any]:
|
| 20 |
+
"""
|
| 21 |
+
Generate financial notes from trial balance file
|
| 22 |
+
Args:
|
| 23 |
+
file_path: Path to the trial balance Excel file
|
| 24 |
+
note_numbers: Optional comma-separated note numbers (e.g., "2,3,4,5"). If empty, generates all notes.
|
| 25 |
+
"""
|
| 26 |
+
execution_id = str(uuid.uuid4())[:8]
|
| 27 |
+
start_time = time.time()
|
| 28 |
+
tool_name = "generate_notes_from_trial_balance"
|
| 29 |
+
|
| 30 |
+
try:
|
| 31 |
+
|
| 32 |
+
# Copy file to input directory
|
| 33 |
+
input_dir = "data/input"
|
| 34 |
+
os.makedirs(input_dir, exist_ok=True)
|
| 35 |
+
input_file = os.path.join(input_dir, "trial_balance.xlsx")
|
| 36 |
+
shutil.copy2(file_path, input_file)
|
| 37 |
+
|
| 38 |
+
file_size = os.path.getsize(input_file)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
# Run notes generation using existing endpoint logic
|
| 42 |
+
env = os.environ.copy()
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
# Use existing notes generation modules
|
| 46 |
+
from notes.data_extraction import extract_trial_balance_data
|
| 47 |
+
from notes.llm_notes_generator import FlexibleFinancialNoteGenerator
|
| 48 |
+
from notes.json_to_excel import json_to_xlsx
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
# Extract trial balance data
|
| 52 |
+
trial_balance_data = extract_trial_balance_data(file_path)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
# Generate notes
|
| 57 |
+
note_generator = FlexibleFinancialNoteGenerator()
|
| 58 |
+
|
| 59 |
+
if note_numbers.strip():
|
| 60 |
+
# Generate specific notes
|
| 61 |
+
note_list = [int(n.strip()) for n in note_numbers.split(',')]
|
| 62 |
+
generated_notes = {}
|
| 63 |
+
for i, note_num in enumerate(note_list, 1):
|
| 64 |
+
note_content = note_generator.generate_note(note_num, trial_balance_data)
|
| 65 |
+
generated_notes[f"note_{note_num}"] = note_content
|
| 66 |
+
message = f"Notes generated for: {note_numbers}"
|
| 67 |
+
else:
|
| 68 |
+
# Generate all notes
|
| 69 |
+
generated_notes = note_generator.generate_all_notes(trial_balance_data)
|
| 70 |
+
message = "All notes generated successfully"
|
| 71 |
+
# Save notes
|
| 72 |
+
output_path = "data/generated_notes/notes.json"
|
| 73 |
+
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
| 74 |
+
with open(output_path, 'w') as f:
|
| 75 |
+
json.dump(generated_notes, f, indent=2)
|
| 76 |
+
|
| 77 |
+
execution_time = round(time.time() - start_time, 2)
|
| 78 |
+
output_file_size = os.path.getsize(output_path)
|
| 79 |
+
|
| 80 |
+
return {
|
| 81 |
+
"status": "success",
|
| 82 |
+
"message": message,
|
| 83 |
+
"output_path": output_path,
|
| 84 |
+
"execution_id": execution_id,
|
| 85 |
+
"execution_time": execution_time
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
except Exception as e:
|
| 89 |
+
execution_time = round(time.time() - start_time, 2)
|
| 90 |
+
|
| 91 |
+
return {
|
| 92 |
+
"status": "error",
|
| 93 |
+
"error": str(e),
|
| 94 |
+
"execution_id": execution_id,
|
| 95 |
+
"execution_time": execution_time
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
@tool
|
| 99 |
+
def generate_balance_sheet(file_path: str) -> Dict[str, Any]:
|
| 100 |
+
"""
|
| 101 |
+
Generate balance sheet from trial balance file using complete pipeline
|
| 102 |
+
Args:
|
| 103 |
+
file_path: Path to trial balance Excel file
|
| 104 |
+
"""
|
| 105 |
+
execution_id = str(uuid.uuid4())[:8]
|
| 106 |
+
start_time = time.time()
|
| 107 |
+
tool_name = "generate_balance_sheet"
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
try:
|
| 111 |
+
# Use the complete BS pipeline from the existing endpoint
|
| 112 |
+
env = os.environ.copy()
|
| 113 |
+
if os.getenv("OPENROUTER_API_KEY"):
|
| 114 |
+
env["OPENROUTER_API_KEY"] = os.getenv("OPENROUTER_API_KEY")
|
| 115 |
+
env["INPUT_FILE"] = "data/clean_financial_data_bs.json"
|
| 116 |
+
cwd = os.getcwd()
|
| 117 |
+
|
| 118 |
+
# Step 1: Run balance_sheet_data_extractor.py
|
| 119 |
+
|
| 120 |
+
result1 = subprocess.run(
|
| 121 |
+
["python", "bs/balance_sheet_data_extractor.py", file_path],
|
| 122 |
+
env=env,
|
| 123 |
+
cwd=cwd,
|
| 124 |
+
capture_output=True,
|
| 125 |
+
text=True
|
| 126 |
+
)
|
| 127 |
+
|
| 128 |
+
if result1.returncode != 0:
|
| 129 |
+
|
| 130 |
+
return {"status": "error", "error": f"Balance sheet data extraction failed: {result1.stderr}"}
|
| 131 |
+
|
| 132 |
+
# Step 2: Run balance_sheet_csv_to_json_converter.py
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
result2 = subprocess.run(
|
| 136 |
+
["python", "bs/balance_sheet_csv_to_json_converter.py"],
|
| 137 |
+
env=env,
|
| 138 |
+
cwd=cwd,
|
| 139 |
+
capture_output=True,
|
| 140 |
+
text=True
|
| 141 |
+
)
|
| 142 |
+
|
| 143 |
+
if result2.returncode != 0:
|
| 144 |
+
|
| 145 |
+
return {"status": "error", "error": f"CSV to JSON conversion failed: {result2.stderr}"}
|
| 146 |
+
|
| 147 |
+
# Step 3: Run balance_sheet_generator.py
|
| 148 |
+
|
| 149 |
+
result3 = subprocess.run(
|
| 150 |
+
["python", "bs/balance_sheet_generator.py"],
|
| 151 |
+
env=env,
|
| 152 |
+
cwd=cwd,
|
| 153 |
+
capture_output=True,
|
| 154 |
+
text=True
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
if result3.returncode == 0:
|
| 158 |
+
# Check for output files in data/output directory
|
| 159 |
+
output_files = []
|
| 160 |
+
if os.path.exists("data/output"):
|
| 161 |
+
output_files = [f for f in os.listdir("data/output") if f.endswith('.xlsx')]
|
| 162 |
+
|
| 163 |
+
execution_time = round(time.time() - start_time, 2)
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
return {
|
| 167 |
+
"status": "success",
|
| 168 |
+
"message": "Balance sheet generated successfully",
|
| 169 |
+
"output_path": "data/output/",
|
| 170 |
+
"output_files": output_files,
|
| 171 |
+
"execution_id": execution_id,
|
| 172 |
+
"execution_time": execution_time
|
| 173 |
+
}
|
| 174 |
+
else:
|
| 175 |
+
execution_time = round(time.time() - start_time, 2)
|
| 176 |
+
|
| 177 |
+
return {
|
| 178 |
+
"status": "error",
|
| 179 |
+
"error": f"Balance sheet generation failed: {result3.stderr}",
|
| 180 |
+
"execution_id": execution_id
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
except Exception as e:
|
| 184 |
+
execution_time = round(time.time() - start_time, 2)
|
| 185 |
+
|
| 186 |
+
return {
|
| 187 |
+
"status": "error",
|
| 188 |
+
"error": str(e),
|
| 189 |
+
"execution_id": execution_id,
|
| 190 |
+
"execution_time": execution_time
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
@tool
|
| 194 |
+
def generate_pnl_statement(file_path: str) -> Dict[str, Any]:
|
| 195 |
+
"""
|
| 196 |
+
Generate P&L statement from trial balance file using complete pipeline
|
| 197 |
+
Args:
|
| 198 |
+
file_path: Path to trial balance Excel file
|
| 199 |
+
"""
|
| 200 |
+
execution_id = str(uuid.uuid4())[:8]
|
| 201 |
+
start_time = time.time()
|
| 202 |
+
tool_name = "generate_pnl_statement"
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
try:
|
| 206 |
+
# Use the complete P&L pipeline from existing endpoint
|
| 207 |
+
env = os.environ.copy()
|
| 208 |
+
if os.getenv("OPENROUTER_API_KEY"):
|
| 209 |
+
env["OPENROUTER_API_KEY"] = os.getenv("OPENROUTER_API_KEY")
|
| 210 |
+
env["INPUT_FILE"] = "data/clean_financial_data_pnl.json"
|
| 211 |
+
cwd = os.getcwd()
|
| 212 |
+
|
| 213 |
+
# Step 1: Run profit_loss_data_extractor.py
|
| 214 |
+
|
| 215 |
+
result1 = subprocess.run(
|
| 216 |
+
["python", "pnl/profit_loss_data_extractor.py", file_path],
|
| 217 |
+
env=env,
|
| 218 |
+
cwd=cwd,
|
| 219 |
+
capture_output=True,
|
| 220 |
+
text=True
|
| 221 |
+
)
|
| 222 |
+
|
| 223 |
+
if result1.returncode != 0:
|
| 224 |
+
|
| 225 |
+
return {"status": "error", "error": f"P&L data extraction failed: {result1.stderr}"}
|
| 226 |
+
|
| 227 |
+
# Step 2: Run profit_loss_csv_to_json_converter.py
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
result2 = subprocess.run(
|
| 231 |
+
["python", "pnl/profit_loss_csv_to_json_converter.py"],
|
| 232 |
+
env=env,
|
| 233 |
+
cwd=cwd,
|
| 234 |
+
capture_output=True,
|
| 235 |
+
text=True
|
| 236 |
+
)
|
| 237 |
+
|
| 238 |
+
if result2.returncode != 0:
|
| 239 |
+
|
| 240 |
+
return {"status": "error", "error": f"P&L CSV to JSON conversion failed: {result2.stderr}"}
|
| 241 |
+
|
| 242 |
+
# Step 3: Run profit_loss_statement_generator.py
|
| 243 |
+
|
| 244 |
+
result3 = subprocess.run(
|
| 245 |
+
["python", "pnl/profit_loss_statement_generator.py", "data/clean_financial_data_pnl.json"],
|
| 246 |
+
env=env,
|
| 247 |
+
cwd=cwd,
|
| 248 |
+
capture_output=True,
|
| 249 |
+
text=True
|
| 250 |
+
)
|
| 251 |
+
|
| 252 |
+
if result3.returncode == 0:
|
| 253 |
+
execution_time = round(time.time() - start_time, 2)
|
| 254 |
+
output_path = "data/pnl_statement.xlsx"
|
| 255 |
+
|
| 256 |
+
return {
|
| 257 |
+
"status": "success",
|
| 258 |
+
"message": "P&L statement generated successfully",
|
| 259 |
+
"output_path": output_path,
|
| 260 |
+
"execution_id": execution_id,
|
| 261 |
+
"execution_time": execution_time
|
| 262 |
+
}
|
| 263 |
+
else:
|
| 264 |
+
execution_time = round(time.time() - start_time, 2)
|
| 265 |
+
|
| 266 |
+
return {
|
| 267 |
+
"status": "error",
|
| 268 |
+
"error": f"P&L generation failed: {result3.stderr}",
|
| 269 |
+
"execution_id": execution_id
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
except Exception as e:
|
| 273 |
+
execution_time = round(time.time() - start_time, 2)
|
| 274 |
+
|
| 275 |
+
return {
|
| 276 |
+
"status": "error",
|
| 277 |
+
"error": str(e),
|
| 278 |
+
"execution_id": execution_id,
|
| 279 |
+
"execution_time": execution_time
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
@tool
|
| 283 |
+
def generate_cash_flow_statement(file_path: str) -> Dict[str, Any]:
|
| 284 |
+
"""
|
| 285 |
+
Generate cash flow statement from trial balance file using complete pipeline
|
| 286 |
+
Args:
|
| 287 |
+
file_path: Path to trial balance Excel file
|
| 288 |
+
"""
|
| 289 |
+
execution_id = str(uuid.uuid4())[:8]
|
| 290 |
+
start_time = time.time()
|
| 291 |
+
tool_name = "generate_cash_flow_statement"
|
| 292 |
+
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
try:
|
| 296 |
+
# Use the complete CF pipeline from existing endpoint
|
| 297 |
+
env = os.environ.copy()
|
| 298 |
+
if os.getenv("OPENROUTER_API_KEY"):
|
| 299 |
+
env["OPENROUTER_API_KEY"] = os.getenv("OPENROUTER_API_KEY")
|
| 300 |
+
env["INPUT_FILE"] = "data/clean_financial_data_cfs.json"
|
| 301 |
+
cwd = os.getcwd()
|
| 302 |
+
|
| 303 |
+
# Step 1: Run cash_flow_data_extractor.py
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
result1 = subprocess.run(
|
| 307 |
+
["python", "cf/cash_flow_data_extractor.py", file_path],
|
| 308 |
+
env=env,
|
| 309 |
+
cwd=cwd,
|
| 310 |
+
capture_output=True,
|
| 311 |
+
text=True
|
| 312 |
+
)
|
| 313 |
+
|
| 314 |
+
if result1.returncode != 0:
|
| 315 |
+
|
| 316 |
+
return {"status": "error", "error": f"Cash flow data extraction failed: {result1.stderr}"}
|
| 317 |
+
|
| 318 |
+
# Step 2: Run cash_flow_csv_to_json_converter.py
|
| 319 |
+
|
| 320 |
+
|
| 321 |
+
result2 = subprocess.run(
|
| 322 |
+
["python", "cf/cash_flow_csv_to_json_converter.py"],
|
| 323 |
+
env=env,
|
| 324 |
+
cwd=cwd,
|
| 325 |
+
capture_output=True,
|
| 326 |
+
text=True
|
| 327 |
+
)
|
| 328 |
+
|
| 329 |
+
if result2.returncode != 0:
|
| 330 |
+
|
| 331 |
+
return {"status": "error", "error": f"Cash flow CSV to JSON conversion failed: {result2.stderr}"}
|
| 332 |
+
|
| 333 |
+
# Step 3: Run cash_flow_data_processor.py
|
| 334 |
+
|
| 335 |
+
|
| 336 |
+
result3 = subprocess.run(
|
| 337 |
+
["python", "cf/cash_flow_data_processor.py"],
|
| 338 |
+
env=env,
|
| 339 |
+
cwd=cwd,
|
| 340 |
+
capture_output=True,
|
| 341 |
+
text=True
|
| 342 |
+
)
|
| 343 |
+
|
| 344 |
+
if result3.returncode != 0:
|
| 345 |
+
|
| 346 |
+
return {"status": "error", "error": f"Cash flow data processing failed: {result3.stderr}"}
|
| 347 |
+
|
| 348 |
+
# Step 4: Run cash_flow_statement_generator.py
|
| 349 |
+
|
| 350 |
+
|
| 351 |
+
result4 = subprocess.run(
|
| 352 |
+
["python", "cf/cash_flow_statement_generator.py"],
|
| 353 |
+
env=env,
|
| 354 |
+
cwd=cwd,
|
| 355 |
+
capture_output=True,
|
| 356 |
+
text=True
|
| 357 |
+
)
|
| 358 |
+
|
| 359 |
+
if result4.returncode == 0:
|
| 360 |
+
execution_time = round(time.time() - start_time, 2)
|
| 361 |
+
output_path = "data/cash_flow_statements.xlsx"
|
| 362 |
+
|
| 363 |
+
return {
|
| 364 |
+
"status": "success",
|
| 365 |
+
"message": "Cash flow statement generated successfully",
|
| 366 |
+
"output_path": output_path,
|
| 367 |
+
"execution_id": execution_id,
|
| 368 |
+
"execution_time": execution_time
|
| 369 |
+
}
|
| 370 |
+
else:
|
| 371 |
+
execution_time = round(time.time() - start_time, 2)
|
| 372 |
+
|
| 373 |
+
return {
|
| 374 |
+
"status": "error",
|
| 375 |
+
"error": f"Cash flow generation failed: {result4.stderr}",
|
| 376 |
+
"execution_id": execution_id
|
| 377 |
+
}
|
| 378 |
+
|
| 379 |
+
except Exception as e:
|
| 380 |
+
execution_time = round(time.time() - start_time, 2)
|
| 381 |
+
|
| 382 |
+
return {
|
| 383 |
+
"status": "error",
|
| 384 |
+
"error": str(e),
|
| 385 |
+
"execution_id": execution_id,
|
| 386 |
+
"execution_time": execution_time
|
| 387 |
+
}
|
| 388 |
+
|
| 389 |
+
# Simplified tool list - only financial statement generation
|
| 390 |
+
FINANCIAL_TOOLS = [
|
| 391 |
+
generate_notes_from_trial_balance,
|
| 392 |
+
generate_balance_sheet,
|
| 393 |
+
generate_pnl_statement,
|
| 394 |
+
generate_cash_flow_statement
|
| 395 |
+
]
|
app.py
CHANGED
|
@@ -17,11 +17,13 @@ from notes.notes_generator import process_json
|
|
| 17 |
from notes.json_to_excel import json_to_xlsx
|
| 18 |
from utils.utils_normalize import normalize_llm_note_json, normalize_llm_notes_json
|
| 19 |
|
| 20 |
-
|
| 21 |
# Configure logging for the application
|
| 22 |
logging.basicConfig(level=logging.INFO)
|
| 23 |
logger = logging.getLogger("financial_notes_api")
|
| 24 |
|
|
|
|
|
|
|
|
|
|
| 25 |
app = FastAPI(
|
| 26 |
title="Financial Notes Generator API",
|
| 27 |
description="API for generating financial notes, balance sheets, cash flow statements, and P&L reports.",
|
|
@@ -213,14 +215,14 @@ async def bs_from_notes(file: UploadFile = File(...)):
|
|
| 213 |
env["OPENROUTER_API_KEY"] = os.getenv("OPENROUTER_API_KEY")
|
| 214 |
env["INPUT_FILE"] = "data/clean_financial_data_bs.json"
|
| 215 |
cwd = os.getenv("PROJECT_ROOT", os.getcwd())
|
| 216 |
-
# Run
|
| 217 |
-
run_subprocess("bs/
|
| 218 |
logger.info(f"Files in data/csv_notes_bs/: {os.listdir('data/csv_notes_bs') if os.path.exists('data/csv_notes_bs') else 'data/csv_notes_bs does not exist'}")
|
| 219 |
-
# Run
|
| 220 |
-
run_subprocess("bs/
|
| 221 |
logger.info(f"data/clean_financial_data_bs.json exists: {os.path.exists('data/clean_financial_data_bs.json')}")
|
| 222 |
-
# Run
|
| 223 |
-
result = run_subprocess("bs/
|
| 224 |
output_file = extract_output_file(result.stdout)
|
| 225 |
if output_file and not os.path.isabs(output_file):
|
| 226 |
output_file_path = os.path.join(cwd, output_file)
|
|
@@ -228,8 +230,8 @@ async def bs_from_notes(file: UploadFile = File(...)):
|
|
| 228 |
output_file_path = output_file
|
| 229 |
if not output_file or not os.path.exists(output_file_path):
|
| 230 |
debug_msg = f"\nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}"
|
| 231 |
-
logger.error(f"Could not determine output file from
|
| 232 |
-
raise HTTPException(status_code=500, detail=f"Could not determine output file from
|
| 233 |
logger.info(f"Pipeline completed. Output file: {output_file_path}")
|
| 234 |
return FileResponse(
|
| 235 |
output_file_path,
|
|
@@ -250,16 +252,16 @@ async def pnl_from_notes(file: UploadFile = File(...)):
|
|
| 250 |
env["OPENROUTER_API_KEY"] = os.getenv("OPENROUTER_API_KEY")
|
| 251 |
env["INPUT_FILE"] = "data/clean_financial_data_pnl.json"
|
| 252 |
cwd = os.getenv("PROJECT_ROOT", os.getcwd())
|
| 253 |
-
# Run
|
| 254 |
-
run_subprocess("pnl/
|
| 255 |
csv_notes_pnl_path = os.path.join(cwd, 'data/csv_notes_pnl')
|
| 256 |
logger.info(f"Files in {csv_notes_pnl_path}/: {os.listdir(csv_notes_pnl_path) if os.path.exists(csv_notes_pnl_path) else f'{csv_notes_pnl_path} does not exist'}")
|
| 257 |
-
# Run
|
| 258 |
-
run_subprocess("pnl/
|
| 259 |
json_path = os.path.join(cwd, 'data/clean_financial_data_pnl.json')
|
| 260 |
logger.info(f"data/clean_financial_data_pnl.json exists: {os.path.exists(json_path)}")
|
| 261 |
-
# Run
|
| 262 |
-
run_subprocess("pnl/
|
| 263 |
# Use fixed output file path
|
| 264 |
output_file_path = os.path.join(cwd, "data/pnl_statement.xlsx")
|
| 265 |
if not os.path.exists(output_file_path):
|
|
@@ -282,20 +284,20 @@ async def cf_from_notes(file: UploadFile = File(...)):
|
|
| 282 |
logger.info(f"Files in data/input/: {os.listdir('data/input')}")
|
| 283 |
env = os.environ.copy()
|
| 284 |
cwd = os.getenv("PROJECT_ROOT", os.getcwd())
|
| 285 |
-
# Step 1: Run
|
| 286 |
-
run_subprocess("cf/
|
| 287 |
csv_notes_cfs_path = os.path.join(cwd, 'data/csv_notes_cfs')
|
| 288 |
logger.info(f"Files in {csv_notes_cfs_path}/: {os.listdir(csv_notes_cfs_path) if os.path.exists(csv_notes_cfs_path) else f'{csv_notes_cfs_path} does not exist'}")
|
| 289 |
-
# Step 2: Run
|
| 290 |
-
run_subprocess("cf/
|
| 291 |
json_path = os.path.join(cwd, 'data/clean_financial_data_cfs.json')
|
| 292 |
logger.info(f"data/clean_financial_data_cfs.json exists: {os.path.exists(json_path)}")
|
| 293 |
-
# Step 3: Run
|
| 294 |
-
run_subprocess("cf/
|
| 295 |
extracted_json_path = os.path.join(cwd, 'data/extracted_cfs_data.json')
|
| 296 |
logger.info(f"data/extracted_cfs_data.json exists: {os.path.exists(extracted_json_path)}")
|
| 297 |
-
# Step 4: Run
|
| 298 |
-
result = run_subprocess("cf/
|
| 299 |
output_file = "data/cash_flow_statements.xlsx"
|
| 300 |
output_file_path = os.path.join(cwd, output_file)
|
| 301 |
if not os.path.exists(output_file_path):
|
|
@@ -311,6 +313,45 @@ async def cf_from_notes(file: UploadFile = File(...)):
|
|
| 311 |
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
| 312 |
)
|
| 313 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 314 |
app.include_router(router)
|
| 315 |
|
| 316 |
if __name__ == "__main__":
|
|
|
|
| 17 |
from notes.json_to_excel import json_to_xlsx
|
| 18 |
from utils.utils_normalize import normalize_llm_note_json, normalize_llm_notes_json
|
| 19 |
|
|
|
|
| 20 |
# Configure logging for the application
|
| 21 |
logging.basicConfig(level=logging.INFO)
|
| 22 |
logger = logging.getLogger("financial_notes_api")
|
| 23 |
|
| 24 |
+
# Import agent system
|
| 25 |
+
from agents.simple_agent import FinancialStatementAgent
|
| 26 |
+
|
| 27 |
app = FastAPI(
|
| 28 |
title="Financial Notes Generator API",
|
| 29 |
description="API for generating financial notes, balance sheets, cash flow statements, and P&L reports.",
|
|
|
|
| 215 |
env["OPENROUTER_API_KEY"] = os.getenv("OPENROUTER_API_KEY")
|
| 216 |
env["INPUT_FILE"] = "data/clean_financial_data_bs.json"
|
| 217 |
cwd = os.getenv("PROJECT_ROOT", os.getcwd())
|
| 218 |
+
# Run Balance Sheet Data Extractor
|
| 219 |
+
run_subprocess("bs/balance_sheet_data_extractor.py", [input_excel_path], env, cwd)
|
| 220 |
logger.info(f"Files in data/csv_notes_bs/: {os.listdir('data/csv_notes_bs') if os.path.exists('data/csv_notes_bs') else 'data/csv_notes_bs does not exist'}")
|
| 221 |
+
# Run Balance Sheet CSV to JSON Converter
|
| 222 |
+
run_subprocess("bs/balance_sheet_csv_to_json_converter.py", [], env, cwd)
|
| 223 |
logger.info(f"data/clean_financial_data_bs.json exists: {os.path.exists('data/clean_financial_data_bs.json')}")
|
| 224 |
+
# Run Balance Sheet Generator
|
| 225 |
+
result = run_subprocess("bs/balance_sheet_generator.py", [], env, cwd)
|
| 226 |
output_file = extract_output_file(result.stdout)
|
| 227 |
if output_file and not os.path.isabs(output_file):
|
| 228 |
output_file_path = os.path.join(cwd, output_file)
|
|
|
|
| 230 |
output_file_path = output_file
|
| 231 |
if not output_file or not os.path.exists(output_file_path):
|
| 232 |
debug_msg = f"\nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}"
|
| 233 |
+
logger.error(f"Could not determine output file from balance_sheet_generator.py output.{debug_msg}")
|
| 234 |
+
raise HTTPException(status_code=500, detail=f"Could not determine output file from balance_sheet_generator.py output.{debug_msg}")
|
| 235 |
logger.info(f"Pipeline completed. Output file: {output_file_path}")
|
| 236 |
return FileResponse(
|
| 237 |
output_file_path,
|
|
|
|
| 252 |
env["OPENROUTER_API_KEY"] = os.getenv("OPENROUTER_API_KEY")
|
| 253 |
env["INPUT_FILE"] = "data/clean_financial_data_pnl.json"
|
| 254 |
cwd = os.getenv("PROJECT_ROOT", os.getcwd())
|
| 255 |
+
# Run Profit & Loss Data Extractor
|
| 256 |
+
run_subprocess("pnl/profit_loss_data_extractor.py", [input_excel_path], env, cwd)
|
| 257 |
csv_notes_pnl_path = os.path.join(cwd, 'data/csv_notes_pnl')
|
| 258 |
logger.info(f"Files in {csv_notes_pnl_path}/: {os.listdir(csv_notes_pnl_path) if os.path.exists(csv_notes_pnl_path) else f'{csv_notes_pnl_path} does not exist'}")
|
| 259 |
+
# Run Profit & Loss CSV to JSON Converter
|
| 260 |
+
run_subprocess("pnl/profit_loss_csv_to_json_converter.py", [], env, cwd)
|
| 261 |
json_path = os.path.join(cwd, 'data/clean_financial_data_pnl.json')
|
| 262 |
logger.info(f"data/clean_financial_data_pnl.json exists: {os.path.exists(json_path)}")
|
| 263 |
+
# Run Profit & Loss Statement Generator
|
| 264 |
+
run_subprocess("pnl/profit_loss_statement_generator.py", [], env, cwd)
|
| 265 |
# Use fixed output file path
|
| 266 |
output_file_path = os.path.join(cwd, "data/pnl_statement.xlsx")
|
| 267 |
if not os.path.exists(output_file_path):
|
|
|
|
| 284 |
logger.info(f"Files in data/input/: {os.listdir('data/input')}")
|
| 285 |
env = os.environ.copy()
|
| 286 |
cwd = os.getenv("PROJECT_ROOT", os.getcwd())
|
| 287 |
+
# Step 1: Run Cash Flow Data Extractor
|
| 288 |
+
run_subprocess("cf/cash_flow_data_extractor.py", [input_excel_path], env, cwd)
|
| 289 |
csv_notes_cfs_path = os.path.join(cwd, 'data/csv_notes_cfs')
|
| 290 |
logger.info(f"Files in {csv_notes_cfs_path}/: {os.listdir(csv_notes_cfs_path) if os.path.exists(csv_notes_cfs_path) else f'{csv_notes_cfs_path} does not exist'}")
|
| 291 |
+
# Step 2: Run Cash Flow CSV to JSON Converter
|
| 292 |
+
run_subprocess("cf/cash_flow_csv_to_json_converter.py", [], env, cwd)
|
| 293 |
json_path = os.path.join(cwd, 'data/clean_financial_data_cfs.json')
|
| 294 |
logger.info(f"data/clean_financial_data_cfs.json exists: {os.path.exists(json_path)}")
|
| 295 |
+
# Step 3: Run Cash Flow Data Processor
|
| 296 |
+
run_subprocess("cf/cash_flow_data_processor.py", [], env, cwd)
|
| 297 |
extracted_json_path = os.path.join(cwd, 'data/extracted_cfs_data.json')
|
| 298 |
logger.info(f"data/extracted_cfs_data.json exists: {os.path.exists(extracted_json_path)}")
|
| 299 |
+
# Step 4: Run Cash Flow Statement Generator
|
| 300 |
+
result = run_subprocess("cf/cash_flow_statement_generator.py", [], env, cwd)
|
| 301 |
output_file = "data/cash_flow_statements.xlsx"
|
| 302 |
output_file_path = os.path.join(cwd, output_file)
|
| 303 |
if not os.path.exists(output_file_path):
|
|
|
|
| 313 |
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
| 314 |
)
|
| 315 |
|
| 316 |
+
@router.post("/agent/generate")
|
| 317 |
+
async def agent_generate_statements(
|
| 318 |
+
file: UploadFile = File(...),
|
| 319 |
+
note_numbers: str = Form(""),
|
| 320 |
+
statement_type: str = Form("all") # all, notes, balance_sheet, pnl, cash_flow
|
| 321 |
+
):
|
| 322 |
+
"""
|
| 323 |
+
Use AI agent to generate financial statements (Notes/BS/P&L/CF)
|
| 324 |
+
"""
|
| 325 |
+
try:
|
| 326 |
+
# Save uploaded file
|
| 327 |
+
upload_dir = "data/input"
|
| 328 |
+
os.makedirs(upload_dir, exist_ok=True)
|
| 329 |
+
file_path = os.path.join(upload_dir, file.filename)
|
| 330 |
+
|
| 331 |
+
with open(file_path, "wb") as buffer:
|
| 332 |
+
shutil.copyfileobj(file.file, buffer)
|
| 333 |
+
|
| 334 |
+
# Initialize financial statement agent
|
| 335 |
+
agent = FinancialStatementAgent()
|
| 336 |
+
|
| 337 |
+
# Generate requested statements
|
| 338 |
+
if statement_type == "all":
|
| 339 |
+
result = await agent.generate_all_statements(file_path, note_numbers)
|
| 340 |
+
else:
|
| 341 |
+
result = await agent.generate_specific_statement(file_path, statement_type, note_numbers)
|
| 342 |
+
|
| 343 |
+
return JSONResponse(content={
|
| 344 |
+
"message": f"Financial statement generation completed: {statement_type}",
|
| 345 |
+
"file_processed": file.filename,
|
| 346 |
+
"note_numbers": note_numbers,
|
| 347 |
+
"statement_type": statement_type,
|
| 348 |
+
"result": result
|
| 349 |
+
})
|
| 350 |
+
|
| 351 |
+
except Exception as e:
|
| 352 |
+
logger.error(f"Error in agent statement generation: {e}")
|
| 353 |
+
raise HTTPException(status_code=500, detail=f"Agent generation failed: {str(e)}")
|
| 354 |
+
|
| 355 |
app.include_router(router)
|
| 356 |
|
| 357 |
if __name__ == "__main__":
|
bs/{csv_json_bs.py → balance_sheet_csv_to_json_converter.py}
RENAMED
|
File without changes
|
bs/{sircodebs.py → balance_sheet_data_extractor.py}
RENAMED
|
File without changes
|
bs/{bl_llm.py → balance_sheet_generator.py}
RENAMED
|
File without changes
|
bs/{temp_bl.py → balance_sheet_template_handler.py}
RENAMED
|
File without changes
|
cf/{csv_json_cf.py → cash_flow_csv_to_json_converter.py}
RENAMED
|
File without changes
|
cf/{sircodecf.py → cash_flow_data_extractor.py}
RENAMED
|
File without changes
|
cf/{cf_middlestep.py → cash_flow_data_processor.py}
RENAMED
|
File without changes
|
cf/{cf_generation.py → cash_flow_statement_generator.py}
RENAMED
|
File without changes
|
docker-compose.yml
CHANGED
|
@@ -1,14 +1,37 @@
|
|
|
|
|
|
|
|
| 1 |
services:
|
| 2 |
-
|
| 3 |
build: .
|
| 4 |
-
container_name:
|
| 5 |
-
volumes:
|
| 6 |
-
- .:/app
|
| 7 |
ports:
|
| 8 |
- "8000:8000"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
environment:
|
| 10 |
- PYTHONUNBUFFERED=1
|
| 11 |
- PORT=8000
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
env_file:
|
| 13 |
- .env
|
| 14 |
-
restart: unless-stopped
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version: '3.8'
|
| 2 |
+
|
| 3 |
services:
|
| 4 |
+
finryver:
|
| 5 |
build: .
|
| 6 |
+
container_name: finryver-app
|
|
|
|
|
|
|
| 7 |
ports:
|
| 8 |
- "8000:8000"
|
| 9 |
+
volumes:
|
| 10 |
+
# Mount data directory for persistent storage
|
| 11 |
+
- ./data:/app/data
|
| 12 |
+
# Mount config directory for business rules
|
| 13 |
+
- ./config:/app/config
|
| 14 |
environment:
|
| 15 |
- PYTHONUNBUFFERED=1
|
| 16 |
- PORT=8000
|
| 17 |
+
# Default agent settings (override with .env file)
|
| 18 |
+
- AGENT_MODEL=gpt-3.5-turbo
|
| 19 |
+
- AGENT_TEMPERATURE=0.1
|
| 20 |
+
- AGENT_MAX_TOKENS=2000
|
| 21 |
env_file:
|
| 22 |
- .env
|
| 23 |
+
restart: unless-stopped
|
| 24 |
+
healthcheck:
|
| 25 |
+
test: ["CMD", "curl", "-f", "http://localhost:8000/docs"]
|
| 26 |
+
interval: 30s
|
| 27 |
+
timeout: 10s
|
| 28 |
+
retries: 3
|
| 29 |
+
start_period: 40s
|
| 30 |
+
|
| 31 |
+
# Optional: Add Redis for caching (future enhancement)
|
| 32 |
+
# redis:
|
| 33 |
+
# image: redis:7-alpine
|
| 34 |
+
# container_name: finryver-redis
|
| 35 |
+
# ports:
|
| 36 |
+
# - "6379:6379"
|
| 37 |
+
# restart: unless-stopped
|
pnl/{csv_json_pnl.py → profit_loss_csv_to_json_converter.py}
RENAMED
|
File without changes
|
pnl/{sircodepnl.py → profit_loss_data_extractor.py}
RENAMED
|
File without changes
|
pnl/{pnl_note.py → profit_loss_statement_generator.py}
RENAMED
|
File without changes
|
requirements.txt
CHANGED
|
@@ -6,4 +6,11 @@ python-dotenv
|
|
| 6 |
pydantic-settings
|
| 7 |
pydantic
|
| 8 |
requests
|
| 9 |
-
python-multipart
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
pydantic-settings
|
| 7 |
pydantic
|
| 8 |
requests
|
| 9 |
+
python-multipart
|
| 10 |
+
|
| 11 |
+
# LangChain dependencies
|
| 12 |
+
langchain
|
| 13 |
+
langchain-openai
|
| 14 |
+
langchain-community
|
| 15 |
+
langchain-core
|
| 16 |
+
langsmith
|