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.2.tar.gz (13.5 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.2-py3-none-any.whl (10.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: pyrlmlite-0.1.2.tar.gz
  • Upload date:
  • Size: 13.5 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.2.tar.gz
Algorithm Hash digest
SHA256 e79a48178e6dc26b1dbc5b6870f3f0444236b7fb9ee13231fa948d7f36ddb136
MD5 0609557f37d3d40f35cc9bffd003a3e8
BLAKE2b-256 f6f815ea3ee19734ce536a502c5af27500997a803aff6a51d7615c156b676cbc

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyrlmlite-0.1.2.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.2-py3-none-any.whl.

File metadata

  • Download URL: pyrlmlite-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 10.3 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.2-py3-none-any.whl
Algorithm Hash digest
SHA256 17f0b0db2be9b0a39d0d0b8d0aed576a9bc9806209607781044ab257952868cb
MD5 4f6f859942193d2bab9eb72dad87622e
BLAKE2b-256 3ba252371229dd0462e83153377acd1efb74c7a8ad74bd01c0a2fe21d571ed84

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyrlmlite-0.1.2-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