Skip to main content

LLM-powered CSV enrichment made easy

Project description

RicherText

Turn raw CSV data into enriched insights using LLMs. Classify, label, score, summarize, and analyze your data at scale.

Before:                              After:
┌─────────────────────────────┐      ┌─────────────────────────────────────────────────────────┐
│ title        │ description  │      │ title        │ seniority │ department │ skills         │
├─────────────────────────────┤  →   ├─────────────────────────────────────────────────────────┤
│ Sr Engineer  │ Python, AWS  │      │ Sr Engineer  │ senior    │ engineering│ python;aws     │
│ Sales Rep    │ Outbound...  │      │ Sales Rep    │ entry     │ sales      │ communication  │
└─────────────────────────────┘      └─────────────────────────────────────────────────────────┘

100 records × 8 enrichments = 800 LLM calls in ~8 seconds with automatic rate limiting and resume capability.


Quick Start

1. Install

pip install richertext

2. Set your API key

export GOOGLE_API_KEY="your-gemini-api-key"

# Or add to .env file (auto-loaded by richertext)
echo 'GOOGLE_API_KEY=your-gemini-api-key' >> .env

Get a free API key at Google AI Studio.

3. Initialize and run

richertext init my-project
cd my-project
richertext run input/job_postings_10.csv --config taskflows/job_postings_tasks.yaml -v

That's it. Check output/job_postings_10_enriched.csv for results.


What You Get

The init command creates a complete working example:

my-project/
├── .env                              # Your API key goes here
├── taskflows/
│   └── job_postings_tasks.yaml       # Enrichment configuration
├── prompts/
│   └── job_postings_prompts.yaml     # Prompt templates
├── input/
│   ├── job_postings_10.csv           # 10 sample records
│   └── job_postings_100.csv          # 100 sample records
├── output/                           # Enriched results
└── docs/                             # Documentation
    ├── README.md
    ├── TUTORIAL.md
    └── ENRICHMENTS.md

The sample taskflow enriches job postings with:

Enrichment Type Output
seniority classifier entry, mid, senior, executive
department classifier engineering, sales, hr, etc.
skills labeler python;aws;sql (multiple)
experience_required scorer 7 (1-10 scale)
technical_complexity scorer 8 (1-10 scale)
salary_estimate scorer 6 (1-10 scale)
candidate_profile summarizer "Ideal candidate is..."
remote_friendly classifier remote, hybrid, onsite, unclear (inline prompt)

Use Cases

  • Lead scoring - Classify prospects by fit, urgency, budget
  • Customer feedback - Sentiment, topics, urgency detection
  • Content categorization - Auto-tag articles, support tickets, documents
  • Resume screening - Skills extraction, experience level, role fit
  • Product reviews - Sentiment, feature mentions, competitor comparisons
  • Survey analysis - Theme extraction, satisfaction scoring

Configuration

Taskflow Structure

# taskflows/job_postings_tasks.yaml
provider:
  type: gemini
  model: gemini-2.5-flash-lite  # Fast and cheap

prompts_file: ../prompts/job_postings_prompts.yaml  # External prompts

enrichments:
  - name: sentiment
    type: classifier
    prompt_key: sentiment_prompt      # References job_postings_prompts.yaml
    categories: [positive, negative, neutral]
    input_columns: [review_text]

  - name: themes
    type: labeler
    labels: [pricing, quality, support, shipping]
    input_columns: [review_text]
    prompt: |                         # Or inline prompt
      What themes appear in this review?

      {review_text}

Enrichment Types

Type What it does Output
classifier Pick ONE category sentiment_category
labeler Pick MULTIPLE labels themes_labels (semicolon-separated)
summarizer Condense text summary_summary
scorer Rate on a scale (1-10) quality_score
reasoner Free-form analysis analysis_analysis

Prompts File

# prompts/job_postings_prompts.yaml
sentiment_prompt: |
  Classify the sentiment of this review:

  {review_text}

summary_prompt: |
  Summarize in 1-2 sentences:

  {review_text}

Use {column_name} to inject CSV columns into prompts.


Python API

For programmatic use without config files:

from richertext import classify, summarize, score, label, reason

# Single-record functions
result = classify("I love this product!", categories=["positive", "negative"])
print(result["category"])  # "positive"

summary = summarize("Long article text here...", max_length=100)

clarity = score("Evaluate this essay", prompt="Rate the clarity of: {text}")
# 8

labels = label("Tech news about AI", labels=["tech", "politics", "sports"])
# {"labels": ["tech"]}

