Skip to main content

A simple SDK for building recursive language models with OpenAI

Project description

Recursive Language Model (RLM)

A Python implementation demonstrating how to build a recursive language model using OpenAI's API with function calling capabilities. This pattern allows the model to search and retrieve information from large documents without loading the entire document into the context window.

Overview

This project implements a Recursive Language Model (RLM) that:

  • Processes large documents efficiently by using tool-based retrieval
  • Enables the model to search and peek into documents dynamically
  • Reduces token usage by only loading relevant document sections
  • Demonstrates iterative tool calling with OpenAI's API

How It Works

Core Concept

Instead of sending an entire large document to the model, this implementation provides the model with two tools:

  1. search: Find text patterns using regex in the document
  2. peek: Retrieve specific sections of the document by byte offset

The model autonomously decides when and how to use these tools to answer questions about the document.

Architecture

┌─────────────┐
│   User      │
│  Question   │
└──────┬──────┘
       │
       ▼
┌─────────────────────────────────────┐
│  run_rlm(question)                  │
│  ┌───────────────────────────────┐  │
│  │ 1. Initial API call           │  │
│  │    with question & tools      │  │
│  └───────────┬───────────────────┘  │
│              │                       │
│  ┌───────────▼───────────────────┐  │
│  │ 2. Tool loop                  │  │
│  │    - Model requests tools     │  │
│  │    - Execute search/peek      │  │
│  │    - Send results back        │  │
│  │    - Repeat until answer      │  │
│  └───────────┬───────────────────┘  │
│              │                       │
│  ┌───────────▼───────────────────┐  │
│  │ 3. Return final answer        │  │
│  └───────────────────────────────┘  │
└─────────────────────────────────────┘

Installation

Prerequisites

  • Python 3.7+
  • OpenAI API key

Setup

Option 1: Install from source

git clone https://github.com/damodharm10/RLM.git
cd RLM
pip install -e .

Option 2: Direct installation

pip install openai

Configure API Key

Set your OpenAI API key as an environment variable:

export OPENAI_API_KEY='your-api-key-here'

Or on Windows:

set OPENAI_API_KEY=your-api-key-here

Usage

Using the SDK (Recommended)

The easiest way to use RLM is through the provided SDK:

from rlm_sdk import RLMClient

# Initialize the client
client = RLMClient()

# Load a document
client.load_document_from_file("your_document.txt")

# Ask questions
answer = client.query("What is the main topic of this document?")
print(answer)

Quick Query (One-liner)

For simple use cases:

from rlm_sdk import quick_query

answer = quick_query("document.txt", "Summarize the key points.")
print(answer)

Advanced Usage

from rlm_sdk import RLMClient, RLMDocument

# Initialize with custom settings
client = RLMClient(
    model="gpt-4o-mini",
    max_iterations=15
)

# Load from text
text = "Your document content here..."
client.load_document_from_text(text, name="MyDoc")

# Query with verbose mode to see tool calls
answer = client.query(
    "What are the key findings?",
    verbose=True
)
print(answer)

# Get document info
info = client.get_document_info()
print(f"Document: {info['name']}, Length: {info['length']} chars")

Original Implementation Example

If you prefer the original implementation:

from rlm import run_rlm

# Ask a question about your document
answer = run_rlm("Summarize the key steps in the process described in DOC.")
print(answer)

Complete Implementation

import os, re, json
from openai import OpenAI

client = OpenAI()

# Load your document
DOC = open("big_document.txt", "r", encoding="utf-8").read()

def tool_search(pattern: str, max_hits: int = 10):
    """Search for regex pattern in document."""
    hits = []
    for m in re.finditer(pattern, DOC, flags=re.IGNORECASE):
        hits.append({"start": m.start(), "end": m.end()})
        if len(hits) >= max_hits:
            break
    return {"hits": hits}

def tool_peek(start: int, end: int):
    """Retrieve document section from start to end offset."""
    start = max(0, start)
    end = min(len(DOC), end)
    return {"text": DOC[start:end], "start": start, "end": end}

# Define tools for the model
TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "search",
            "description": "Find locations of a regex/text pattern in DOC. Return start/end offsets.",
            "parameters": {
                "type": "object",
                "properties": {
                    "pattern": {"type": "string"},
                    "max_hits": {"type": "integer", "default": 10}
                },
                "required": ["pattern"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "peek",
            "description": "Return DOC substring for [start,end). Keep end-start small (e.g., <= 2000 chars).",
            "parameters": {
                "type": "object",
                "properties": {
                    "start": {"type": "integer"},
                    "end": {"type": "integer"}
                },
                "required": ["start", "end"]
            }
        }
    }
]

def run_rlm(question: str):
    """Run recursive language model query."""
    # First request
    resp = client.responses.create(
        model="gpt-4.1-mini",
        input=[{
            "role": "system",
            "content": "You cannot see DOC. Use tools search/peek to inspect DOC and answer."
        },{
            "role": "user",
            "content": question
        }],
        tools=TOOLS
    )

    # Tool loop
    while True:
        # If model already produced final text, return it
        if getattr(resp, "output_text", None):
            return resp.output_text

        # Otherwise handle tool calls in resp.output
        tool_outputs = []
        for item in resp.output:
            if item.type == "tool_call" and item.tool_name in ("search", "peek"):
                args = json.loads(item.arguments)

                if item.tool_name == "search":
                    result = tool_search(**args)
                else:
                    result = tool_peek(**args)

                tool_outputs.append({
                    "type": "tool_result",
                    "tool_call_id": item.id,
                    "output": json.dumps(result)
                })

        # Send tool results back to model, continue the same response thread
        resp = client.responses.create(
            model="gpt-4.1-mini",
            previous_response_id=resp.id,
            input=tool_outputs,
            tools=TOOLS
        )

