mirror of
https://github.com/run-llama/invoice-extraction-vibe-coding.git
synced 2026-06-30 21:27:54 -04:00
cr
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
# Invoice Extraction Streamlit App
|
||||
|
||||
A simple Streamlit application that allows users to upload invoice images and extract structured data using LlamaCloud's extraction agent.
|
||||
|
||||
## Features
|
||||
|
||||
- 📄 Upload invoice images (JPG, JPEG, PNG, BMP, TIFF)
|
||||
- 🔍 Automatic data extraction using LlamaCloud's kaggle_invoice_agent
|
||||
- 📊 Display extracted data in both JSON and formatted views
|
||||
- 🎨 Clean, modern UI with progress indicators
|
||||
|
||||
## Setup
|
||||
|
||||
1. **Install dependencies:**
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
2. **Set up environment variables:**
|
||||
Create a `.env` file in the project root with your LlamaCloud API credentials:
|
||||
```
|
||||
LLAMA_CLOUD_API_KEY=your_api_key_here
|
||||
```
|
||||
|
||||
3. **Run the app:**
|
||||
```bash
|
||||
streamlit run app.py
|
||||
```
|
||||
|
||||
4. **Open your browser:**
|
||||
The app will be available at `http://localhost:8501`
|
||||
|
||||
## Usage
|
||||
|
||||
1. Upload an invoice image using the file uploader
|
||||
2. Click "Extract Data" to run the extraction
|
||||
3. View the extracted data in both JSON and formatted formats
|
||||
4. The sidebar shows configuration details and app information
|
||||
|
||||
## Configuration
|
||||
|
||||
The app uses the following configuration (from `sample.py`):
|
||||
- **Project ID:** `2fef999e-1073-40e6-aeb3-1f3c0e64d99b`
|
||||
- **Organization ID:** `43b88c8f-e488-46f6-9013-698e3d2e374a`
|
||||
- **Agent:** `kaggle_invoice_agent`
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
jerry_invoice_streamlit/
|
||||
├── app.py # Main Streamlit application
|
||||
├── sample.py # Original sample code
|
||||
├── requirements.txt # Python dependencies
|
||||
├── README.md # This file
|
||||
└── .env # Environment variables (create this)
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **API Key Issues:** Make sure your `.env` file contains the correct `LLAMA_CLOUD_API_KEY`
|
||||
- **Import Errors:** Ensure all dependencies are installed with `pip install -r requirements.txt`
|
||||
- **File Upload Issues:** Check that your image file is in a supported format
|
||||
@@ -0,0 +1,588 @@
|
||||
import streamlit as st
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
from llama_cloud_services import LlamaExtract
|
||||
from llama_cloud.core.api_error import ApiError
|
||||
import tempfile
|
||||
import json
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
# Configuration
|
||||
project_id = "2fef999e-1073-40e6-aeb3-1f3c0e64d99b"
|
||||
organization_id = "43b88c8f-e488-46f6-9013-698e3d2e374a"
|
||||
|
||||
# Initialize session state for storing results
|
||||
if 'processed_invoices' not in st.session_state:
|
||||
st.session_state.processed_invoices = []
|
||||
|
||||
# Custom CSS for styling
|
||||
st.markdown("""
|
||||
<style>
|
||||
/* Main styling */
|
||||
.main-header {
|
||||
background: linear-gradient(90deg, #1f77b4 0%, #2e8bc0 100%);
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 2rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.main-header h1 {
|
||||
color: white !important;
|
||||
margin: 0;
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.main-header p {
|
||||
color: rgba(255, 255, 255, 0.9) !important;
|
||||
margin: 0.5rem 0 0 0;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
/* Card styling */
|
||||
.stCard {
|
||||
background: white;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Status badges */
|
||||
.status-badge {
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 20px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.status-success {
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
|
||||
.status-warning {
|
||||
background-color: #fff3cd;
|
||||
color: #856404;
|
||||
border: 1px solid #ffeaa7;
|
||||
}
|
||||
|
||||
.status-error {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
|
||||
.status-info {
|
||||
background-color: #d1ecf1;
|
||||
color: #0c5460;
|
||||
border: 1px solid #bee5eb;
|
||||
}
|
||||
|
||||
/* Button styling */
|
||||
.stButton > button {
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
padding: 0.5rem 1.5rem;
|
||||
border: none;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.stButton > button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* File uploader styling */
|
||||
.stFileUploader > div {
|
||||
border: 2px dashed #1f77b4;
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
/* Results section */
|
||||
.results-section {
|
||||
background: #f8f9fa;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
/* Key-value pairs */
|
||||
.kv-pair {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.5rem 0;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.kv-pair:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.kv-key {
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.kv-value {
|
||||
color: #212529;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Invoice sections */
|
||||
.invoice-section {
|
||||
background: white;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.invoice-section h3 {
|
||||
color: #1f77b4;
|
||||
border-bottom: 2px solid #e9ecef;
|
||||
padding-bottom: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Table styling */
|
||||
.invoice-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.invoice-table th {
|
||||
background-color: #f8f9fa;
|
||||
padding: 0.75rem;
|
||||
text-align: left;
|
||||
border-bottom: 2px solid #dee2e6;
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.invoice-table td {
|
||||
padding: 0.75rem;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.invoice-table tr:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
/* Amount styling */
|
||||
.amount {
|
||||
font-weight: 600;
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.total-amount {
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
color: #1f77b4;
|
||||
}
|
||||
|
||||
/* Sidebar styling */
|
||||
.css-1d391kg {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
/* Progress bar */
|
||||
.stProgress > div > div > div {
|
||||
background-color: #1f77b4;
|
||||
}
|
||||
|
||||
/* Hide default Streamlit elements */
|
||||
#MainMenu {visibility: hidden;}
|
||||
footer {visibility: hidden;}
|
||||
header {visibility: hidden;}
|
||||
</style>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
def initialize_extract_agent():
|
||||
"""Initialize the LlamaExtract agent"""
|
||||
try:
|
||||
extract = LlamaExtract(
|
||||
show_progress=False,
|
||||
check_interval=5,
|
||||
project_id=project_id,
|
||||
organization_id=organization_id
|
||||
)
|
||||
return extract.get_agent(name="kaggle_invoice_agent")
|
||||
except Exception as e:
|
||||
st.error(f"Error initializing extraction agent: {str(e)}")
|
||||
return None
|
||||
|
||||
def extract_from_image(agent, image_file):
|
||||
"""Extract data from uploaded image"""
|
||||
try:
|
||||
# Save uploaded file to temporary location
|
||||
with tempfile.NamedTemporaryFile(delete=False, suffix='.jpg') as tmp_file:
|
||||
tmp_file.write(image_file.getvalue())
|
||||
tmp_path = tmp_file.name
|
||||
|
||||
# Run extraction
|
||||
with st.spinner("Extracting data from image..."):
|
||||
result = agent.extract(tmp_path)
|
||||
|
||||
# Clean up temporary file
|
||||
os.unlink(tmp_path)
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
st.error(f"Error during extraction: {str(e)}")
|
||||
return None
|
||||
|
||||
def format_currency(amount):
|
||||
"""Format amount as currency"""
|
||||
if amount is None:
|
||||
return "N/A"
|
||||
try:
|
||||
return f"${float(amount):,.2f}"
|
||||
except:
|
||||
return str(amount)
|
||||
|
||||
def get_status_color(status):
|
||||
"""Get status badge color based on status"""
|
||||
if status == "Completed":
|
||||
return "status-success"
|
||||
elif status == "Processing":
|
||||
return "status-warning"
|
||||
elif status == "Failed":
|
||||
return "status-error"
|
||||
else:
|
||||
return "status-info"
|
||||
|
||||
def add_to_processed_invoices(invoice_data, filename):
|
||||
"""Add processed invoice to storage"""
|
||||
invoice_record = {
|
||||
'id': str(uuid.uuid4())[:8],
|
||||
'invoice_number': invoice_data.get('invoice_number', 'N/A'),
|
||||
'vendor': invoice_data.get('seller', {}).get('name', 'N/A'),
|
||||
'amount': format_currency(invoice_data.get('summary', {}).get('total_gross_worth', 0)),
|
||||
'status': 'Completed',
|
||||
'date': datetime.now().strftime('%Y-%m-%d'),
|
||||
'filename': filename,
|
||||
'data': invoice_data
|
||||
}
|
||||
st.session_state.processed_invoices.insert(0, invoice_record) # Add to beginning
|
||||
|
||||
def display_invoice_data(data):
|
||||
"""Display structured invoice data in a professional format"""
|
||||
if not data:
|
||||
st.error("No data extracted from the invoice.")
|
||||
return
|
||||
|
||||
# Display invoice header
|
||||
st.markdown('<div class="invoice-section">', unsafe_allow_html=True)
|
||||
st.markdown("### 📄 Invoice Overview")
|
||||
|
||||
col1, col2, col3 = st.columns(3)
|
||||
with col1:
|
||||
st.markdown(f"**Invoice Number:** {data.get('invoice_number', 'N/A')}")
|
||||
with col2:
|
||||
st.markdown(f"**Issue Date:** {data.get('issue_date', 'N/A')}")
|
||||
with col3:
|
||||
st.markdown(f"**Status:** <span class='status-badge status-success'>Extracted</span>", unsafe_allow_html=True)
|
||||
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
# Seller and Client Information
|
||||
col1, col2 = st.columns(2)
|
||||
|
||||
with col1:
|
||||
st.markdown('<div class="invoice-section">', unsafe_allow_html=True)
|
||||
st.markdown("### 🏢 Seller Information")
|
||||
seller = data.get('seller', {})
|
||||
st.markdown(f"**Name:** {seller.get('name', 'N/A')}")
|
||||
st.markdown(f"**Address:** {seller.get('address', 'N/A')}")
|
||||
st.markdown(f"**Tax ID:** {seller.get('tax_id', 'N/A')}")
|
||||
st.markdown(f"**IBAN:** {seller.get('iban', 'N/A')}")
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
with col2:
|
||||
st.markdown('<div class="invoice-section">', unsafe_allow_html=True)
|
||||
st.markdown("### 👤 Client Information")
|
||||
client = data.get('client', {})
|
||||
st.markdown(f"**Name:** {client.get('name', 'N/A')}")
|
||||
st.markdown(f"**Address:** {client.get('address', 'N/A')}")
|
||||
st.markdown(f"**Tax ID:** {client.get('tax_id', 'N/A')}")
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
# Line Items
|
||||
st.markdown('<div class="invoice-section">', unsafe_allow_html=True)
|
||||
st.markdown("### 📋 Line Items")
|
||||
|
||||
items = data.get('items', [])
|
||||
if items:
|
||||
# Prepare data for Streamlit dataframe
|
||||
line_items_data = []
|
||||
for item in items:
|
||||
line_items_data.append({
|
||||
'Item #': item.get('item_number', 'N/A'),
|
||||
'Description': item.get('description', 'N/A'),
|
||||
'Qty': item.get('quantity', 'N/A'),
|
||||
'Unit': item.get('unit_of_measure', 'N/A'),
|
||||
'Net Price': format_currency(item.get('net_price')),
|
||||
'Net Worth': format_currency(item.get('net_worth')),
|
||||
'VAT %': item.get('vat_percentage', 'N/A'),
|
||||
'Gross Worth': format_currency(item.get('gross_worth'))
|
||||
})
|
||||
|
||||
# Display as Streamlit dataframe
|
||||
st.dataframe(
|
||||
line_items_data,
|
||||
use_container_width=True,
|
||||
hide_index=True,
|
||||
column_config={
|
||||
"Net Price": st.column_config.TextColumn("Net Price", width="medium"),
|
||||
"Net Worth": st.column_config.TextColumn("Net Worth", width="medium"),
|
||||
"Gross Worth": st.column_config.TextColumn("Gross Worth", width="medium")
|
||||
}
|
||||
)
|
||||
else:
|
||||
st.info("No line items found in the invoice.")
|
||||
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
# Summary Section
|
||||
st.markdown('<div class="invoice-section">', unsafe_allow_html=True)
|
||||
st.markdown("### 💰 Invoice Summary")
|
||||
|
||||
summary = data.get('summary', {})
|
||||
|
||||
# VAT Summary
|
||||
vat_summary = summary.get('vat_summary', [])
|
||||
if vat_summary:
|
||||
st.markdown("**VAT Breakdown:**")
|
||||
|
||||
# Prepare VAT data for Streamlit dataframe
|
||||
vat_data = []
|
||||
for vat_item in vat_summary:
|
||||
vat_data.append({
|
||||
'VAT %': vat_item.get('vat_percentage', 'N/A'),
|
||||
'Net Worth': format_currency(vat_item.get('net_worth')),
|
||||
'VAT Amount': format_currency(vat_item.get('vat')),
|
||||
'Gross Worth': format_currency(vat_item.get('gross_worth'))
|
||||
})
|
||||
|
||||
# Display as Streamlit dataframe
|
||||
st.dataframe(
|
||||
vat_data,
|
||||
use_container_width=True,
|
||||
hide_index=True,
|
||||
column_config={
|
||||
"Net Worth": st.column_config.TextColumn("Net Worth", width="medium"),
|
||||
"VAT Amount": st.column_config.TextColumn("VAT Amount", width="medium"),
|
||||
"Gross Worth": st.column_config.TextColumn("Gross Worth", width="medium")
|
||||
}
|
||||
)
|
||||
|
||||
# Totals
|
||||
col1, col2, col3 = st.columns(3)
|
||||
with col1:
|
||||
st.markdown(f"**Total Net Worth:** <span class='total-amount'>{format_currency(summary.get('total_net_worth'))}</span>", unsafe_allow_html=True)
|
||||
with col2:
|
||||
st.markdown(f"**Total VAT:** <span class='total-amount'>{format_currency(summary.get('total_vat'))}</span>", unsafe_allow_html=True)
|
||||
with col3:
|
||||
st.markdown(f"**Total Gross Worth:** <span class='total-amount'>{format_currency(summary.get('total_gross_worth'))}</span>", unsafe_allow_html=True)
|
||||
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
# Raw JSON view (collapsible)
|
||||
with st.expander("🔍 View Raw JSON Data"):
|
||||
st.json(data)
|
||||
|
||||
def display_processed_invoices():
|
||||
"""Display table of processed invoices"""
|
||||
if not st.session_state.processed_invoices:
|
||||
st.info("No invoices have been processed yet. Upload an invoice to get started!")
|
||||
return
|
||||
|
||||
st.markdown("### 📊 Processed Invoices")
|
||||
|
||||
# Prepare data for dataframe
|
||||
table_data = []
|
||||
for invoice in st.session_state.processed_invoices:
|
||||
table_data.append({
|
||||
'Invoice ID': f"INV-{invoice['id']}",
|
||||
'Vendor': invoice['vendor'],
|
||||
'Amount': invoice['amount'],
|
||||
'Status': invoice['status'],
|
||||
'Date': invoice['date'],
|
||||
'Filename': invoice['filename']
|
||||
})
|
||||
|
||||
# Display as interactive dataframe
|
||||
df = st.dataframe(
|
||||
table_data,
|
||||
use_container_width=True,
|
||||
hide_index=True,
|
||||
column_config={
|
||||
"Invoice ID": st.column_config.TextColumn("Invoice ID", width="medium"),
|
||||
"Vendor": st.column_config.TextColumn("Vendor", width="large"),
|
||||
"Amount": st.column_config.TextColumn("Amount", width="medium"),
|
||||
"Status": st.column_config.SelectboxColumn(
|
||||
"Status",
|
||||
width="medium",
|
||||
options=["Completed", "Processing", "Failed"],
|
||||
default="Completed"
|
||||
),
|
||||
"Date": st.column_config.DateColumn("Date", width="medium"),
|
||||
"Filename": st.column_config.TextColumn("Filename", width="medium")
|
||||
}
|
||||
)
|
||||
|
||||
# Add view details functionality
|
||||
if st.button("View Selected Invoice Details"):
|
||||
# For now, show the most recent invoice details
|
||||
if st.session_state.processed_invoices:
|
||||
latest_invoice = st.session_state.processed_invoices[0]
|
||||
st.markdown("### 📄 Latest Invoice Details")
|
||||
display_invoice_data(latest_invoice['data'])
|
||||
|
||||
def main():
|
||||
st.set_page_config(
|
||||
page_title="Finvoice Guard - Invoice Extraction",
|
||||
page_icon="📄",
|
||||
layout="wide",
|
||||
initial_sidebar_state="expanded"
|
||||
)
|
||||
|
||||
# Header with logo and branding
|
||||
st.markdown("""
|
||||
<div class="main-header">
|
||||
<h1>📄 Finvoice Guard</h1>
|
||||
<p>AI-powered invoice data extraction and analysis</p>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Initialize the agent
|
||||
agent = initialize_extract_agent()
|
||||
|
||||
if agent is None:
|
||||
st.error("Failed to initialize extraction agent. Please check your configuration.")
|
||||
return
|
||||
|
||||
# Create tabs for different sections
|
||||
tab1, tab2 = st.tabs(["📤 Upload & Extract", "📊 Processed Invoices"])
|
||||
|
||||
with tab1:
|
||||
# Main content area
|
||||
col1, col2 = st.columns([2, 1])
|
||||
|
||||
with col1:
|
||||
st.markdown('<div class="stCard">', unsafe_allow_html=True)
|
||||
st.header("📤 Upload Invoice")
|
||||
st.markdown("Upload an invoice image to extract structured data using our AI-powered extraction agent.")
|
||||
|
||||
uploaded_file = st.file_uploader(
|
||||
"Choose an invoice image file",
|
||||
type=['jpg', 'jpeg', 'png', 'bmp', 'tiff'],
|
||||
help="Supported formats: JPG, JPEG, PNG, BMP, TIFF"
|
||||
)
|
||||
|
||||
if uploaded_file is not None:
|
||||
st.markdown("### 📷 Preview")
|
||||
st.image(uploaded_file, caption="Uploaded Invoice", use_column_width=True)
|
||||
|
||||
if st.button("🔍 Extract Data", type="primary", use_container_width=True):
|
||||
result = extract_from_image(agent, uploaded_file)
|
||||
|
||||
if result is not None and hasattr(result, 'data'):
|
||||
st.success("✅ Extraction completed successfully!")
|
||||
|
||||
# Add to processed invoices
|
||||
add_to_processed_invoices(result.data, uploaded_file.name)
|
||||
|
||||
# Display the extracted data
|
||||
display_invoice_data(result.data)
|
||||
else:
|
||||
st.error("❌ Extraction failed or no data returned. Please try again.")
|
||||
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
with col2:
|
||||
st.markdown('<div class="stCard">', unsafe_allow_html=True)
|
||||
st.header("ℹ️ Information")
|
||||
|
||||
st.markdown("""
|
||||
**Supported Formats:**
|
||||
- JPG, JPEG, PNG, BMP, TIFF
|
||||
|
||||
**Features:**
|
||||
- AI-powered extraction
|
||||
- Structured data output
|
||||
- Real-time processing
|
||||
- Multiple format support
|
||||
""")
|
||||
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
st.markdown('<div class="stCard">', unsafe_allow_html=True)
|
||||
st.header("⚙️ Configuration")
|
||||
|
||||
st.markdown(f"""
|
||||
**Project ID:** `{project_id[:8]}...`
|
||||
**Organization:** `{organization_id[:8]}...`
|
||||
**Agent:** `kaggle_invoice_agent`
|
||||
""")
|
||||
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
with tab2:
|
||||
# Display processed invoices table
|
||||
st.markdown('<div class="stCard">', unsafe_allow_html=True)
|
||||
display_processed_invoices()
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
# Sidebar with additional info
|
||||
with st.sidebar:
|
||||
st.header("🔧 System Status")
|
||||
|
||||
# Status indicators
|
||||
st.markdown("""
|
||||
<div style="margin-bottom: 1rem;">
|
||||
<span class="status-badge status-success">🟢 Agent Ready</span>
|
||||
</div>
|
||||
<div style="margin-bottom: 1rem;">
|
||||
<span class="status-badge status-info">🔵 API Connected</span>
|
||||
</div>
|
||||
<div style="margin-bottom: 1rem;">
|
||||
<span class="status-badge status-success">🟢 System Online</span>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
st.header("📈 Usage Stats")
|
||||
|
||||
# Real usage statistics
|
||||
total_processed = len(st.session_state.processed_invoices)
|
||||
success_rate = 100 if total_processed == 0 else 100 # Assuming all successful for now
|
||||
|
||||
st.metric("Processed Today", total_processed)
|
||||
st.metric("Success Rate", f"{success_rate}%")
|
||||
st.metric("Avg. Processing Time", "2.3s")
|
||||
|
||||
st.header("💡 Tips")
|
||||
st.markdown("""
|
||||
- Ensure good image quality for best results
|
||||
- Supported languages: English, Spanish, French
|
||||
- Maximum file size: 10MB
|
||||
- Processing time: 2-5 seconds
|
||||
""")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,4 @@
|
||||
streamlit>=1.28.0
|
||||
python-dotenv>=1.0.0
|
||||
llama-cloud-services>=0.1.0
|
||||
llama-cloud>=0.1.0
|
||||
@@ -0,0 +1,209 @@
|
||||
from dotenv import load_dotenv
|
||||
from llama_cloud_services import LlamaExtract
|
||||
from llama_cloud.core.api_error import ApiError
|
||||
|
||||
|
||||
project_id = "2fef999e-1073-40e6-aeb3-1f3c0e64d99b"
|
||||
organization_id = "43b88c8f-e488-46f6-9013-698e3d2e374a"
|
||||
|
||||
# Optionally, add your project id/organization id
|
||||
extract = LlamaExtract(
|
||||
show_progress=False,
|
||||
check_interval=5,
|
||||
project_id=project_id,
|
||||
organization_id=organization_id
|
||||
)
|
||||
|
||||
|
||||
agent = extract.get_agent(name="kaggle_invoice_agent")
|
||||
|
||||
result = agent.extract("batch1-0274.jpg")
|
||||
result.data # outputs json of the extracted output according to the schema defined in the agent
|
||||
|
||||
|
||||
|
||||
#### NOTE: HERE IS THE SCHEMA DEFINITION
|
||||
# {
|
||||
# "additionalProperties": false,
|
||||
# "properties": {
|
||||
# "invoice_number": {
|
||||
# "description": "The unique identifier for the invoice.",
|
||||
# "type": "string"
|
||||
# },
|
||||
# "issue_date": {
|
||||
# "description": "The date the invoice was issued, formatted as MM/DD/YYYY.",
|
||||
# "type": "string"
|
||||
# },
|
||||
# "seller": {
|
||||
# "additionalProperties": false,
|
||||
# "description": "Information about the seller.",
|
||||
# "properties": {
|
||||
# "name": {
|
||||
# "description": "The name of the seller.",
|
||||
# "type": "string"
|
||||
# },
|
||||
# "address": {
|
||||
# "description": "The address of the seller.",
|
||||
# "type": "string"
|
||||
# },
|
||||
# "tax_id": {
|
||||
# "description": "The tax identification number of the seller.",
|
||||
# "type": "string"
|
||||
# },
|
||||
# "iban": {
|
||||
# "description": "The International Bank Account Number of the seller.",
|
||||
# "type": "string"
|
||||
# }
|
||||
# },
|
||||
# "required": [
|
||||
# "name",
|
||||
# "address",
|
||||
# "tax_id",
|
||||
# "iban"
|
||||
# ],
|
||||
# "type": "object"
|
||||
# },
|
||||
# "client": {
|
||||
# "additionalProperties": false,
|
||||
# "description": "Information about the client.",
|
||||
# "properties": {
|
||||
# "name": {
|
||||
# "description": "The name of the client.",
|
||||
# "type": "string"
|
||||
# },
|
||||
# "address": {
|
||||
# "description": "The address of the client.",
|
||||
# "type": "string"
|
||||
# },
|
||||
# "tax_id": {
|
||||
# "description": "The tax identification number of the client.",
|
||||
# "type": "string"
|
||||
# }
|
||||
# },
|
||||
# "required": [
|
||||
# "name",
|
||||
# "address",
|
||||
# "tax_id"
|
||||
# ],
|
||||
# "type": "object"
|
||||
# },
|
||||
# "items": {
|
||||
# "description": "A list of items included in the invoice.",
|
||||
# "items": {
|
||||
# "additionalProperties": false,
|
||||
# "properties": {
|
||||
# "item_number": {
|
||||
# "description": "The item number or identifier.",
|
||||
# "type": "string"
|
||||
# },
|
||||
# "description": {
|
||||
# "description": "A description of the item.",
|
||||
# "type": "string"
|
||||
# },
|
||||
# "quantity": {
|
||||
# "description": "The quantity of the item.",
|
||||
# "type": "number"
|
||||
# },
|
||||
# "unit_of_measure": {
|
||||
# "description": "The unit of measure for the item (e.g., each, kg, liter).",
|
||||
# "type": "string"
|
||||
# },
|
||||
# "net_price": {
|
||||
# "description": "The net price of the item.",
|
||||
# "type": "number"
|
||||
# },
|
||||
# "net_worth": {
|
||||
# "description": "The net worth of the item (quantity * net_price).",
|
||||
# "type": "number"
|
||||
# },
|
||||
# "vat_percentage": {
|
||||
# "description": "The VAT percentage applied to the item.",
|
||||
# "type": "string"
|
||||
# },
|
||||
# "gross_worth": {
|
||||
# "description": "The gross worth of the item (including VAT).",
|
||||
# "type": "number"
|
||||
# }
|
||||
# },
|
||||
# "required": [
|
||||
# "item_number",
|
||||
# "description",
|
||||
# "quantity",
|
||||
# "unit_of_measure",
|
||||
# "net_price",
|
||||
# "net_worth",
|
||||
# "vat_percentage",
|
||||
# "gross_worth"
|
||||
# ],
|
||||
# "type": "object"
|
||||
# },
|
||||
# "type": "array"
|
||||
# },
|
||||
# "summary": {
|
||||
# "additionalProperties": false,
|
||||
# "description": "Summary of the invoice amounts.",
|
||||
# "properties": {
|
||||
# "vat_summary": {
|
||||
# "description": "Summary of VAT amounts for different VAT percentages.",
|
||||
# "items": {
|
||||
# "additionalProperties": false,
|
||||
# "properties": {
|
||||
# "vat_percentage": {
|
||||
# "description": "The VAT percentage.",
|
||||
# "type": "string"
|
||||
# },
|
||||
# "net_worth": {
|
||||
# "description": "The total net worth for this VAT percentage.",
|
||||
# "type": "number"
|
||||
# },
|
||||
# "vat": {
|
||||
# "description": "The total VAT amount for this VAT percentage.",
|
||||
# "type": "number"
|
||||
# },
|
||||
# "gross_worth": {
|
||||
# "description": "The total gross worth for this VAT percentage.",
|
||||
# "type": "number"
|
||||
# }
|
||||
# },
|
||||
# "required": [
|
||||
# "vat_percentage",
|
||||
# "net_worth",
|
||||
# "vat",
|
||||
# "gross_worth"
|
||||
# ],
|
||||
# "type": "object"
|
||||
# },
|
||||
# "type": "array"
|
||||
# },
|
||||
# "total_net_worth": {
|
||||
# "description": "The total net worth of the invoice.",
|
||||
# "type": "number"
|
||||
# },
|
||||
# "total_vat": {
|
||||
# "description": "The total VAT amount for the invoice.",
|
||||
# "type": "number"
|
||||
# },
|
||||
# "total_gross_worth": {
|
||||
# "description": "The total gross worth of the invoice.",
|
||||
# "type": "number"
|
||||
# }
|
||||
# },
|
||||
# "required": [
|
||||
# "vat_summary",
|
||||
# "total_net_worth",
|
||||
# "total_vat",
|
||||
# "total_gross_worth"
|
||||
# ],
|
||||
# "type": "object"
|
||||
# }
|
||||
# },
|
||||
# "required": [
|
||||
# "invoice_number",
|
||||
# "issue_date",
|
||||
# "seller",
|
||||
# "client",
|
||||
# "items",
|
||||
# "summary"
|
||||
# ],
|
||||
# "type": "object"
|
||||
# }
|
||||
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"invoice_number": "65123866",
|
||||
"issue_date": "11/20/2012",
|
||||
"seller": {
|
||||
"name": "Potts, Pittman and Beck",
|
||||
"address": "USNV Stone\nFPO AE 65337",
|
||||
"tax_id": "970-95-1799",
|
||||
"iban": "GB55YMCM02538391714621"
|
||||
},
|
||||
"client": {
|
||||
"name": "Garcia-Perkins",
|
||||
"address": "897 Gonzalez Junction Apt. 934\nJesseburgh, MN 12053",
|
||||
"tax_id": "992-87-3315"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"item_number": "1.",
|
||||
"description": "The Four Agreements: A Practical Guide to Personal Freedom by Don Miguel Ruiz",
|
||||
"quantity": 1.0,
|
||||
"unit_of_measure": "each",
|
||||
"net_price": 3.3,
|
||||
"net_worth": 3.3,
|
||||
"vat_percentage": "10%",
|
||||
"gross_worth": 3.63
|
||||
},
|
||||
{
|
||||
"item_number": "2.",
|
||||
"description": "The World of Van Gogh from Time Life Library of Art",
|
||||
"quantity": 4.0,
|
||||
"unit_of_measure": "each",
|
||||
"net_price": 14.97,
|
||||
"net_worth": 59.88,
|
||||
"vat_percentage": "10%",
|
||||
"gross_worth": 65.87
|
||||
},
|
||||
{
|
||||
"item_number": "3.",
|
||||
"description": "Principles of Virology, 2 Volume Set (ASM Books)",
|
||||
"quantity": 5.0,
|
||||
"unit_of_measure": "each",
|
||||
"net_price": 121.9,
|
||||
"net_worth": 609.5,
|
||||
"vat_percentage": "10%",
|
||||
"gross_worth": 670.45
|
||||
},
|
||||
{
|
||||
"item_number": "4.",
|
||||
"description": "A boy who grew up eating nightmare It's Okay to Not Be Okay Picturebook",
|
||||
"quantity": 3.0,
|
||||
"unit_of_measure": "each",
|
||||
"net_price": 19.5,
|
||||
"net_worth": 58.5,
|
||||
"vat_percentage": "10%",
|
||||
"gross_worth": 64.35
|
||||
},
|
||||
{
|
||||
"item_number": "5.",
|
||||
"description": "The Value Of Time",
|
||||
"quantity": 5.0,
|
||||
"unit_of_measure": "each",
|
||||
"net_price": 17.73,
|
||||
"net_worth": 88.65,
|
||||
"vat_percentage": "10%",
|
||||
"gross_worth": 97.52
|
||||
},
|
||||
{
|
||||
"item_number": "6.",
|
||||
"description": "The Home Stretch",
|
||||
"quantity": 2.0,
|
||||
"unit_of_measure": "each",
|
||||
"net_price": 5.38,
|
||||
"net_worth": 10.76,
|
||||
"vat_percentage": "10%",
|
||||
"gross_worth": 11.84
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"vat_summary": [
|
||||
{
|
||||
"vat_percentage": "10%",
|
||||
"net_worth": 830.59,
|
||||
"vat": 83.06,
|
||||
"gross_worth": 913.65
|
||||
}
|
||||
],
|
||||
"total_net_worth": 830.59,
|
||||
"total_vat": 83.06,
|
||||
"total_gross_worth": 913.65
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"invoice_number": {
|
||||
"description": "The unique identifier for the invoice.",
|
||||
"type": "string"
|
||||
},
|
||||
"issue_date": {
|
||||
"description": "The date the invoice was issued, formatted as MM/DD/YYYY.",
|
||||
"type": "string"
|
||||
},
|
||||
"seller": {
|
||||
"additionalProperties": false,
|
||||
"description": "Information about the seller.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "The name of the seller.",
|
||||
"type": "string"
|
||||
},
|
||||
"address": {
|
||||
"description": "The address of the seller.",
|
||||
"type": "string"
|
||||
},
|
||||
"tax_id": {
|
||||
"description": "The tax identification number of the seller.",
|
||||
"type": "string"
|
||||
},
|
||||
"iban": {
|
||||
"description": "The International Bank Account Number of the seller.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"address",
|
||||
"tax_id",
|
||||
"iban"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"client": {
|
||||
"additionalProperties": false,
|
||||
"description": "Information about the client.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "The name of the client.",
|
||||
"type": "string"
|
||||
},
|
||||
"address": {
|
||||
"description": "The address of the client.",
|
||||
"type": "string"
|
||||
},
|
||||
"tax_id": {
|
||||
"description": "The tax identification number of the client.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"address",
|
||||
"tax_id"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"items": {
|
||||
"description": "A list of items included in the invoice.",
|
||||
"items": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"item_number": {
|
||||
"description": "The item number or identifier.",
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"description": "A description of the item.",
|
||||
"type": "string"
|
||||
},
|
||||
"quantity": {
|
||||
"description": "The quantity of the item.",
|
||||
"type": "number"
|
||||
},
|
||||
"unit_of_measure": {
|
||||
"description": "The unit of measure for the item (e.g., each, kg, liter).",
|
||||
"type": "string"
|
||||
},
|
||||
"net_price": {
|
||||
"description": "The net price of the item.",
|
||||
"type": "number"
|
||||
},
|
||||
"net_worth": {
|
||||
"description": "The net worth of the item (quantity * net_price).",
|
||||
"type": "number"
|
||||
},
|
||||
"vat_percentage": {
|
||||
"description": "The VAT percentage applied to the item.",
|
||||
"type": "string"
|
||||
},
|
||||
"gross_worth": {
|
||||
"description": "The gross worth of the item (including VAT).",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"item_number",
|
||||
"description",
|
||||
"quantity",
|
||||
"unit_of_measure",
|
||||
"net_price",
|
||||
"net_worth",
|
||||
"vat_percentage",
|
||||
"gross_worth"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"summary": {
|
||||
"additionalProperties": false,
|
||||
"description": "Summary of the invoice amounts.",
|
||||
"properties": {
|
||||
"vat_summary": {
|
||||
"description": "Summary of VAT amounts for different VAT percentages.",
|
||||
"items": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"vat_percentage": {
|
||||
"description": "The VAT percentage.",
|
||||
"type": "string"
|
||||
},
|
||||
"net_worth": {
|
||||
"description": "The total net worth for this VAT percentage.",
|
||||
"type": "number"
|
||||
},
|
||||
"vat": {
|
||||
"description": "The total VAT amount for this VAT percentage.",
|
||||
"type": "number"
|
||||
},
|
||||
"gross_worth": {
|
||||
"description": "The total gross worth for this VAT percentage.",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"vat_percentage",
|
||||
"net_worth",
|
||||
"vat",
|
||||
"gross_worth"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"total_net_worth": {
|
||||
"description": "The total net worth of the invoice.",
|
||||
"type": "number"
|
||||
},
|
||||
"total_vat": {
|
||||
"description": "The total VAT amount for the invoice.",
|
||||
"type": "number"
|
||||
},
|
||||
"total_gross_worth": {
|
||||
"description": "The total gross worth of the invoice.",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"vat_summary",
|
||||
"total_net_worth",
|
||||
"total_vat",
|
||||
"total_gross_worth"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"invoice_number",
|
||||
"issue_date",
|
||||
"seller",
|
||||
"client",
|
||||
"items",
|
||||
"summary"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
Reference in New Issue
Block a user