初始化项目,由ModelHub XC社区提供模型
Model: ratulsur/multi-format-finance-parser Source: Original Platform
This commit is contained in:
233
app.py
Normal file
233
app.py
Normal file
@@ -0,0 +1,233 @@
|
||||
import gradio as gr
|
||||
import requests
|
||||
import json
|
||||
import re
|
||||
import os
|
||||
|
||||
# ── Config ─────────────────────────────────────────────────────
|
||||
MODEL_ID = "ratulsur/multi-format-finance-parser"
|
||||
API_URL = f"https://api-inference.huggingface.co/models/{MODEL_ID}"
|
||||
HF_TOKEN = os.environ.get("HF_TOKEN", "")
|
||||
|
||||
SYSTEM_PROMPT = """You are a production financial document parser.
|
||||
Given raw text from any financial document, output ONLY a single valid JSON object.
|
||||
Schema: {document_type, vendor, client, date (YYYY-MM-DD), due_date, document_id,
|
||||
currency, subtotal, tax_amount, tax_rate_pct, total_amount,
|
||||
line_items:[{description,quantity,unit_price,amount}], payment_terms, notes, metadata}.
|
||||
All monetary values must be floats. Unknown fields → null. No explanation."""
|
||||
|
||||
# ── Inference ──────────────────────────────────────────────────
|
||||
def call_api(text: str) -> dict:
|
||||
prompt = (
|
||||
f"<|im_start|>system\n{SYSTEM_PROMPT}<|im_end|>\n"
|
||||
f"<|im_start|>user\nParse this financial document:\n\n{text}<|im_end|>\n"
|
||||
f"<|im_start|>assistant\n"
|
||||
)
|
||||
headers = {
|
||||
"Authorization": f"Bearer {HF_TOKEN}",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
payload = {
|
||||
"inputs": prompt,
|
||||
"parameters": {
|
||||
"max_new_tokens": 512,
|
||||
"temperature": 0.05,
|
||||
"return_full_text": False,
|
||||
"do_sample": False,
|
||||
},
|
||||
}
|
||||
resp = requests.post(API_URL, headers=headers, json=payload, timeout=120)
|
||||
resp.raise_for_status()
|
||||
raw = resp.json()[0]["generated_text"].strip()
|
||||
raw = re.sub(r"```json\s*|```\s*", "", raw).strip()
|
||||
|
||||
# JSON repair heuristics
|
||||
try:
|
||||
return json.loads(raw)
|
||||
except json.JSONDecodeError:
|
||||
raw = (raw
|
||||
.replace("'", '"')
|
||||
.replace("None", "null")
|
||||
.replace("True", "true")
|
||||
.replace("False", "false")
|
||||
.replace(",\n}", "\n}")
|
||||
.replace(",\n]", "\n]"))
|
||||
match = re.search(r"\{.*\}", raw, re.DOTALL)
|
||||
try:
|
||||
return json.loads(match.group() if match else raw)
|
||||
except Exception:
|
||||
return {"error": "Could not parse model output", "raw": raw}
|
||||
|
||||
|
||||
# ── Main processing function ───────────────────────────────────
|
||||
def process(text_input: str, doc_hint: str):
|
||||
if not text_input.strip():
|
||||
return "⚠️ Please paste some document text.", ""
|
||||
|
||||
try:
|
||||
text = text_input.strip()
|
||||
if doc_hint and doc_hint != "Auto-detect":
|
||||
text = f"[Document type: {doc_hint}]\n\n{text}"
|
||||
|
||||
result = call_api(text)
|
||||
|
||||
# Build summary
|
||||
summary = []
|
||||
if result.get("error"):
|
||||
return f"❌ Error: {result['error']}", json.dumps(result, indent=2)
|
||||
if result.get("document_type"):
|
||||
summary.append(f"**Type:** {result['document_type']}")
|
||||
if result.get("vendor"):
|
||||
summary.append(f"**Vendor:** {result['vendor']}")
|
||||
if result.get("client"):
|
||||
summary.append(f"**Client:** {result['client']}")
|
||||
if result.get("date"):
|
||||
summary.append(f"**Date:** {result['date']}")
|
||||
if result.get("due_date"):
|
||||
summary.append(f"**Due date:** {result['due_date']}")
|
||||
if result.get("document_id"):
|
||||
summary.append(f"**Document ID:** {result['document_id']}")
|
||||
if result.get("currency") and result.get("total_amount") is not None:
|
||||
summary.append(f"**Total:** {result['currency']} {result['total_amount']:,.2f}")
|
||||
if result.get("tax_amount") is not None:
|
||||
summary.append(f"**Tax:** {result.get('currency','')} {result['tax_amount']:,.2f}")
|
||||
if result.get("line_items"):
|
||||
summary.append(f"**Line items:** {len(result['line_items'])}")
|
||||
if result.get("payment_terms"):
|
||||
summary.append(f"**Payment terms:** {result['payment_terms']}")
|
||||
|
||||
return "\n\n".join(summary), json.dumps(result, indent=2, ensure_ascii=False)
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
return "⚠️ Model is loading, please wait 20 seconds and try again.", ""
|
||||
except requests.exceptions.HTTPError as e:
|
||||
return f"❌ API Error: {e}", ""
|
||||
except Exception as e:
|
||||
return f"❌ Error: {e}", ""
|
||||
|
||||
|
||||
# ── Examples ───────────────────────────────────────────────────
|
||||
EXAMPLES = [
|
||||
["""INVOICE
|
||||
Vendor: Tata Consultancy Services Ltd.
|
||||
Invoice No: TCS-2024-8821
|
||||
Date: 2024-11-15
|
||||
Due Date: 2024-12-15
|
||||
Bill To: Reliance Industries Ltd.
|
||||
|
||||
Service: Cloud Infrastructure Management (Oct 2024) INR 42,500.00
|
||||
Service: SAP Integration Support INR 18,000.00
|
||||
GST @ 18%: INR 10,890.00
|
||||
TOTAL DUE: INR 71,390.00
|
||||
Payment Terms: Net 30""", "Invoice"],
|
||||
|
||||
["""SAP FI - VENDOR PAYMENT REPORT
|
||||
Company Code: 1000 | Fiscal Year: 2024
|
||||
Run Date: 2024-09-30
|
||||
|
||||
|DocNo |Vendor |Amount |Curr|Status |
|
||||
|----------|--------------------|--------------|----|--------|
|
||||
|1900045621|Wipro Limited | 4,25,000.00 |INR |Open |
|
||||
|1900045622|HCL Technologies | 2,10,500.00 |INR |Cleared |
|
||||
|1900045623|Infosys BPO | 8,75,200.00 |INR |Open |
|
||||
|
||||
Total: 15,10,700.00 INR""", "SAP Report"],
|
||||
|
||||
["""INCOME STATEMENT
|
||||
Reliance Industries Ltd.
|
||||
Period ending: 2024-09-30
|
||||
(in INR)
|
||||
|
||||
Revenue: 50,000,000.00
|
||||
Cost of Revenue: (22,000,000.00)
|
||||
Gross Profit: 28,000,000.00
|
||||
Operating Expenses: (12,000,000.00)
|
||||
EBIT: 16,000,000.00
|
||||
Income Tax 25%: (4,000,000.00)
|
||||
Net Income: 12,000,000.00""", "Income Statement"],
|
||||
|
||||
["""PURCHASE ORDER
|
||||
PO Number: PO-2024-00456
|
||||
Date: 2024-10-01
|
||||
Vendor: Amazon Web Services India
|
||||
Ship To: HDFC Bank Ltd., Mumbai
|
||||
|
||||
Item 1: EC2 Reserved Instances (1yr) USD 12,000.00
|
||||
Item 2: S3 Storage 50TB USD 1,800.00
|
||||
Item 3: RDS Multi-AZ USD 4,200.00
|
||||
Subtotal: USD 18,000.00
|
||||
Tax: USD 0.00
|
||||
Total: USD 18,000.00
|
||||
Payment Terms: Net 45""", "Purchase Order"],
|
||||
]
|
||||
|
||||
|
||||
# ── UI ─────────────────────────────────────────────────────────
|
||||
with gr.Blocks(
|
||||
title="Multi-Format Finance Parser",
|
||||
theme=gr.themes.Soft(),
|
||||
css=".json-output { font-family: monospace; font-size: 13px; }"
|
||||
) as demo:
|
||||
|
||||
gr.Markdown("""
|
||||
# 🏦 Multi-Format Finance Document Parser
|
||||
**Production-grade** financial document extraction → structured JSON.
|
||||
|
||||
Supports: **Invoice · SAP Report · Income Statement · Bank Statement · Purchase Order · SQL results**
|
||||
|
||||
*Fine-tuned Qwen2.5-7B-Instruct · QLoRA 4-bit NF4 · Trained on CORD-v2 + synthetic finance data*
|
||||
""")
|
||||
|
||||
with gr.Row():
|
||||
with gr.Column(scale=1):
|
||||
text_in = gr.Textbox(
|
||||
label="Paste document text",
|
||||
lines=16,
|
||||
placeholder="Paste your invoice, SAP export, income statement, or any financial document here...",
|
||||
)
|
||||
hint_in = gr.Dropdown(
|
||||
choices=[
|
||||
"Auto-detect",
|
||||
"Invoice",
|
||||
"SAP Report",
|
||||
"Balance Sheet",
|
||||
"Income Statement",
|
||||
"Bank Statement",
|
||||
"Purchase Order",
|
||||
"SQL Result",
|
||||
],
|
||||
value="Auto-detect",
|
||||
label="Document type hint (optional)",
|
||||
)
|
||||
parse_btn = gr.Button("Parse Document", variant="primary", size="lg")
|
||||
|
||||
with gr.Column(scale=1):
|
||||
summary_out = gr.Markdown(label="Summary")
|
||||
json_out = gr.Code(
|
||||
label="Structured JSON output",
|
||||
language="json",
|
||||
lines=18,
|
||||
)
|
||||
|
||||
gr.Markdown("### Try an example")
|
||||
gr.Examples(
|
||||
examples=EXAMPLES,
|
||||
inputs=[text_in, hint_in],
|
||||
label="Click any example to load it",
|
||||
)
|
||||
|
||||
gr.Markdown("""
|
||||
---
|
||||
**Model:** [ratulsur/multi-format-finance-parser](https://huggingface.co/ratulsur/multi-format-finance-parser)
|
||||
**Training:** QLoRA (4-bit NF4 double quantization) on Qwen2.5-7B-Instruct
|
||||
**Dataset:** CORD-v2 receipts + synthetic invoices, SAP reports, income statements
|
||||
""")
|
||||
|
||||
parse_btn.click(
|
||||
fn=process,
|
||||
inputs=[text_in, hint_in],
|
||||
outputs=[summary_out, json_out],
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
demo.launch()
|
||||
Reference in New Issue
Block a user