Reminix Runtime - Serve AI agents as REST APIs with streaming support
Project description
reminix-runtime
Core runtime package for serving AI agents and tools via REST APIs. Provides the serve() function, Agent class, @tool decorator, and BaseAdapter for building framework integrations.
Built on FastAPI with full async support.
Ready to go live? Deploy to Reminix Cloud for zero-config hosting, or self-host on your own infrastructure.
Installation
pip install reminix-runtime
Quick Start
from reminix_runtime import serve, Agent, InvokeRequest, InvokeResponse, ChatRequest, ChatResponse
# Create an agent with decorators
agent = Agent("my-agent")
@agent.on_invoke
async def handle_invoke(request: InvokeRequest) -> InvokeResponse:
task = request.input.get("task", "unknown")
return InvokeResponse(output=f"Completed: {task}")
@agent.on_chat
async def handle_chat(request: ChatRequest) -> ChatResponse:
user_msg = request.messages[-1].content
response = f"You said: {user_msg}"
return ChatResponse(
output=response,
messages=[
*[{"role": m.role, "content": m.content} for m in request.messages],
{"role": "assistant", "content": response}
]
)
# Serve the agent
serve(agents=[agent], port=8080)
How It Works
The runtime creates a REST server with the following endpoints:
| Endpoint | Method | Description |
|---|---|---|
/health |
GET | Health check |
/info |
GET | Runtime discovery (version, agents, tools) |
/agents/{name}/invoke |
POST | Stateless invocation |
/agents/{name}/chat |
POST | Conversational chat |
/tools/{name}/execute |
POST | Execute a tool |
Health Endpoint
curl http://localhost:8080/health
Returns {"status": "ok"} if the server is running.
Discovery Endpoint
curl http://localhost:8080/info
Returns runtime information and available agents:
{
"runtime": {
"name": "reminix-runtime",
"version": "0.0.6",
"language": "python",
"framework": "fastapi"
},
"agents": [
{
"name": "my-agent",
"type": "adapter",
"adapter": "langchain",
"invoke": { "streaming": true },
"chat": { "streaming": true }
}
]
}
Invoke Endpoint
POST /agents/{name}/invoke - For stateless operations.
curl -X POST http://localhost:8080/agents/my-agent/invoke \
-H "Content-Type: application/json" \
-d '{
"input": {
"task": "summarize",
"text": "Lorem ipsum..."
}
}'
Response:
{
"output": "Summary: ..."
}
Chat Endpoint
POST /agents/{name}/chat - For conversational interactions.
curl -X POST http://localhost:8080/agents/my-agent/chat \
-H "Content-Type: application/json" \
-d '{
"messages": [
{"role": "system", "content": "You are helpful"},
{"role": "user", "content": "What is the weather?"}
]
}'
Response:
{
"output": "The weather is 72°F and sunny!",
"messages": [
{"role": "system", "content": "You are helpful"},
{"role": "user", "content": "What is the weather?"},
{"role": "assistant", "content": "The weather is 72°F and sunny!"}
]
}
The output field contains the assistant's response, while messages includes the full conversation history.
Tool Execute Endpoint
POST /tools/{name}/execute - Execute a standalone tool.
curl -X POST http://localhost:8080/tools/get_weather/execute \
-H "Content-Type: application/json" \
-d '{
"input": {
"location": "San Francisco"
}
}'
Response:
{
"output": { "temp": 72, "condition": "sunny" }
}
Tools
Tools are standalone functions that can be served via the runtime. They're useful for exposing utility functions, external API integrations, or any reusable logic.
Creating Tools
Use the @tool decorator to create tools:
from reminix_runtime import tool, serve
@tool
async def get_weather(location: str, units: str = "celsius") -> dict:
"""Get current weather for a location."""
# Call weather API...
return {"temp": 72, "condition": "sunny", "location": location}
# Serve tools (with or without agents)
serve(tools=[get_weather], port=8080)
The decorator automatically extracts:
- name from the function name
- description from the docstring
- parameters from type hints and defaults
- output from the return type hint (e.g.,
-> dict,-> str,-> list)
The output schema is included in the /info endpoint for documentation and enables better type inference for clients.
Custom Tool Configuration
You can customize the tool name and description:
@tool(name="weather_lookup", description="Look up weather for any city")
async def get_weather(location: str) -> dict:
return {"temp": 72, "condition": "sunny"}
Serving Agents and Tools Together
You can serve both agents and tools from the same runtime:
from reminix_runtime import Agent, tool, serve
agent = Agent("my-agent")
@agent.on_invoke
async def handle(request):
return {"output": "Hello!"}
@tool
def calculate(expression: str) -> dict:
"""Perform basic math operations."""
return {"result": eval(expression)} # Note: use a safe evaluator in production
serve(agents=[agent], tools=[calculate], port=8080)
Framework Adapters
Instead of creating custom agents, use our pre-built adapters for popular frameworks:
| Package | Framework |
|---|---|
reminix-langchain |
LangChain |
reminix-langgraph |
LangGraph |
reminix-openai |
OpenAI |
reminix-anthropic |
Anthropic |
reminix-llamaindex |
LlamaIndex |
API Reference
serve(agents, tools, port, host)
Start the runtime server.
| Parameter | Type | Default | Description |
|---|---|---|---|
agents |
list[Agent] |
[] |
List of agents to serve |
tools |
list[Tool] |
[] |
List of tools to serve |
port |
int |
8080 |
Port to listen on. Falls back to PORT environment variable if not provided. |
host |
str |
"0.0.0.0" |
Host to bind to (all interfaces). Can be overridden via HOST env var. |
At least one agent or tool is required.
create_app(agents, tools)
Create a FastAPI app without starting the server. Useful for testing or custom deployment.
from reminix_runtime import create_app
app = create_app(agents=[my_agent], tools=[my_tool])
# Use with uvicorn, gunicorn, etc.
@tool
Decorator to create a tool from a function.
from reminix_runtime import tool
@tool
async def my_tool(param: str, optional_param: int = 10) -> dict:
"""Tool description from docstring."""
return {"result": param, "value": optional_param}
# Or with custom name/description
@tool(name="custom_name", description="Custom description")
def another_tool(x: int) -> int:
return x * 2
The decorator automatically extracts parameters from type hints. Supports both sync and async functions.
Agent
Concrete class for building agents with decorators.
from reminix_runtime import Agent, InvokeRequest, InvokeResponse, ChatRequest, ChatResponse
agent = Agent("my-agent", metadata={"version": "1.0"})
@agent.on_invoke
async def handle_invoke(request: InvokeRequest) -> InvokeResponse:
return InvokeResponse(output="Hello!")
@agent.on_chat
async def handle_chat(request: ChatRequest) -> ChatResponse:
return ChatResponse(output="Hi!", messages=[...])
# Optional: streaming handlers
@agent.on_invoke_stream
async def handle_invoke_stream(request: InvokeRequest):
yield '{"chunk": "Hello"}'
yield '{"chunk": " world!"}'
@agent.on_chat_stream
async def handle_chat_stream(request: ChatRequest):
yield '{"chunk": "Hi"}'
| Method | Description |
|---|---|
on_invoke(fn) |
Register invoke handler |
on_chat(fn) |
Register chat handler |
on_invoke_stream(fn) |
Register streaming invoke handler |
on_chat_stream(fn) |
Register streaming chat handler |
to_asgi() |
Returns an ASGI app for serverless |
agent.to_asgi()
Returns an ASGI application for serverless deployments.
# AWS Lambda with Mangum
from mangum import Mangum
from reminix_runtime import Agent, InvokeResponse
agent = Agent("my-agent")
@agent.on_invoke
async def handle(request):
return InvokeResponse(output="Hello!")
# Lambda handler
handler = Mangum(agent.to_asgi())
Works with:
- AWS Lambda - Use Mangum adapter
- GCP Cloud Functions - Use functions-framework with ASGI
- Any ASGI server - uvicorn, hypercorn, daphne
BaseAdapter
Abstract base class for framework adapters. Use this when wrapping an existing AI framework.
from reminix_runtime import BaseAdapter, InvokeRequest, InvokeResponse, ChatRequest, ChatResponse
class MyFrameworkAdapter(BaseAdapter):
# Adapter name shown in /info endpoint
adapter_name = "my-framework"
# BaseAdapter defaults both to True; override if your adapter doesn't support streaming
# invoke_streaming = False
# chat_streaming = False
def __init__(self, client, name: str = "my-framework"):
self._client = client
self._name = name
@property
def name(self) -> str:
return self._name
async def invoke(self, request: InvokeRequest) -> InvokeResponse:
# Pass input to your framework
result = await self._client.run(request.input)
return InvokeResponse(output=result)
async def chat(self, request: ChatRequest) -> ChatResponse:
# Convert messages and call your framework
result = await self._client.chat(request.messages)
return ChatResponse(
output=result,
messages=[
*[{"role": m.role, "content": m.content} for m in request.messages],
{"role": "assistant", "content": result}
]
)
# Optional: provide a wrap() factory function
def wrap(client, name: str = "my-framework") -> MyFrameworkAdapter:
return MyFrameworkAdapter(client, name)
Request/Response Types
class InvokeRequest:
input: dict[str, Any] # Arbitrary input for task execution
stream: bool = False # Whether to stream the response
context: dict[str, Any] | None # Optional metadata
class InvokeResponse:
output: Any # The result (can be any type)
class ChatRequest:
messages: list[Message] # Conversation history
stream: bool = False # Whether to stream the response
context: dict[str, Any] | None # Optional metadata
class ChatResponse:
output: str # The final answer
messages: list[dict] # Full execution history
Deployment
Ready to go live?
- Deploy to Reminix Cloud - Zero-config cloud hosting
- Self-host - Run on your own infrastructure
Links
License
Apache-2.0
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 reminix_runtime-0.0.6.tar.gz.
File metadata
- Download URL: reminix_runtime-0.0.6.tar.gz
- Upload date:
- Size: 25.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
00b94579815f81b7a8aaebd8998080fd5b4d533e670c10e314543d634eb1d2ad
|
|
| MD5 |
1da6143499df85ffd54f9bc91b1bc416
|
|
| BLAKE2b-256 |
1b4a605bf92a8c53230710c9a9bf561af9d2ad51222c09b03f704a47f1ae7645
|
File details
Details for the file reminix_runtime-0.0.6-py3-none-any.whl.
File metadata
- Download URL: reminix_runtime-0.0.6-py3-none-any.whl
- Upload date:
- Size: 18.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c25678426a3bf0050ef823f9c72f2284dd18af805a7b514620e02ed2b9d5a825
|
|
| MD5 |
063a27a7e6462730ff0a9cbfc206308f
|
|
| BLAKE2b-256 |
b0569828a6926f49bda7217e037645a4430ecc8afe0118759867d3100076439c
|