Doc2SFT is a resilient, fault-tolerant, and hardware-optimized data generation pipeline. It automatically ingests raw PDF documents and utilizes Local LLMs (via the official Ollama AsyncClient) to autonomously generate high-quality, hallucination-free QA (Direct Answer) or CoT (Chain-of-Thought) training datasets in the standard ShareGPT format, ready for immediate model fine-tuning.
The Open Source community has access to incredible Small and Mid-size Language Models like Qwen2 (lightweight 1.5B/7B), Llama-3 (8B), and highly capable mid-range models like Qwen3 (14B). However, the biggest bottlenecks for fine-tuning these models on proprietary enterprise data are Data Privacy and Data Quality.
When asking an AI to generate training data from raw proprietary text, standard scripts and cloud APIs fail due to:
- The Cloud Privacy Breach: Sending internal enterprise PDFs to external cloud APIs exposes highly sensitive IP and trade secrets to third-party model providers.
- JSON Formatting Collapse: Smaller local models frequently break JSON structures, causing offline pipelines to crash entirely. (Solved via Native JSON Grammar Masking).
- Chunk Myopia: Text chunking destroys document-wide context, leading to inaccurate QA pairs.
- The "Garbage In" Problem: Without strict validation, LLMs generate hallucinated or low-quality data.
- Hardware Exhaustion: Processing long contexts rapidly overwhelms Unified Memory on edge devices or standard GPUs. (Solved via Async Semaphore Queues).
Doc2SFT solves this. It is an offline-first, 100% locally isolated solution. By integrating directly with local LLMs via Ollama, it guarantees that your proprietary input data never leaves your infrastructure or gets exposed to the cloud.
Engineered as a bulletproof state-machine, it safely extracts golden data across any environmentβfrom an M3 MacBook Air with 16 GB RAM running a lightweight 1.5B model, up to ultra-large scale configurations on enterprise cluster infrastructure. You get cloud-grade dataset generation with absolute, air-gapped data security.
graph TD
subgraph Input Phase
A[Raw PDF Documents] --> B(Pure-Python pypdf Extractor)
B --> C(LangChain Semantic Chunking)
B --> D(Global Context Map Generator)
end
subgraph Asynchronous Generation Loop
C --> E{Async Semaphore Queue}
D -.->|Injected Context| F
E --> F[Local LLM / Ollama AsyncClient]
F --> G{Pydantic V2 Structural Check}
end
subgraph Validation & Self-Healing
G -- Fails --> H[Context-Isolated Retry Engine]
H -->|Injects pydantic_errors| F
H -->|Retries = 3| I((Quarantine Chunk))
G -- Passes --> J{AI Judge Quality Check}
end
subgraph Telemetry & Output Phase
I -.-> Z[traceability.jsonl Logs]
J -- Score < Target --> K((Discard Hallucinated Data))
J -- Score >= Target --> L[Async File Lock]
L --> M[dataset_qa.jsonl]
L --> N[(state.json Gap Tracking)]
end
Doc2SFT is built on five highly isolated, fault-tolerant architectural blocks that power the end-to-end flow above.
Before chunking begins, the pipeline extracts all text and forces the LLM to generate a cross-document map. This solves the "Chunk Myopia" problem by giving the AI situational awareness of the entire domain, even when it is only processing a small 500-word slice.
graph LR
A[Doc 1] & B[Doc 2] & C[Doc N] --> D[pypdf Text Extraction]
D --> E[LLM: Analyze Dependencies & Intersections]
E --> F[(Global Context Map)]
F -.->|Injected as System Prompt| G[Chunk Generation 1]
F -.->|Injected as System Prompt| H[Chunk Generation 2]
To maximize GPU/Unified Memory utilization without causing out-of-memory (OOM) crashes, chunk processing is handled by an asynchronous semaphore queue. Concurrency limits are strictly controlled by the .env configuration.
sequenceDiagram
participant Main as Pipeline Core
participant Sem as Async Semaphore (Limit=N)
participant LLM as Ollama AsyncClient
Main->>Sem: Request execution slot for Chunk hash_id
Sem-->>Main: Slot granted
Main->>LLM: Await async generate()
Note over LLM: Hardware processes<br/>N chunks concurrently
LLM-->>Main: Response returned
Main->>Sem: Release slot for next Chunk
Lightweight models frequently add conversational fluff or drop brackets. Doc2SFT enforces a strict C-level JSON Grammar Mask (format="json") directly into the Ollama inference engine. If the output still violates schema, Pydantic V2 isolates the exact error (e.g., [messages -> 1 -> role] Field required) to trigger the self-healing loops.
graph TD
A[Ollama JSON Mask format='json'] --> B[Omni-Extractor Boundary Check]
B --> C{Pydantic V2 Schema Match}
C -- ValidationError --> D[Extract e.errors]
D --> E[Trigger Retry Engine]
C -- Success --> F[Validated JSON Objects]
If JSON validation fails, the pipeline does not recursively append error messages to the old prompt (which bloats context and confuses small models). Instead, it isolates the exact Pydantic error stack and re-injects it against a pristine base_prompt.
graph TD
A[Pydantic Schema Collapse] --> B{Check Remaining Retries?}
B -- Yes > 0 --> C[Re-inject pristine base_prompt <br/>+ exact pydantic_errors stack]
C --> D[Retry API Call]
D --> E((Validation Loop))
B -- No == 0 --> F[QUARANTINE CHUNK]
F --> G[1. Log to traceability.jsonl]
F --> H[2. Discard Corrupted Data]
F --> I[3. Exclude from state.json]
Even if an LLM outputs perfect JSON, it may hallucinate facts. Doc2SFT deploys a secondary, zero-temperature "Auditor" prompt to ruthlessly grade the generated data against the original ground-truth chunk. Failed chunks are safely quarantined, and the state.json file deliberately leaves "gaps" in its tracking arrays. This allows you to restart the pipeline anytime, and it will automatically target only the missing/quarantined chunks.
sequenceDiagram
participant P as Pipeline
participant J as AI Judge (JSON Mode)
P->>J: Transmit [Source Chunk] + [Generated QA Pair]
Note over J: Prompt: "Act as strict AI Auditor. Score 1 to 5."
J-->>P: Returns JSON: {"score": 4}
alt Score >= MIN_QUALITY_SCORE
P->>P: Data Approved -> Async Lock -> Append to .jsonl
else Score < MIN_QUALITY_SCORE
P->>P: Hallucination Detected -> Silently Discard Data
end
Doc2SFT/
βββ data_input/ # Drop your source PDFs here (Git ignored)
βββ data_output/ # Final ShareGPT SFT training datasets saved here
βββ logs/
β βββ traceability.jsonl # Enterprise JSON telemetry for error analysis
β βββ state.json # Gap-tracking state ledger for zero-loss recovery
βββ .env.example.m3air... # Blueprint template for lightweight edge hardware
βββ .env.example.m5pro... # Blueprint template for workstation hardware
βββ .gitignore # Enforces strict data and token credential isolation
βββ analyze_logs.py # Diagnostic script to analyze Pydantic failures
βββ generate_data.py # Core asynchronous framework file
βββ requirements.txt # Verified project dependency configuration
- Python 3.10+
- Ollama installed and running locally (or pointing to a remote server).
- Pull your target model:
ollama pull qwen3:14b(or your model of choice).
# Clone the repository
git clone [https://github.com/yourusername/Doc2SFT.git](https://github.com/yourusername/Doc2SFT.git)
cd Doc2SFT
# Install the upgraded asynchronous dependencies
pip install -r requirements.txt
# Create necessary directories to preserve git structure
mkdir -p data_input data_output logsDoc2SFT is deeply customizable. Copy a provided blueprint to create your active .env file based on your hardware:
For Edge Devices (e.g., M3 Air with 16GB RAM):
cp .env.example.m3air_qwen2-1.5b .envFor Workstations (e.g., M5 Pro with 24GB RAM):
cp .env.example.m5pro_qwen3-14b .envOpen the .env file and tune the parameters to match your hardware and dataset goals.
LLM & Endpoint Configuration
OLLAMA_HOST: The URL of your local or remote Ollama instance (default:http://localhost:11434).OLLAMA_MODEL: The model used for generation and judging (e.g.,qwen2:1.5bfor edge hardware,qwen3:14bor larger for workstations).
Pipeline Behavior
TARGET_YIELD: The total number of high-quality data pairs you want extracted across all documents.GENERATION_STYLE: Set toqafor direct knowledge-extraction, orcotfor reasoning paths.SYSTEM_PROMPT: Hardcode your target persona to prevent model drift (e.g.,"You are an expert AI governance assistant."), or set toautoto let large models infer the domain dynamically.
Quality & Hardware Constraints (CRITICAL)
MIN_QUALITY_SCORE: (Scale 1-5). Sets the threshold for the AI Judge. Set to1to bypass the judge entirely if you want maximum speed on small models, or4for strict enterprise quality using mid-range models (14B+).CONCURRENCY_LIMIT: Controls async chunking execution. Set to1for 16GB Unified Memory (M2/M3 MacBooks) to prevent swap thrashing and lockups. Scale up to2-5if running on high-VRAM clusters or M5 Pro setups.OLLAMA_NUM_CTX_QA: Context window size. The pipeline dynamically routes this to the AsyncClient to prevent memory truncation. Set carefully based on your VRAM (e.g.,4096for standard QA,8192for CoT).
- Input: Drop your target
.pdffiles into the/data_inputfolder. - Execute: Start the generation process.
python generate_data.py
- Output: Your pristine, fine-tune-ready data will be saved in
/data_output/dataset_qa.jsonl(ordataset_cot.jsonl). - Diagnose: Run
python analyze_logs.pyto view an aggregated report of hardware latency, retry metrics, and specific JSON structural collapses.
Note: If the script is interrupted, simply run it again. The asynchronous state.json tracker will automatically pick up exactly where it left off, aggressively targeting only the gaps/quarantined chunks.
The generated data is perfectly structured for immediate ingestion into frameworks like Axolotl, MLX, Llama-Factory, or Unsloth:
{
"messages": [
{
"role": "system",
"content": "You are an expert AI governance and machine learning architecture assistant."
},
{
"role": "user",
"content": "Identify the robustness challenge for LLMs in the provided use cases."
},
{
"role": "assistant",
"content": "Stability under adversarial conditions and prompt-injection attacks."
}
]
}Doc2SFT thrives on community input! Whether you are optimizing token usage, adding support for new document types (.docx, .md), or integrating alternative inference endpoints (vLLM, OpenAI API), your contributions are welcome.
How to contribute:
- Fork the repository.
- Create a feature branch (
git checkout -b feature/AmazingFeature). - Commit your changes (
git commit -m 'Add some AmazingFeature'). - Push to the branch (
git push origin feature/AmazingFeature). - Open a Pull Request.
When opening a PR, please ensure your code handles asynchronous state locks safely, as data purity is the primary directive of this project.
Distributed under the MIT License. See LICENSE for more information.