Skip to main content

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?

Links

License

Apache-2.0

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

reminix_runtime-0.0.6.tar.gz (25.2 kB view details)

Uploaded Source

Built Distribution

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

reminix_runtime-0.0.6-py3-none-any.whl (18.7 kB view details)

Uploaded Python 3

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

Hashes for reminix_runtime-0.0.6.tar.gz
Algorithm Hash digest
SHA256 00b94579815f81b7a8aaebd8998080fd5b4d533e670c10e314543d634eb1d2ad
MD5 1da6143499df85ffd54f9bc91b1bc416
BLAKE2b-256 1b4a605bf92a8c53230710c9a9bf561af9d2ad51222c09b03f704a47f1ae7645

See more details on using hashes here.

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

Hashes for reminix_runtime-0.0.6-py3-none-any.whl
Algorithm Hash digest
SHA256 c25678426a3bf0050ef823f9c72f2284dd18af805a7b514620e02ed2b9d5a825
MD5 063a27a7e6462730ff0a9cbfc206308f
BLAKE2b-256 b0569828a6926f49bda7217e037645a4430ecc8afe0118759867d3100076439c

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