Tahasaif3 commited on
Commit
eeabf12
ยท
verified ยท
1 Parent(s): 4ae9f5c

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +69 -23
main.py CHANGED
@@ -44,9 +44,6 @@ class StudentRecord(BaseModel):
44
  section: str = Field(default="", description="Section letter")
45
  mobile: str = Field(default="", description="Mobile number")
46
 
47
- class ExtractResponse(BaseModel):
48
- students: List[StudentRecord] = Field(default_factory=list)
49
-
50
  class PDFRequest(BaseModel):
51
  pdfUrl: str
52
 
@@ -76,10 +73,9 @@ Return ONLY this JSON format:
76
  ]
77
  }
78
 
79
- IMPORTANT: Ensure ALL students are included. If you see 200 students, return all 200. No truncation allowed.
80
  """,
81
- output_type=ExtractResponse,
82
- model_settings=ModelSettings(temperature=0.05, top_p=0.7, max_tokens=4000)
83
  )
84
 
85
  runner = Runner()
@@ -103,6 +99,41 @@ def download_and_extract_text(pdf_url: str) -> str:
103
  return text
104
 
105
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  def regex_fallback_extraction(text: str) -> dict:
107
  """Robust regex-based extraction for when agent fails"""
108
  print("๐Ÿ”„ Using regex fallback for extraction...")
@@ -111,17 +142,17 @@ def regex_fallback_extraction(text: str) -> dict:
111
  # Try multiple regex patterns for flexibility
112
  patterns = [
113
  # Pattern 1: Name | Roll | Class | Section | Mobile
114
- r'^([A-Za-z\s]+?)\s*\|\s*(\d+)\s*\|\s*([\w\d\s\.]+?)\s*\|\s*([A-Z])\s*\|\s*(\d+)',
115
  # Pattern 2: Space-separated format
116
- r'^([A-Za-z\s]+?)\s+(\d+)\s+([\w\d\s\.]+?)\s+([A-Z])\s+(\d{10,})',
117
- # Pattern 3: Tab-separated or multiple spaces
118
- r'^([A-Za-z\s]+?)\t+(\d+)\t+([\w\d\s\.]+?)\t+([A-Z])\t+(\d{10,})',
119
  ]
120
 
121
  seen = set()
122
  for line in text.splitlines():
123
  line = line.strip()
124
- if not line or "name" in line.lower() or "roll" in line.lower():
125
  continue
126
 
127
  for pattern in patterns:
@@ -130,11 +161,11 @@ def regex_fallback_extraction(text: str) -> dict:
130
  name = match.group(1).strip()
131
  roll_no = match.group(2)
132
  class_name = match.group(3).strip()
133
- section = match.group(4)
134
  mobile = match.group(5)
135
 
136
  key = (name, roll_no)
137
- if key not in seen:
138
  seen.add(key)
139
  students.append({
140
  "name": name,
@@ -150,10 +181,9 @@ def regex_fallback_extraction(text: str) -> dict:
150
 
151
 
152
  async def extract_from_text_chunked(text: str) -> dict:
153
- """Runs the agent with chunking for large datasets"""
154
  print(f"๐Ÿ“„ Extracting from {len(text)} characters...")
155
 
156
- # Try direct extraction first
157
  try:
158
  resp = await runner.run(
159
  student_agent,
@@ -168,14 +198,23 @@ async def extract_from_text_chunked(text: str) -> dict:
168
  output = resp.final_output
169
 
170
  if output:
171
- result = output.model_dump() if hasattr(output, "model_dump") else output
 
 
 
 
 
 
 
172
  student_count = len(result.get("students", []))
173
  print(f"โœ… Agent extracted {student_count} students")
174
 
175
  if student_count > 0:
176
  return result
 
 
177
  except Exception as e:
178
- print(f"โš ๏ธ Agent extraction failed: {e}")
179
 
180
  # Fallback to regex if agent fails or returns empty
181
  return regex_fallback_extraction(text)
@@ -187,8 +226,11 @@ def clean_and_deduplicate(students: List[dict]) -> List[dict]:
187
  unique = []
188
 
189
  for s in students:
190
- key = (s.get("name", "").strip(), s.get("roll_no", "").strip())
191
- if key and key not in seen:
 
 
 
192
  seen.add(key)
193
  unique.append(s)
194
 
@@ -204,7 +246,7 @@ async def extract_student(req: PDFRequest):
204
 
205
  Features:
206
  - Handles large datasets (200+ students)
207
- - Agent-based extraction with fallback
208
  - Regex backup for comprehensive coverage
209
  - Automatic deduplication
210
  """
@@ -220,11 +262,15 @@ async def extract_student(req: PDFRequest):
220
  "success": True,
221
  "pdfUrl": req.pdfUrl,
222
  "total_students": len(cleaned),
223
- "students": cleaned,
224
- "raw_text_preview": text[:1000]
225
  }
226
  except Exception as e:
227
  print(f"โŒ Error: {e}")
228
  import traceback
229
  traceback.print_exc()
230
- return {"success": False, "error": str(e)}
 
 
 
 
 
 
44
  section: str = Field(default="", description="Section letter")
45
  mobile: str = Field(default="", description="Mobile number")
46
 
 
 
 
47
  class PDFRequest(BaseModel):
48
  pdfUrl: str
49
 
 
73
  ]
74
  }
75
 
