arjunbhargav212 commited on
Commit
dc23f92
Β·
verified Β·
1 Parent(s): ad5d213

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile +6 -12
  2. README.md +32 -108
  3. app.py +374 -40
  4. requirements.txt +7 -3
Dockerfile CHANGED
@@ -1,18 +1,12 @@
1
- FROM python:3.10
2
 
3
- # Update apt-get and install system libraries needed for Docling
4
- RUN apt-get update && apt-get install -y \
5
- libgl1 \
6
- libglib2.0-0 \
7
- && rm -rf /var/lib/apt/lists/*
8
 
9
- WORKDIR /code
 
10
 
11
- COPY ./requirements.txt /code/requirements.txt
12
- RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
13
-
14
- COPY . .
15
 
16
  EXPOSE 7860
17
 
18
- CMD ["python", "app.py"]
 
1
+ FROM python:3.10-slim
2
 
3
+ WORKDIR /app
 
 
 
 
4
 
5
+ COPY requirements.txt .
6
+ RUN pip install --no-cache-dir -r requirements.txt
7
 
8
+ COPY app.py .
 
 
 
9
 
10
  EXPOSE 7860
11
 
12
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,126 +1,50 @@
1
- # πŸš€ Hugging Face Document Extraction APIs
2
-
3
- Complete deployment package for **Docling** and **DocStrange** on Hugging Face Spaces.
4
-
5
  ---
6
-
7
- ## πŸ“ **Folder Structure**
8
-
9
- ```
10
- huggingface_deploy/
11
- β”œβ”€β”€ README.md # This file
12
- β”œβ”€β”€ docling-api/
13
- β”‚ β”œβ”€β”€ app.py # Docling FastAPI application
14
- β”‚ β”œβ”€β”€ requirements.txt # Python dependencies
15
- β”‚ └── Dockerfile # Docker configuration
16
- β”œβ”€β”€ docstrange-api/
17
- β”‚ β”œβ”€β”€ app.py # DocStrange FastAPI application
18
- β”‚ β”œβ”€β”€ requirements.txt # Python dependencies
19
- β”‚ └── Dockerfile # Docker configuration
20
- └── test-scripts/
21
- β”œβ”€β”€ test_docling.py # Test Docling API
22
- └── test_docstrange.py # Test DocStrange API
23
- ```
24
-
25
  ---
26
 
27
- ## 🎯 **Quick Start**
28
-
29
- ### **Step 1: Create Hugging Face Spaces**
30
-
31
- 1. Go to **https://huggingface.co/spaces**
32
- 2. Click **Create new Space**
33
- 3. Create TWO spaces:
34
- - `docling-api` (for Docling)
35
- - `docstrange-api` (for DocStrange)
36
-
37
- ### **Step 2: Upload Files**
38
-
39
- For each space:
40
- 1. Upload the corresponding `app.py` and `requirements.txt`
41
- 2. Wait for deployment (2-5 minutes)
42
- 3. Copy your Space URL: `https://YOUR_USERNAME-docling-api.hf.space`
43
-
44
- ### **Step 3: Connect to DataSync**
45
-
46
- In DataSync β†’ Import Data β†’ DocStrange tab:
47
- 1. Select engine: `πŸ”¬ Docling Hugging Face` or `πŸ§ͺ DocStrange Hugging Face`
48
- 2. Paste your HF URL
49
- 3. Click **Extract**
50
-
51
- ---
52
 
53
- ## πŸ“Š **API Endpoints**
54
 
55
- ### **Docling API**
56
 
57
- | Endpoint | Method | Description |
58
- |----------|--------|-------------|
59
- | `/` | GET | Health check |
60
- | `/health` | GET | Health status |
61
- | `/convert` | POST | Full document conversion |
62
- | `/convert/markdown` | POST | Markdown only |
63
- | `/convert/tables` | POST | Tables only |
64
 
65
- ### **DocStrange API**
 
 
 
66
 
67
- | Endpoint | Method | Description |
68
- |----------|--------|-------------|
69
- | `/` | GET | Health check |
70
- | `/health` | GET | Health status |
71
- | `/extract` | POST | Full document extraction |
72
- | `/extract/markdown` | POST | Markdown/text only |
73
- | `/extract/tables` | POST | Tables only |
74
 
75
- ---
 
 
 
 
76
 
77
- ## πŸ§ͺ **Testing**
78
 
79
  ```bash
80
- # Test Docling API
81
- python test-scripts/test_docling.py https://your-docling-api.hf.space
 
82
 
83
- # Test DocStrange API
84
- python test-scripts/test_docstrange.py https://your-docstrange-api.hf.space
 
85
  ```
86
 
87
- ---
88
-
89
- ## πŸ’° **Hugging Face Tiers**
90
-
91
- | Tier | Cost | Memory | Best For |
92
- |------|------|--------|----------|
93
- | **CPU Basic** | Free | 16GB | Testing, small PDFs |
94
- | **CPU Upgrade** | Free | 32GB | Medium documents |
95
- | **T4 GPU** | $0.60/hr | 16GB + 16GB VRAM | Large docs, fast extraction |
96
-
97
- **Recommendation**: Start with **Free CPU tier** for testing, upgrade to GPU for production.
98
-
99
- ---
100
-
101
- ## πŸ” **Private Spaces**
102
-
103
- If you want private APIs:
104
- 1. Go to Space Settings β†’ **Make Private**
105
- 2. Create token: https://huggingface.co/settings/tokens
106
- 3. In DataSync, enter both URL and token
107
-
108
- ---
109
-
110
- ## πŸ“š **Full Documentation**
111
-
112
- - [Docling API Details](docling-api/README.md)
113
- - [DocStrange API Details](docstrange-api/README.md)
114
-
115
- ---
116
 
117
- ## 🎯 **Integration with DataSync**
118
 
119
- All APIs are fully integrated with:
120
- - βœ… DataSync Import Data module
121
- - βœ… Automatic fallback on failure
122
- - βœ… Structured data display
123
- - βœ… Column mapping to ERPNext
124
- - βœ… CSV/JSON export
125
 
126
- **Ready to deploy!** πŸš€
 
 
 
 
 
1
  ---
2
+ title: Unified Document Extraction API
3
+ emoji: πŸ“„
4
+ colorFrom: blue
5
+ colorTo: indigo
6
+ sdk: docker
7
+ app_file: app.py
8
+ pinned: false
 
 
 
 
 
 
 
 
 
 
 
 
9
  ---
10
 
11
+ # πŸš€ Unified Document Extraction API
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
+ **One API, Two Engines: Docling + DocStrange**
14
 
15
+ Extract structured data from any document using AI-powered engines.
16
 
17
+ ## Features
 
 
 
 
 
 
18
 
19
+ - βœ… **Docling** - Advanced document parsing with structure preservation
20
+ - βœ… **DocStrange** - GPU-accelerated intelligent document processing
21
+ - βœ… **Multiple formats** - PDF, DOCX, XLSX, PPTX, Images, and more
22
+ - βœ… **Structured output** - Markdown, JSON, Tables
23
 
24
+ ## API Endpoints
 
 
 
 
 
 
25
 
26
+ - `GET /` - Health check
27
+ - `GET /engines` - List available engines
28
+ - `POST /convert` - Full document conversion
29
+ - `POST /convert/markdown` - Markdown only
30
+ - `POST /convert/tables` - Tables only
31
 
32
+ ## Usage
33
 
34
  ```bash
35
+ # Convert with Docling
36
+ curl -X POST "https://YOUR_SPACE.hf.space/convert?engine=docling" \
37
+ -F "file=@document.pdf"
38
 
39
+ # Convert with DocStrange
40
+ curl -X POST "https://YOUR_SPACE.hf.space/convert?engine=docstrange" \
41
+ -F "file=@document.pdf"
42
  ```
43
 
44
+ ## Integration
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
+ Works with **DataSync** application for ERPNext integration.
47
 
48
+ ## License
 
 
 
 
 
49
 
50
+ MIT
app.py CHANGED
@@ -1,51 +1,385 @@
1
- from flask import Flask, request, jsonify, render_template_string
2
- from docling.document_converter import DocumentConverter
 
 
 
3
  import os
 
 
 
4
 
 
 
 
 
5
 
6
- app = Flask(__name__)
7
- converter = DocumentConverter()
 
8
 
9
- @app.route('/', methods=['GET'])
10
- def index():
11
- return render_template_string('''
12
- <!doctype html>
13
- <title>Docling Processor</title>
14
- <h1>Upload PDF to Convert to Markdown</h1>
15
- <form method="post" action="/process" enctype="multipart/form-data">
16
- <input type="file" name="file">
17
- <input type="submit" value="Convert">
18
- </form>
19
- ''')
20
-
21
- @app.route('/process', methods=['POST'])
22
- def process_document():
23
- if 'file' not in request.files:
24
- return jsonify({"status": "error", "message": "No file part"}), 400
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
- file = request.files['file']
27
- if file.filename == '':
28
- return jsonify({"status": "error", "message": "No selected file"}), 400
 
 
 
 
 
 
 
29
 
30
- # Save file temporarily
31
- temp_path = f"/tmp/{file.filename}"
32
- file.save(temp_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
  try:
35
- # Convert the document
36
- result = converter.convert(temp_path)
37
-
38
- # Return the exported text (or other format)
39
- return jsonify({
40
- "status": "success",
41
- "content": result.document.export_to_markdown()
42
- })
43
- except Exception as e:
44
- return jsonify({"status": "error", "message": str(e)}), 500
45
- finally:
 
46
  # Cleanup
47
- if os.path.exists(temp_path):
48
- os.remove(temp_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
  if __name__ == "__main__":
51
- app.run(host="0.0.0.0", port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Unified Document Extraction API - Docling + DocStrange
3
+ Deploy this as a SINGLE app on Hugging Face Spaces
4
+ Provides both Docling AND DocStrange extraction in one service
5
+ """
6
  import os
7
+ import sys
8
+ import tempfile
9
+ from pathlib import Path
10
 
11
+ from fastapi import FastAPI, File, UploadFile, HTTPException, Query
12
+ from fastapi.responses import JSONResponse
13
+ from fastapi.middleware.cors import CORSMiddleware
14
+ import uvicorn
15
 
16
+ # ============================================================================
17
+ # INITIALIZATION
18
+ # ============================================================================
19
 
20
+ # Docling setup
21
+ HAS_DOCLING = False
22
+ docling_converter = None
23
+ try:
24
+ from docling.document_converter import DocumentConverter
25
+ HAS_DOCLING = True
26
+ except ImportError:
27
+ pass
28
+
29
+ # DocStrange setup
30
+ HAS_DOCTSTRANGE = False
31
+ docstrange_extractor = None
32
+ try:
33
+ # Add docstrange to path
34
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'docstrange'))
35
+ from docstrange import DocumentExtractor
36
+ HAS_DOCTSTRANGE = True
37
+ except ImportError:
38
+ pass
39
+
40
+ app = FastAPI(
41
+ title="Unified Document Extraction API",
42
+ description="Extract documents using Docling OR DocStrange AI engines",
43
+ version="2.0.0"
44
+ )
45
+
46
+ # Allow CORS for DataSync integration
47
+ app.add_middleware(
48
+ CORSMiddleware,
49
+ allow_origins=["*"],
50
+ allow_credentials=True,
51
+ allow_methods=["*"],
52
+ allow_headers=["*"],
53
+ )
54
+
55
+
56
+ # ============================================================================
57
+ # LAZY INITIALIZATION
58
+ # ============================================================================
59
+
60
+ def get_docling_converter():
61
+ """Get or create Docling converter"""
62
+ global docling_converter
63
+ if docling_converter is None and HAS_DOCLING:
64
+ docling_converter = DocumentConverter()
65
+ return docling_converter
66
+
67
+
68
+ def get_docstrange_extractor():
69
+ """Get or create DocStrange extractor"""
70
+ global docstrange_extractor
71
+ if docstrange_extractor is None and HAS_DOCTSTRANGE:
72
+ # Auto-detect GPU
73
+ try:
74
+ import torch
75
+ gpu = torch.cuda.is_available()
76
+ except:
77
+ gpu = False
78
+ docstrange_extractor = DocumentExtractor(gpu=gpu)
79
+ return docstrange_extractor
80
+
81
+
82
+ # ============================================================================
83
+ # HEALTH & INFO ENDPOINTS
84
+ # ============================================================================
85
+
86
+ @app.get("/")
87
+ def root():
88
+ """Health check"""
89
+ return {
90
+ "status": "ok",
91
+ "service": "Unified Document Extraction API",
92
+ "version": "2.0.0",
93
+ "engines": {
94
+ "docling": HAS_DOCLING,
95
+ "docstrange": HAS_DOCTSTRANGE
96
+ }
97
+ }
98
+
99
+
100
+ @app.get("/health")
101
+ def health():
102
+ """Detailed health check"""
103
+ try:
104
+ import torch
105
+ gpu = torch.cuda.is_available()
106
+ vram = f"{torch.cuda.get_device_properties(0).total_mem/1024**3:.1f}GB" if gpu else "N/A"
107
+ except:
108
+ gpu = False
109
+ vram = "N/A"
110
 
111
+ return {
112
+ "status": "ok",
113
+ "gpu": gpu,
114
+ "vram": vram,
115
+ "engines": {
116
+ "docling": HAS_DOCLING,
117
+ "docstrange": HAS_DOCTSTRANGE
118
+ }
119
+ }
120
+
121
 
122
+ @app.get("/engines")
123
+ def list_engines():
124
+ """List available extraction engines"""
125
+ return {
126
+ "engines": [
127
+ {
128
+ "id": "docling",
129
+ "name": "Docling AI",
130
+ "available": HAS_DOCLING,
131
+ "description": "Advanced document parsing with structure preservation"
132
+ },
133
+ {
134
+ "id": "docstrange",
135
+ "name": "DocStrange",
136
+ "available": HAS_DOCTSTRANGE,
137
+ "description": "GPU-accelerated intelligent document processing"
138
+ }
139
+ ]
140
+ }
141
+
142
+
143
+ # ============================================================================
144
+ # EXTRACTION ENDPOINTS
145
+ # ============================================================================
146
+
147
+ @app.post("/convert")
148
+ async def convert_document(
149
+ file: UploadFile = File(...),
150
+ engine: str = Query("docling", description="Extraction engine: docling or docstrange"),
151
+ output_format: str = Query("markdown", description="Output format: markdown, json, tables")
152
+ ):
153
+ """
154
+ Convert document using specified engine
155
+
156
+ Args:
157
+ file: Document file (PDF, DOCX, XLSX, Images, etc.)
158
+ engine: docling or docstrange
159
+ output_format: markdown, json, tables
160
+
161
+ Returns: JSON with extracted data
162
+ """
163
+ if not file.filename:
164
+ raise HTTPException(status_code=400, detail="No file provided")
165
+
166
+ # Validate engine
167
+ if engine not in ['docling', 'docstrange']:
168
+ raise HTTPException(status_code=400, detail=f"Unknown engine: {engine}. Use 'docling' or 'docstrange'")
169
+
170
+ # Check engine availability
171
+ if engine == 'docling' and not HAS_DOCLING:
172
+ raise HTTPException(status_code=503, detail="Docling engine not available")
173
+ if engine == 'docstrange' and not HAS_DOCTSTRANGE:
174
+ raise HTTPException(status_code=503, detail="DocStrange engine not available")
175
+
176
+ # Validate file extension
177
+ supported_extensions = ['.pdf', '.docx', '.xlsx', '.pptx', '.png', '.jpg', '.jpeg',
178
+ '.bmp', '.tiff', '.webp', '.gif', '.txt', '.html', '.md', '.csv']
179
+ ext = Path(file.filename).suffix.lower()
180
+ if ext not in supported_extensions:
181
+ raise HTTPException(status_code=400, detail=f"Unsupported format: {ext}")
182
 
183
  try:
184
+ # Save uploaded file temporarily
185
+ with tempfile.NamedTemporaryFile(delete=False, suffix=ext) as tmp:
186
+ content = await file.read()
187
+ tmp.write(content)
188
+ tmp_path = tmp.name
189
+
190
+ # Extract using selected engine
191
+ if engine == 'docling':
192
+ result = _extract_with_docling(tmp_path, output_format)
193
+ else: # docstrange
194
+ result = _extract_with_docstrange(tmp_path, output_format)
195
+
196
  # Cleanup
197
+ os.unlink(tmp_path)
198
+
199
+ return JSONResponse(content=result)
200
+
201
+ except Exception as e:
202
+ # Cleanup on error
203
+ if 'tmp_path' in locals():
204
+ try:
205
+ os.unlink(tmp_path)
206
+ except:
207
+ pass
208
+
209
+ raise HTTPException(status_code=500, detail=f"Extraction failed: {str(e)}")
210
+
211
+
212
+ @app.post("/convert/markdown")
213
+ async def convert_to_markdown(
214
+ file: UploadFile = File(...),
215
+ engine: str = Query("docling", description="docling or docstrange")
216
+ ):
217
+ """Extract document to markdown only (lightweight endpoint)"""
218
+ try:
219
+ with tempfile.NamedTemporaryFile(delete=False, suffix=Path(file.filename).suffix.lower()) as tmp:
220
+ content = await file.read()
221
+ tmp.write(content)
222
+ tmp_path = tmp.name
223
+
224
+ if engine == 'docling' and HAS_DOCLING:
225
+ converter = get_docling_converter()
226
+ result = converter.convert(tmp_path)
227
+ markdown = result.document.export_to_markdown()
228
+ elif engine == 'docstrange' and HAS_DOCTSTRANGE:
229
+ ext = get_docstrange_extractor()
230
+ result = ext.extract_document(tmp_path, output_format='markdown')
231
+ markdown = result.get('data', '')
232
+ else:
233
+ raise HTTPException(status_code=503, detail=f"{engine} engine not available")
234
+
235
+ os.unlink(tmp_path)
236
+
237
+ return {
238
+ "success": True,
239
+ "markdown": markdown,
240
+ "engine": engine,
241
+ "file_name": file.filename
242
+ }
243
+
244
+ except Exception as e:
245
+ if 'tmp_path' in locals():
246
+ try:
247
+ os.unlink(tmp_path)
248
+ except:
249
+ pass
250
+ raise HTTPException(status_code=500, detail=str(e))
251
+
252
+
253
+ @app.post("/convert/tables")
254
+ async def convert_tables(
255
+ file: UploadFile = File(...),
256
+ engine: str = Query("docling", description="docling or docstrange")
257
+ ):
258
+ """Extract tables only from document"""
259
+ try:
260
+ with tempfile.NamedTemporaryFile(delete=False, suffix=Path(file.filename).suffix.lower()) as tmp:
261
+ content = await file.read()
262
+ tmp.write(content)
263
+ tmp_path = tmp.name
264
+
265
+ tables_data = []
266
+
267
+ if engine == 'docling' and HAS_DOCLING:
268
+ converter = get_docling_converter()
269
+ result = converter.convert(tmp_path)
270
+ for table_idx, table in enumerate(result.document.tables):
271
+ try:
272
+ df = table.export_to_dataframe()
273
+ tables_data.append({
274
+ "table_index": table_idx,
275
+ "headers": list(df.columns),
276
+ "rows": df.to_dict('records'),
277
+ "row_count": len(df)
278
+ })
279
+ except:
280
+ pass
281
+
282
+ os.unlink(tmp_path)
283
+
284
+ return {
285
+ "success": True,
286
+ "tables": tables_data,
287
+ "tables_count": len(tables_data),
288
+ "engine": engine,
289
+ "file_name": file.filename
290
+ }
291
+
292
+ except Exception as e:
293
+ if 'tmp_path' in locals():
294
+ try:
295
+ os.unlink(tmp_path)
296
+ except:
297
+ pass
298
+ raise HTTPException(status_code=500, detail=str(e))
299
+
300
+
301
+ # ============================================================================
302
+ # ENGINE-SPECIFIC EXTRACTION FUNCTIONS
303
+ # ============================================================================
304
+
305
+ def _extract_with_docling(file_path, output_format):
306
+ """Extract using Docling"""
307
+ converter = get_docling_converter()
308
+ result = converter.convert(file_path)
309
+ doc = result.document
310
+
311
+ response = {
312
+ "success": True,
313
+ "file_name": os.path.basename(file_path),
314
+ "engine": "docling",
315
+ "format": output_format,
316
+ "document": {
317
+ "markdown": doc.export_to_markdown(),
318
+ "num_pages": len(doc.pages) if hasattr(doc, 'pages') else 0,
319
+ "tables_count": len(doc.tables)
320
+ },
321
+ "metadata": {
322
+ "engine": "docling",
323
+ "model": "docling-default"
324
+ }
325
+ }
326
+
327
+ # Add tables if requested
328
+ if output_format in ['json', 'tables']:
329
+ tables_data = []
330
+ for table_idx, table in enumerate(doc.tables):
331
+ try:
332
+ df = table.export_to_dataframe()
333
+ tables_data.append({
334
+ "table_index": table_idx,
335
+ "rows": df.to_dict('records'),
336
+ "row_count": len(df)
337
+ })
338
+ except:
339
+ pass
340
+ response['document']['tables'] = tables_data
341
+
342
+ return response
343
+
344
+
345
+ def _extract_with_docstrange(file_path, output_format):
346
+ """Extract using DocStrange"""
347
+ ext = get_docstrange_extractor()
348
+ result = ext.extract_document(file_path, output_format=output_format)
349
+
350
+ response = {
351
+ "success": True,
352
+ "file_name": os.path.basename(file_path),
353
+ "engine": "docstrange",
354
+ "format": result.get('format', output_format),
355
+ "data": result.get('data', {}),
356
+ "metadata": {
357
+ "engine": "docstrange",
358
+ "file_size": result.get('metadata', {}).get('file_size', 0),
359
+ "gpu_mode": result.get('metadata', {}).get('gpu_mode', False)
360
+ }
361
+ }
362
+
363
+ return response
364
+
365
+
366
+ # ============================================================================
367
+ # MAIN ENTRY POINT
368
+ # ============================================================================
369
 
370
  if __name__ == "__main__":
371
+ print("\n" + "="*60)
372
+ print("Unified Document Extraction API")
373
+ print("="*60)
374
+ print(f"Docling: {'βœ… Available' if HAS_DOCLING else '❌ Not installed'}")
375
+ print(f"DocStrange: {'βœ… Available' if HAS_DOCTSTRANGE else '❌ Not installed'}")
376
+ print("="*60)
377
+ print("URL: http://localhost:7860")
378
+ print("Docs: http://localhost:7860/docs")
379
+ print("="*60 + "\n")
380
+
381
+ uvicorn.run(
382
+ "app:app",
383
+ host="0.0.0.0",
384
+ port=7860
385
+ )
requirements.txt CHANGED
@@ -1,3 +1,7 @@
1
- flask
2
- docling
3
- docstrange
 
 
 
 
 
1
+ docling>=2.88.0
2
+ fastapi>=0.100.0
3
+ uvicorn>=0.23.0
4
+ python-multipart>=0.0.6
5
+ pandas>=2.0.0
6
+ pillow>=10.0.0
7
+ torch>=2.0.0