mlbench123 commited on
Commit
1be0b12
·
verified ·
1 Parent(s): b0f731d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +77 -41
app.py CHANGED
@@ -1,3 +1,6 @@
 
 
 
1
  import base64
2
  import json
3
  from pathlib import Path
@@ -10,10 +13,21 @@ MODEL = "gpt-5.1"
10
  client = OpenAI(api_key=API_KEY)
11
 
12
 
 
 
 
 
 
 
 
 
 
 
13
  def build_prompt():
14
  return (
15
- "You are an extraction system. Extract ONLY explicit data. No guessing.\n\n"
16
- "Return JSON strictly:\n"
 
17
  "{\n"
18
  " \"po_number\": string|null,\n"
19
  " \"ship_from\": string|null,\n"
@@ -21,76 +35,98 @@ def build_prompt():
21
  " \"rail_car_number\": string|null,\n"
22
  " \"total_quantity\": number|null,\n"
23
  " \"inventories\": [\n"
24
- " {\n"
25
- " \"productName\": string,\n"
26
- " \"productCode\": string|null,\n"
27
- " \"variants\": [\n"
28
- " {\n"
29
- " \"dimensions\": string|null,\n"
30
- " \"pcs_per_pkg\": number|null,\n"
31
- " \"length_ft\": number|null,\n"
32
- " \"width\": number|null,\n"
33
- " \"packages\": number|null,\n"
34
- " \"pieces\": number|null,\n"
35
- " \"fbm\": number|null\n"
36
- " }\n"
37
- " ],\n"
38
- " \"total_pcs\": number|null,\n"
39
- " \"total_fbm\": number|null\n"
40
- " }\n"
41
  " ],\n"
42
  " \"custom_fields\": {}\n"
43
- "}\n"
44
- )
45
 
 
 
 
 
 
 
 
 
46
 
47
- def upload_pdf(p):
48
- f = client.files.create(file=open(p, "rb"), purpose="assistants")
49
- return f.id
 
 
50
 
 
 
 
51
 
 
 
 
 
 
 
52
  def extract(file):
53
  path = Path(file.name)
54
- prompt = build_prompt()
55
- ext = path.suffix.lower()
56
 
57
- if ext == ".pdf":
58
  fid = upload_pdf(path)
59
  msg = [
60
- {"type": "text", "text": prompt},
61
  {"type": "file", "file": {"file_id": fid}}
62
  ]
63
  else:
64
  b64 = base64.b64encode(path.read_bytes()).decode()
65
- mime = f"image/{ext[1:]}"
66
  msg = [
67
- {"type": "text", "text": prompt},
68
- {"type": "image_url", "image_url": {"url": f"data:{mime};base64,{b64}"}}
 
 
 
69
  ]
70
 
71
  r = client.chat.completions.create(
72
  model=MODEL,
73
  messages=[{"role": "user", "content": msg}]
74
  )
75
- raw = r.choices[0].message.content
76
- s = raw.find("{")
77
- e = raw.rfind("}")
78
- return json.loads(raw[s:e+1])
79
 
 
 
 
 
80
 
81
- sample_files = [
82
- ("IMG_0001.jpg", "IMG_0001.jpg"),
83
- ("IMG_0002.jpg", "IMG_0002.jpg")
84
- ]
85
 
 
86
  def ui(file):
87
  return extract(file)
88
 
89
 
 
 
 
 
 
 
90
  gr.Interface(
91
  fn=ui,
92
  inputs=gr.File(label="Upload PDF or Image"),
93
  outputs=gr.JSON(label="Extracted JSON"),
94
- title="Logistics OCR Extraction",
95
- examples=[f for _, f in sample_files]
96
  ).launch()
 
1
+ #!/usr/bin/env python3
2
+ # app.py — Logistics OCR Extractor (PDF + Images) with strict ship_from rules
3
+
4
  import base64
5
  import json
6
  from pathlib import Path
 
13
  client = OpenAI(api_key=API_KEY)
14
 
15
 
16
+ # ----------------------- PDF Upload -----------------------
17
+ def upload_pdf(path):
18
+ f = client.files.create(
19
+ file=open(path, "rb"),
20
+ purpose="assistants"
21
+ )
22
+ return f.id
23
+
24
+
25
+ # ----------------------- Prompt Builder -----------------------
26
  def build_prompt():
