File size: 4,727 Bytes
715c3e9
 
6f317bd
715c3e9
 
 
 
6f317bd
8fb878c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4d66af2
 
 
 
 
 
 
834b45f
 
 
 
 
 
 
12e951c
834b45f
 
 
 
 
 
4d66af2
8fb878c
 
 
 
 
 
 
 
 
 
 
 
 
4d66af2
8fb878c
 
 
 
 
 
 
 
834b45f
8fb878c
 
 
d4d78d9
 
 
 
 
8fb878c
d4d78d9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import os

# Force Streamlit to use a writable config directory
os.environ["XDG_CONFIG_HOME"] = "/tmp"
os.environ["STREAMLIT_HOME"] = "/tmp"
os.makedirs("/tmp/.streamlit", exist_ok=True)

import streamlit as st
import pandas as pd
from ocr_llm_utils import run_ocr_with_gcv, extract_table_from_text, extract_markdown_table
import tempfile
from PIL import Image
import io

# Set wide layout
st.set_page_config(page_title="Invoice Processor", layout="wide")

# Sidebar navigation
st.sidebar.title("Navigation")
page = st.sidebar.radio("Go to", ["Charges Incurred", "Invoices to Table"])

# Charges Incurred page (placeholder)
if page == "Charges Incurred":
    st.title("Charges Incurred")
    
    col1, col2 = st.columns([1, 1])  # col1 = processing, col2 = image
    with col1:
        st.subheader("1️⃣ Google Cloud Vision OCR Cost")
        st.image("assets/gcv_ocr_costs.png", caption="GCV OCR Pricing", use_container_width=True)
    with col2:
        st.subheader("2️⃣ Groq API Cost (LLaMA 4 Scout)")
        st.image("assets/groq_api_costs.png", caption="Groq LLM Pricing", use_container_width=True)

    st.subheader("3️⃣ Combined Cost Summary")
    st.image("assets/cost_summary_from_chatgpt.png", caption="Total Estimated Cost for 1000 Invoices", use_container_width=True)

# Invoices to Table
elif page == "Invoices to Table":
    st.title("Invoice Table Extractor")

    st.sidebar.markdown("### πŸ”’ Max Tokens for LLM")
    selected_token_limit = st.sidebar.radio(
        "Choose max tokens:",
        options=[512, 1024, 2048, 4096],
        index=3,
        key="token_selector_sidebar"
    )
        # Model selector
    st.sidebar.markdown("### πŸ€– Choose LLM Model")
    selected_model = st.sidebar.radio(
        "Which model to use?",
        options=[
            "meta-llama/llama-4-maverick-17b-128e-instruct",
            "meta-llama/llama-4-scout-17b-16e-instruct",
            "deepseek-r1-distill-llama-70b",
            "llama-3.3-70b-versatile",
            "gemma2-9b-it"
        ],
        index=1,
        key="model_selector_sidebar"
    )
    
    uploaded_file = st.file_uploader("πŸ“€ Upload Invoice Image", type=["jpg", "jpeg", "png"])
    col1, col2 = st.columns([1.5, 1])  # col1 = processing, col2 = image

    if uploaded_file is not None:
        # Save image temporarily
        with tempfile.NamedTemporaryFile(delete=False) as temp_file:
            temp_file.write(uploaded_file.read())
            image_path = temp_file.name

        with col2:
            st.subheader("πŸ–ΌοΈ Invoice Preview")
            st.image(uploaded_file, use_container_width=True)

             
        with col1:
            with st.spinner("πŸ” Running OCR..."):
                text = run_ocr_with_gcv(image_path)

            with st.expander("πŸ“ Extracted Text"):
                st.text_area("OCR Text", text, height=300)

            with st.spinner("πŸ“Š Extracting Table..."):
                table_md = extract_table_from_text(text,max_tokens=selected_token_limit,model=selected_model)

            if st.button("🧠 Parse Table"):
                try:
                    df = extract_markdown_table(table_md)
            
                    # βœ… Store it freshly (overwrite old one if it exists)
                    st.session_state["parsed_table_df"] = df
                    st.success("βœ… Table parsed successfully. You can now edit it.")
                except Exception as e:
                    st.error(f"❌ Parsing failed: {e}")
                    
            if "parsed_table_df" in st.session_state:
                st.subheader("✏️ Editable Parsed Table")
            
                edited_df = st.data_editor(
                    st.session_state["parsed_table_df"],
                    num_rows="dynamic",
                    use_container_width=True,
                    key="invoice_editor"
                )
            
                # Update session state only after editing
                st.session_state["parsed_table_df"] = edited_df
            
                # Export options
                import io
                buffer = io.BytesIO()
                edited_df.to_excel(buffer, index=False)
                buffer.seek(0)
            
                st.download_button(
                    label="πŸ“₯ Download Edited Table as Excel",
                    data=buffer,
                    file_name="edited_invoice_table.xlsx",
                    mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
                )

            if st.button("πŸ”„ Reset Table"):
                st.session_state.pop("parsed_table_df", None)
                st.success("Table has been cleared. You can parse again.")