A Python package for batch API calls to commercial LLM APIs
Project description
Relay
Relay is a Python package for batch API calls to commercial LLM APIs. It wraps different commercial LLM batch APIs into a single interface.
Note: This is a work in progress. The API is subject to change.
Installation
From PyPI (when published)
pip install relay-llm
From Source
git clone https://github.com/neelguha/relay.git
cd relay
pip install -e .
Development Installation
pip install -e ".[dev]"
Quick Start
Basic Usage
To submit a batch job:
from relay import RelayClient, BatchRequest
# Initialize the client with a workspace directory
# All jobs and results will be stored in this directory
client = RelayClient(directory="my_jobs")
# Create batch requests
requests = [
BatchRequest(
id="req-1",
model="gpt-4o-mini",
system_prompt="You are a helpful assistant.",
prompt="Hello! What is 2+2?",
provider_args={}
),
BatchRequest(
id="req-2",
model="gpt-4o-mini",
system_prompt="You are a helpful assistant.",
prompt="What is the capital of France?",
provider_args={}
),
BatchRequest(
id="req-3",
model="gpt-4o-mini",
system_prompt="You are a helpful assistant.",
prompt="Explain quantum computing in one sentence.",
provider_args={}
),
]
# Submit the batch job with a unique job ID
job = client.submit_batch(
requests=requests,
job_id="my-batch-001", # User-provided unique identifier
provider="openai",
description="Example batch job"
)
print(f"Job ID: {job.job_id}")
print(f"Job submitted: {job.submitted_at}")
print(f"Status: {job.status}")
print(f"Number of requests: {job.n_requests}")
Note: Each job must have a unique job_id. If you try to submit a job with an ID that already exists and is still in progress, a ValueError will be raised.
Listing Jobs
All jobs are stored in the workspace directory. You can list all jobs with:
jobs = client.list_jobs()
print(f"Found {len(jobs)} job(s):")
for job_id in jobs:
print(f" - {job_id}")
Getting Job Information
You can retrieve job metadata without monitoring:
job_info = client.get_job("my-batch-001")
if job_info:
print(f"Status: {job_info['status']}")
print(f"Description: {job_info['description']}")
Monitoring Job Progress
You can check on the progress of a job with:
job_status = client.monitor_batch("my-batch-001")
print(f"Status: {job_status.status}")
print(f"Completed: {job_status.completed_requests}/{job_status.n_requests}")
print(f"Failed: {job_status.failed_requests}/{job_status.n_requests}")
Retrieving Results
You can retrieve the results of a completed job. Results are automatically saved to the workspace directory:
results = client.retrieve_batch_results("my-batch-001")
print(f"Retrieved {len(results)} results")
# Process each result
for result in results:
custom_id = result.get('custom_id')
# Access the response data based on provider format
print(f"Request {custom_id}: {result}")
The retrieve_batch_results method:
- Fetches results from the provider API
- Saves them to
{job_id}_results.jsonin the workspace - Returns a list of dictionaries, one per request in the batch
If results already exist on disk, they are returned from cache. To force a fresh fetch:
results = client.retrieve_batch_results("my-batch-001", force_refresh=True)
Getting Cached Results
You can get results from disk without fetching from the API:
results = client.get_results("my-batch-001")
if results:
print(f"Found {len(results)} cached results")
else:
print("No cached results found")
Checking for Results
Check if results exist for a job:
if client.has_results("my-batch-001"):
print("Results are available")
Cancelling a Job
You can cancel a job that is currently in progress:
cancelled = client.cancel_batch("my-batch-001")
if cancelled:
print("Job successfully cancelled")
Web Dashboard
Relay includes a web-based dashboard for monitoring and managing batch jobs. The dashboard provides:
- Job Overview: View all jobs with status, provider, and progress information
- Filtering & Search: Filter by status, provider, date range, description, or job ID
- Statistics: See summary statistics (total jobs, completed, in-progress, failed)
- Job Details: Click on any job to view detailed information
- Auto-refresh: Automatically refresh job status every 30 seconds
Dashboard Installation
Install the dashboard dependencies:
pip install relay-llm[dashboard]
Launching the Dashboard
Launch the dashboard from the command line:
relay-dashboard my_workspace
Or using Python:
from relay.dashboard import run_dashboard
run_dashboard(workspace_dir="my_workspace", host="127.0.0.1", port=5000)
The dashboard will be available at http://127.0.0.1:5000 (or the specified host/port).
Command Line Options
relay-dashboard <workspace_dir> [--host HOST] [--port PORT] [--debug]
workspace_dir: Path to your workspace directory (required)--host: Host to bind to (default: 127.0.0.1)--port: Port to bind to (default: 5000)--debug: Enable debug mode
Dashboard Features
Filtering:
- Filter by status (completed, in-progress, failed, cancelled, pending)
- Filter by provider (OpenAI, Anthropic, Together AI)
- Filter by date range (submitted date)
- Search by description text
- Search by job ID
Job List:
- View all jobs in a sortable table
- See job status with color-coded badges
- View progress (completed/total requests)
- See if results are available
- Click on any job ID to view details
Statistics:
- Total number of jobs
- Number of completed jobs
- Number of in-progress jobs
- Number of failed jobs
Supported Providers
Relay currently supports the following providers:
- OpenAI - Requires
OPENAI_API_KEYenvironment variable - Together AI - Requires
TOGETHER_API_KEYenvironment variable - Anthropic - Requires
ANTHROPIC_API_KEYenvironment variable
Workspace Directory
Relay uses a workspace directory to store all jobs and results. When you create a RelayClient, you specify a directory:
client = RelayClient(directory="my_workspace")
The workspace directory structure:
my_workspace/
job-001.json # Job metadata
job-001_results.json # Results (when retrieved)
job-002.json
job-002_results.json
...
File Formats
Job Metadata Files ({job_id}.json)
Each job is saved as a JSON file containing metadata about the batch job:
{
"job_id": "my-batch-001",
"provider_job_id": "batch_abc123...",
"provider": "openai",
"submitted_at": "2025-12-23T16:27:47.743798",
"status": "completed",
"n_requests": 3,
"completed_requests": 3,
"failed_requests": 0,
"description": "Example batch job"
}
Fields:
job_id: Your custom job identifierprovider_job_id: The provider's internal batch ID (used for API calls)provider: Provider name ("openai","together", or"anthropic")submitted_at: ISO format timestamp when the job was submittedstatus: Current job status (varies by provider)n_requests: Total number of requests in the batchcompleted_requests: Number of successfully completed requestsfailed_requests: Number of failed requestsdescription: Optional description you provided
Results Files ({job_id}_results.json)
Results are saved as a JSON array, with one object per request. The structure varies by provider:
OpenAI Format:
OpenAI uses the Responses API format for batch jobs:
[
{
"id": "batch_req_abc123...",
"custom_id": "req-1",
"response": {
"status_code": 200,
"request_id": "ce77c014cbadfa50999e860db14eff2c",
"body": {
"id": "resp_0999820f510428c300694b33df8664819ca6bf8e5256a18e07",
"object": "response",
"status": "completed",
"model": "gpt-4o-mini-2024-07-18",
"output": [
{
"id": "msg_0999820f510428c300694b33dfddac819caca8d39f030395e8",
"type": "message",
"status": "completed",
"content": [
{
"type": "output_text",
"text": "2 + 2 equals 4."
}
],
"role": "assistant"
}
],
"usage": {
"input_tokens": 24,
"output_tokens": 9,
"total_tokens": 33
}
}
},
"error": null
},
...
]
Together AI Format:
Together AI uses a similar format to OpenAI's Responses API:
[
{
"id": "br_abc123...",
"custom_id": "req-1",
"response": {
"status_code": 200,
"body": {
"choices": [
{
"finish_reason": "stop",
"index": 0,
"message": {
"content": "The answer is 4.",
"role": "assistant"
}
}
],
"model": "openai/gpt-oss-20b",
"usage": {
"prompt_tokens": 20,
"completion_tokens": 10,
"total_tokens": 30
}
}
}
},
...
]
Anthropic Format:
[
{
"custom_id": "req-1",
"result": {
"type": "succeeded",
"message": {
"id": "msg_abc123...",
"content": [
{
"text": "The answer is 4.",
"type": "text"
}
],
"model": "claude-sonnet-4-5-20250929",
"role": "assistant",
"stop_reason": "end_turn",
"usage": {
"input_tokens": 20,
"output_tokens": 10
}
}
}
},
...
]
Key differences:
- OpenAI: Uses Responses API format with
outputarray containing message objects. Access text viaresponse.body.output[0].content[0].text - Together AI: Uses chat completions format with
choicesarray. Access text viaresponse.body.choices[0].message.content - Anthropic: Uses
resultobject withmessage.contentarray. Access text viaresult.message.content[0].text - All formats include the
custom_idfield, which matches theidyou provided in yourBatchRequest
Accessing Results:
results = client.retrieve_batch_results("my-batch-001")
for result in results:
custom_id = result.get('custom_id')
# OpenAI format (Responses API)
if 'response' in result and 'output' in result['response'].get('body', {}):
content = result['response']['body']['output'][0]['content'][0]['text']
# Together AI format (chat completions)
elif 'response' in result and 'choices' in result['response'].get('body', {}):
content = result['response']['body']['choices'][0]['message']['content']
# Anthropic format
elif 'result' in result:
content = result['result']['message']['content'][0]['text']
print(f"{custom_id}: {content}")
Key benefits:
- All jobs and results are stored in one place
- You can create a new
RelayClientwith the same directory to access all existing jobs - Results are cached on disk, so you don't need to re-fetch from the API
- Easy to share or backup a workspace
Environment Variables
Make sure to set the appropriate API key for your provider:
export OPENAI_API_KEY='your-api-key'
export TOGETHER_API_KEY='your-api-key' # For Together AI
export ANTHROPIC_API_KEY='your-api-key' # For Anthropic
Todo
- Add support for XAI batch API
- Add support for Google batch API
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file relay_llm-0.2.0.tar.gz.
File metadata
- Download URL: relay_llm-0.2.0.tar.gz
- Upload date:
- Size: 27.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b8aa482664d67f33734d54ffbfc84f1731caf7cf2b5711a9eb292211bc517dd7
|
|
| MD5 |
ecedf7e312401681bfef29cdef5f88b0
|
|
| BLAKE2b-256 |
4b83c26a7dc7d73a6fdf192671d322a8061d11c8f7345a8f0e31b4f31cf55539
|
File details
Details for the file relay_llm-0.2.0-py3-none-any.whl.
File metadata
- Download URL: relay_llm-0.2.0-py3-none-any.whl
- Upload date:
- Size: 28.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
48b2388d5754776d690e72da61006b84cd90c712e4c134c980180e235be5c6b3
|
|
| MD5 |
53aba4d392386c69d3479647efc26234
|
|
| BLAKE2b-256 |
5e8b9e9ecaf315204494707ec9c3b03b402690c5888fa46d6d0908556bdddcdf
|