analysis = reason("Q3 revenue: $2.1M, down 5% YoY")
# Free-form analysis text

Batch Processing

from richertext import enrich_csv, enrich_records

# From CSV file
enrich_csv("reviews.csv", config="taskflows/sentiment.yaml")

# From list of dicts (custom loading)
records = [
    {"id": 1, "text": "Great product!"},
    {"id": 2, "text": "Terrible experience"},
]
enriched = enrich_records(records, config="taskflows/sentiment.yaml")

# With resume capability (restart interrupted jobs)
enriched = enrich_records(
    records,
    config="taskflows/sentiment.yaml",
    output_path="output/enriched.csv",
    pk_field="id"  # Skip already-processed records
)

Features

Automatic Rate Limiting

Built-in token bucket rate limiting respects Gemini API limits:

Model Limit Burst
gemini-2.5-flash-lite 4,000 RPM 360 req
gemini-2.5-flash 1,000 RPM 90 req
gemini-2.5-pro 150 RPM 13 req

No 429 errors. No manual throttling.

Parallel Processing

Worker count is automatically calculated based on the model's rate limit:

Model Rate Limit Default Workers
gemini-2.5-flash-lite 4,000 RPM 133 workers
gemini-2.5-flash 1,000 RPM 33 workers
gemini-2.5-pro 150 RPM 5 workers

Override with --workers if needed:

richertext run data.csv --config job_postings_tasks.yaml --workers 50

Resume Capability

Interrupted? Just re-run the same command. RicherText reads the output file, extracts the --pk-field values already processed, and skips those records:

# First run - processes 50 records, then interrupted (Ctrl+C)
richertext run data.csv --config job_postings_tasks.yaml --pk-field id
# Output: output/data_enriched.csv (50 rows)

# Second run - reads output file, skips 50 already done, continues with remaining
richertext run data.csv --config job_postings_tasks.yaml --pk-field id
# Output: appends to output/data_enriched.csv

The --pk-field must match a column in your CSV (default: id).

Chaining Enrichments

You can reference output from earlier enrichments in later ones. This is not automatic - you must explicitly include the output column in input_columns:

enrichments:
  - name: language
    type: classifier
    categories: [english, spanish, french]
    input_columns: [text]
    output_column: detected_language  # Creates 'detected_language' column

  - name: summary
    type: summarizer
    input_columns: [text, detected_language]  # Explicitly include previous output
    prompt: |
      Summarize this {detected_language} text:

      {text}

CLI Reference

# Initialize new project
richertext init [project_name]

# Run enrichment
richertext run <input.csv> --config <taskflow.yaml> [options]

Options:
  -c, --config FILE      Taskflow config (required)
  -o, --output FILE      Output path (default: output/<input>_enriched.csv)
  -v, --verbose          Show progress
  -w, --workers N        Parallel workers (default: auto based on model)
  --pk-field FIELD       Primary key for resume (default: id)

Troubleshooting

"No API key found"

export GOOGLE_API_KEY="your-key"
# Or add to .env file

"Prompts file not found"

  • Path is relative to the taskflow location
  • Use ../prompts/job_postings_prompts.yaml if prompts dir is sibling to taskflows

Rate limit errors

  • The built-in rate limiter should prevent these
  • If you see 429s, reduce --workers

Resuming doesn't skip records

  • Make sure --pk-field matches a column in your CSV
  • Check that output file exists from previous run

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

richertext-0.1.1.tar.gz (43.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

richertext-0.1.1-py3-none-any.whl (48.5 kB view details)

Uploaded Python 3

File details

Details for the file richertext-0.1.1.tar.gz.

File metadata

  • Download URL: richertext-0.1.1.tar.gz
  • Upload date:
  • Size: 43.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for richertext-0.1.1.tar.gz
Algorithm Hash digest
SHA256 e0369c716308e224121101105c359a83498dc8a57d7769beede4269a9b050c9d
MD5 b551066f0129adeb6cf342a169cbdea9
BLAKE2b-256 76a44d7ca705799c1e4078fab8daa29cbf76eaa7b4f4f1631116d841e093cb2f

See more details on using hashes here.

File details

Details for the file richertext-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: richertext-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 48.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for richertext-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 46dedaccce39a75f16db3f38a50d958b7dbc0c51d3a3f6b4783564b77299601a
MD5 ee6662a6e7a6919b4e9719a14b991343
BLAKE2b-256 9c67b4261c6bcbd66fef5112566e4d5ecd0921bf65fc53c4eed3a15a838ad9df

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page