76
+ IMPORTANT: Ensure ALL students are included. No truncation allowed. Close all JSON arrays and objects properly.
77
  """,
78
+ model_settings=ModelSettings(temperature=0.05, top_p=0.7, max_tokens=8000)
 
79
  )
80
 
81
  runner = Runner()
 
99
  return text
100
 
101
 
102
+ def parse_json_from_output(output: str) -> dict:
103
+ """Parse JSON from agent output, handling truncation and errors"""
104
+ if not output:
105
+ return {"students": []}
106
+
107
+ # Try direct JSON parse first
108
+ try:
109
+ return json.loads(output)
110
+ except json.JSONDecodeError:
111
+ pass
112
+
113
+ # Try to extract JSON from markdown or other formatting
114
+ json_match = re.search(r'\{[\s\S]*\}', output)
115
+ if not json_match:
116
+ return {"students": []}
117
+
118
+ json_str = json_match.group(0)
119
+
120
+ # Try to fix incomplete JSON
121
+ try:
122
+ return json.loads(json_str)
123
+ except json.JSONDecodeError:
124
+ # Try closing the JSON if it's truncated
125
+ open_braces = json_str.count('{') - json_str.count('}')
126
+ open_brackets = json_str.count('[') - json_str.count(']')
127
+
128
+ json_str = json_str.rstrip().rstrip(',') + ']' * open_brackets + '}' * open_braces
129
+
130
+ try:
131
+ return json.loads(json_str)
132
+ except json.JSONDecodeError as e:
133
+ print(f"โš ๏ธ Failed to parse JSON even after fixing: {e}")
134
+ return {"students": []}
135
+
136
+
137
  def regex_fallback_extraction(text: str) -> dict:
138
  """Robust regex-based extraction for when agent fails"""
139
  print("๐Ÿ”„ Using regex fallback for extraction...")
 
142
  # Try multiple regex patterns for flexibility
143
  patterns = [
144
  # Pattern 1: Name | Roll | Class | Section | Mobile
145
+ r'^([A-Za-z\s]+?)\s*\|\s*(\d+)\s*\|\s*([\w\d\s\.,-]+?)\s*\|\s*([A-Za-z0-9\s,.-]+?)\s*\|\s*(\d+)',
146
  # Pattern 2: Space-separated format
147
+ r'^([A-Za-z\s]+?)\s+(\d{8,})\s+([\w\d\s\.,-]+?)\s+([A-Za-z0-9\s,.-]+?)\s+(\d{11,})',
148
+ # Pattern 3: Tab-separated
149
+ r'^([A-Za-z\s]+?)\t+(\d{8,})\t+([\w\d\s\.,-]+?)\t+([A-Za-z0-9\s,.-]+?)\t+(\d{11,})',
150
  ]
151
 
152
  seen = set()
153
  for line in text.splitlines():
154
  line = line.strip()
155
+ if not line or "name" in line.lower() or "roll" in line.lower() or "generated" in line.lower():
156
  continue
157
 
158
  for pattern in patterns:
 
161
  name = match.group(1).strip()
162
  roll_no = match.group(2)
163
  class_name = match.group(3).strip()
164
+ section = match.group(4).strip()
165
  mobile = match.group(5)
166
 
167
  key = (name, roll_no)
168
+ if key not in seen and name and roll_no:
169
  seen.add(key)
170
  students.append({
171
  "name": name,
 
181
 
182
 
183
  async def extract_from_text_chunked(text: str) -> dict:
184
+ """Runs the agent with flexible JSON parsing for large datasets"""
185
  print(f"๐Ÿ“„ Extracting from {len(text)} characters...")
186
 
 
187
  try:
188
  resp = await runner.run(
189
  student_agent,
 
198
  output = resp.final_output
199
 
200
  if output:
201
+ # Convert to string if needed
202
+ if isinstance(output, str):
203
+ output_str = output
204
+ else:
205
+ output_str = str(output)
206
+
207
+ # Parse JSON flexibly
208
+ result = parse_json_from_output(output_str)
209
  student_count = len(result.get("students", []))
210
  print(f"โœ… Agent extracted {student_count} students")
211
 
212
  if student_count > 0:
213
  return result
214
+ else:
215
+ print("โš ๏ธ Agent returned empty results")
216
  except Exception as e:
217
+ print(f"โš ๏ธ Agent extraction error: {e}")
218
 
219
  # Fallback to regex if agent fails or returns empty
220
  return regex_fallback_extraction(text)
 
226
  unique = []
227
 
228
  for s in students:
229
+ name = str(s.get("name", "")).strip()
230
+ roll_no = str(s.get("roll_no", "")).strip()
231
+ key = (name, roll_no)
232
+
233
+ if key and key[0] and key[1] and key not in seen:
234
  seen.add(key)
235
  unique.append(s)
236
 
 
246
 
247
  Features:
248
  - Handles large datasets (200+ students)
249
+ - No strict validation - flexible JSON parsing
250
  - Regex backup for comprehensive coverage
251
  - Automatic deduplication
252
  """
 
262
  "success": True,
263
  "pdfUrl": req.pdfUrl,
264
  "total_students": len(cleaned),
265
+ "students": cleaned
 
266
  }
267
  except Exception as e:
268
  print(f"โŒ Error: {e}")
269
  import traceback
270
  traceback.print_exc()
271
+ return {
272
+ "success": False,
273
+ "error": str(e),
274
+ "total_students": 0,
275
+ "students": []
276
+ }