27
  return (
28
+ "Extract structured JSON from this logistics shipping document. "
29
+ "Use only what appears in the PDF/image, never hallucinate. "
30
+ "Return strictly valid JSON in this schema:\n\n"
31
  "{\n"
32
  " \"po_number\": string|null,\n"
33
  " \"ship_from\": string|null,\n"
 
35
  " \"rail_car_number\": string|null,\n"
36
  " \"total_quantity\": number|null,\n"
37
  " \"inventories\": [\n"
38
+ " {\n"
39
+ " \"productName\": string,\n"
40
+ " \"productCode\": string|null,\n"
41
+ " \"variants\": [\n"
42
+ " {\n"
43
+ " \"dimensions\": string|null,\n"
44
+ " \"pcs_per_pkg\": number|null,\n"
45
+ " \"length_ft\": number|null,\n"
46
+ " \"width\": number|null,\n"
47
+ " \"packages\": number|null,\n"
48
+ " \"pieces\": number|null,\n"
49
+ " \"fbm\": number|null\n"
50
+ " }\n"
51
+ " ],\n"
52
+ " \"total_pcs\": number|null,\n"
53
+ " \"total_fbm\": number|null\n"
54
+ " }\n"
55
  " ],\n"
56
  " \"custom_fields\": {}\n"
57
+ "}\n\n"
 
58
 
59
+ "SHIP_FROM EXTRACTION RULES (MANDATORY):\n"
60
+ "1. If document contains explicit Origin/Ship From labels, extract that value.\n"
61
+ "2. If document is an email-based inbound notice and no explicit origin exists, "
62
+ "set ship_from = the email 'From:' field.\n"
63
+ "3. If both Origin and Mill exist, use Origin.\n"
64
+ "4. If only Mill exists AND it is clearly the shipping location, use Mill.\n"
65
+ "5. Priority order: Origin → Email From → Mill → Sender company block.\n"
66
+ "6. If none apply, ship_from = null.\n\n"
67
 
68
+ "Rules for inventories:\n"
69
+ "- Do NOT merge different lengths; create a separate variant per length.\n"
70
+ "- Extract EXACT numbers shown: packages, pcs_per_pkg, pieces, fbm.\n"
71
+ "- total_pcs = sum of all variant pieces.\n"
72
+ "- total_fbm = sum of all variant fbm.\n\n"
73
 
74
+ "Rules for total_quantity:\n"
75
+ "- If the document shows a total PCS value explicitly, use it.\n"
76
+ "- If only variants exist, do not compute total_quantity unless the document explicitly states it.\n\n"
77
 
78
+ "Parse tables carefully. If a dimension group (like 2x6) appears, use that.\n"
79
+ "Return only JSON. No explanations."
80
+ )
81
+
82
+
83
+ # ----------------------- Extraction Logic -----------------------
84
  def extract(file):
85
  path = Path(file.name)
86
+ suffix = path.suffix.lower()
 
87
 
88
+ if suffix == ".pdf":
89
  fid = upload_pdf(path)
90
  msg = [
91
+ {"type": "text", "text": build_prompt()},
92
  {"type": "file", "file": {"file_id": fid}}
93
  ]
94
  else:
95
  b64 = base64.b64encode(path.read_bytes()).decode()
 
96
  msg = [
97
+ {"type": "text", "text": build_prompt()},
98
+ {
99
+ "type": "image_url",
100
+ "image_url": {"url": f"data:image/{suffix[1:]};base64,{b64}"}
101
+ }
102
  ]
103
 
104
  r = client.chat.completions.create(
105
  model=MODEL,
106
  messages=[{"role": "user", "content": msg}]
107
  )
 
 
 
 
108
 
109
+ txt = r.choices[0].message.content
110
+ s = txt.find("{")
111
+ e = txt.rfind("}")
112
+ return txt[s:e+1]
113
 
 
 
 
 
114
 
115
+ # ----------------------- Gradio UI -----------------------
116
  def ui(file):
117
  return extract(file)
118
 
119
 
120
+ # Sample images (optional)
121
+ sample_files = [
122
+ ("IMG_0001.jpg", "samples/IMG_0001.jpg"),
123
+ ("IMG_0002.jpg", "samples/IMG_0002.jpg")
124
+ ]
125
+
126
  gr.Interface(
127
  fn=ui,
128
  inputs=gr.File(label="Upload PDF or Image"),
129
  outputs=gr.JSON(label="Extracted JSON"),
130
+ title="Logistics OCR Data Extractor (GPT-5.1)",
131
+ examples=sample_files
132
  ).launch()