serene-abyss commited on
Commit
fc3fac6
·
verified ·
1 Parent(s): 3d09688

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +163 -0
app.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import json
3
+ import hashlib
4
+ from web3 import Web3
5
+ from google import genai
6
+ from google.genai import types
7
+
8
+ # --------------------------------------------------
9
+ # PAGE CONFIG
10
+ # --------------------------------------------------
11
+ st.set_page_config(
12
+ page_title="Invoice-Flow AI",
13
+ layout="wide",
14
+ initial_sidebar_state="collapsed"
15
+ )
16
+
17
+ st.title("📄 Invoice-Flow AI")
18
+ st.markdown("### Agentic invoice verification & on-chain trust registry")
19
+
20
+ # --------------------------------------------------
21
+ # SECRETS (HF SAFE)
22
+ # --------------------------------------------------
23
+ try:
24
+ GEMINI_API_KEY = st.secrets["GEMINI_API_KEY"]
25
+ RPC_URL = st.secrets["RPC_URL"]
26
+ PRIVATE_KEY = st.secrets["PRIVATE_KEY"]
27
+ except Exception:
28
+ st.error("🚨 Secrets missing. Add them in Hugging Face → Settings → Variables.")
29
+ st.stop()
30
+
31
+ # --------------------------------------------------
32
+ # CLIENT SETUP
33
+ # --------------------------------------------------
34
+ try:
35
+ genai_client = genai.Client(api_key=GEMINI_API_KEY)
36
+
37
+ w3 = Web3(Web3.HTTPProvider(RPC_URL))
38
+ account = w3.eth.account.from_key(PRIVATE_KEY)
39
+
40
+ CONTRACT_ADDRESS = "0x3af7C33f4D0303E6eb94c2D24fB0bD82B54c6914"
41
+ checksum_address = Web3.to_checksum_address(CONTRACT_ADDRESS)
42
+
43
+ with open("abi.json", "r") as f:
44
+ ABI = json.load(f)
45
+
46
+ contract = w3.eth.contract(address=checksum_address, abi=ABI)
47
+
48
+ except Exception as e:
49
+ st.error(f"Setup failed: {e}")
50
+ st.stop()
51
+
52
+ # --------------------------------------------------
53
+ # UTIL
54
+ # --------------------------------------------------
55
+ def get_mock_invoice():
56
+ return {
57
+ "seller_name": "TechFlow Solutions Pvt Ltd",
58
+ "seller_gstin": "29AAACF5724R1Z5",
59
+ "invoice_amount": 75000,
60
+ "invoice_number": "INV-2024-001"
61
+ }
62
+
63
+ # --------------------------------------------------
64
+ # SESSION STATE
65
+ # --------------------------------------------------
66
+ for key, value in {
67
+ "invoice": None,
68
+ "approved": False,
69
+ "minted": False,
70
+ "tx_hash": None
71
+ }.items():
72
+ if key not in st.session_state:
73
+ st.session_state[key] = value
74
+
75
+ # --------------------------------------------------
76
+ # UI
77
+ # --------------------------------------------------
78
+ col1, col2 = st.columns([1, 1])
79
+
80
+ with col1:
81
+ st.subheader("1️⃣ Upload & Analyze Invoice")
82
+ uploaded = st.file_uploader("Upload Invoice (PDF)", type=["pdf"])
83
+
84
+ if uploaded:
85
+ pdf_bytes = uploaded.getvalue()
86
+
87
+ if st.button("🚀 Analyze with Gemini 1.5 Flash"):
88
+ with st.spinner("Extracting invoice data..."):
89
+ try:
90
+ response = genai_client.models.generate_content(
91
+ model="gemini-1.5-flash",
92
+ contents=[
93
+ types.Part.from_bytes(pdf_bytes, "application/pdf"),
94
+ "Extract invoice as JSON with keys: seller_name, seller_gstin, invoice_amount, invoice_number. Return ONLY raw JSON."
95
+ ]
96
+ )
97
+
98
+ text = response.text.replace("```json", "").replace("```", "").strip()
99
+ st.session_state.invoice = json.loads(text)
100
+
101
+ except Exception as e:
102
+ st.warning(f"AI parsing failed ({e}). Using fallback.")
103
+ st.session_state.invoice = get_mock_invoice()
104
+
105
+ with col2:
106
+ if st.session_state.invoice:
107
+ st.subheader("2️⃣ Verification Dashboard")
108
+ st.json(st.session_state.invoice)
109
+
110
+ st.markdown("---")
111
+ st.subheader("3️⃣ Buyer Consent")
112
+
113
+ if not st.session_state.approved:
114
+ if st.button("✅ Approve Invoice"):
115
+ st.session_state.approved = True
116
+ st.rerun()
117
+ else:
118
+ st.success("Buyer Approved")
119
+
120
+ if st.session_state.approved:
121
+ st.markdown("---")
122
+ st.subheader("4️⃣ On-Chain Settlement")
123
+
124
+ if not st.session_state.minted:
125
+ if st.button("⛓️ Mint Receipt on Polygon"):
126
+ with st.spinner("Minting on-chain..."):
127
+ inv = st.session_state.invoice
128
+ payload = f"{inv['invoice_number']}{inv['seller_gstin']}{inv['invoice_amount']}"
129
+ receipt_hash = hashlib.sha256(payload.encode()).hexdigest()
130
+ receipt_bytes = Web3.to_bytes(hexstr="0x" + receipt_hash)
131
+
132
+ try:
133
+ tx = contract.functions.mintReceipt(
134
+ receipt_bytes,
135
+ inv["seller_gstin"],
136
+ int(inv["invoice_amount"])
137
+ ).build_transaction({
138
+ "from": account.address,
139
+ "nonce": w3.eth.get_transaction_count(account.address),
140
+ "gas": 250000,
141
+ "gasPrice": w3.eth.gas_price,
142
+ "chainId": 80002
143
+ })
144
+
145
+ signed = w3.eth.account.sign_transaction(tx, PRIVATE_KEY)
146
+ tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
147
+
148
+ st.session_state.tx_hash = w3.to_hex(tx_hash)
149
+ st.session_state.minted = True
150
+ st.rerun()
151
+
152
+ except Exception as e:
153
+ st.error(f"Mint failed: {e}")
154
+
155
+ if st.session_state.minted:
156
+ st.success("Transaction Confirmed")
157
+ st.link_button(
158
+ "View on PolygonScan",
159
+ f"https://amoy.polygonscan.com/tx/{st.session_state.tx_hash}"
160
+ )
161
+
162
+ st.markdown("---")
163
+ st.caption("Hackathon Prototype • Gemini 1.5 Flash • Polygon Amoy")