# Example usage
print(run_rlm("Summarize the key steps in the process described in DOC."))

Tool Functions

1. search(pattern, max_hits=10)

Searches the document for a regex pattern.

Parameters:

  • pattern (str): Regular expression pattern to search for
  • max_hits (int): Maximum number of matches to return (default: 10)

Returns:

  • Dictionary with list of hits containing start/end offsets

Example:

# Model calls: search(pattern="introduction|overview", max_hits=5)
# Returns: {"hits": [{"start": 0, "end": 12}, {"start": 450, "end": 458}]}

2. peek(start, end)

Retrieves a substring from the document.

Parameters:

  • start (int): Starting byte offset
  • end (int): Ending byte offset

Returns:

  • Dictionary with the text snippet and actual start/end positions

Example:

# Model calls: peek(start=0, end=500)
# Returns: {"text": "Document content...", "start": 0, "end": 500}

Benefits

  1. Token Efficiency: Only relevant portions of the document are sent to the model
  2. Large Documents: Handle documents that exceed context window limits
  3. Dynamic Retrieval: Model decides what information it needs
  4. Cost Effective: Reduced token usage means lower API costs
  5. Flexible: Works with any text document format

Use Cases

  • Document Q&A: Answer questions about large technical documents, manuals, or reports
  • Legal Document Analysis: Search and extract information from contracts or legal texts
  • Research Papers: Summarize or query scientific papers
  • Code Documentation: Navigate and explain large codebases
  • Knowledge Base: Build a conversational interface for your documentation

SDK Features

The RLM SDK provides a simple, intuitive interface for building recursive language model applications:

Core Classes

  • RLMClient: Main client for querying documents
  • RLMDocument: Document wrapper with search and peek capabilities
  • quick_query(): Convenience function for one-off queries

Key Methods

# RLMClient methods
client = RLMClient(api_key=None, model="gpt-4o-mini", max_iterations=10)
client.load_document_from_file(filepath)
client.load_document_from_text(text, name)
client.load_document(document)
client.query(question, system_message=None, verbose=False)
client.get_document_info()

# RLMDocument methods
doc = RLMDocument(content, name)
doc = RLMDocument.from_file(filepath, encoding="utf-8")
doc.search(pattern, max_hits=10)
doc.peek(start, end)

Example Usage

See the examples/ directory for complete examples:

  • basic_usage.py - Simple document Q&A
  • multiple_queries.py - Ask multiple questions
  • custom_processing.py - Advanced customization
  • file_loading.py - Different file loading methods
  • quick_usage.py - One-liner usage

Project Structure

RLM/
├── README.md              # This file
├── setup.py              # Package setup
├── rlm_sdk.py            # Main SDK implementation
├── examples/             # Example scripts
│   ├── README.md
│   ├── basic_usage.py
│   ├── multiple_queries.py
│   ├── custom_processing.py
│   ├── file_loading.py
│   └── quick_usage.py
└── tests/                # Unit tests (future)

Limitations

  • Requires OpenAI API access and API key
  • Document must fit in memory (loaded as string)
  • Search uses regex, which has limitations for semantic search
  • Tool calling adds latency to responses

Future Enhancements

  • Add semantic search capabilities with embeddings
  • Support multiple documents
  • Implement caching for frequent queries
  • Add document preprocessing and chunking strategies
  • Support streaming responses
  • Add more sophisticated recursive search strategies (BM25, vector search)

Documentation

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT License - feel free to use this code for your projects.

References


Note: This implementation uses the OpenAI Responses API with gpt-4.1-mini model. Make sure to check the current API documentation for the latest model names and API endpoints.

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

pyrlmlite-0.1.1.tar.gz (6.9 kB view details)

Uploaded Source

Built Distribution

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

pyrlmlite-0.1.1-py3-none-any.whl (6.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: pyrlmlite-0.1.1.tar.gz
  • Upload date:
  • Size: 6.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pyrlmlite-0.1.1.tar.gz
Algorithm Hash digest
SHA256 8fbb9e276de35dffc0834139f752c02721876e0a3f392ca5b1eddccc67ad08a7
MD5 d521386480cddc1cc4e75e57ba13321b
BLAKE2b-256 491ea364256b4c6a3cd900da817d077e9bce6555b8eeda9c6da69372d88d42b5

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyrlmlite-0.1.1.tar.gz:

Publisher: python-publish.yml on dsmeshram/Recursive-Language-Models

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

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

File metadata

  • Download URL: pyrlmlite-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 6.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pyrlmlite-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 685531c2c5afbb9590a85843dffd19588d0d778f9d09e0f6a96cfa9623086548
MD5 28e73fabfb4c97ad46d3f4dc992c083c
BLAKE2b-256 e09fe1ca5e99d5b1e408e78bdaa4f4e5b4e48190f08bfe8db78ffab26913b3ca

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyrlmlite-0.1.1-py3-none-any.whl:

Publisher: python-publish.yml on dsmeshram/Recursive-Language-Models

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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