Agent Platform - Universal Runtime & Hosting for AI Agents
Deploy and share your AI agents instantly. Transform any Python AI agent (CrewAI, LangGraph, OpenAI) into a production-ready streaming API with just one line of code, then deploy and share with one command.
Features
✨ One-Line Integration: Wrap any agent with serve()
🚀 Instant Deployment: Deploy with agent deploy (< 1 second)
🔗 Auto-Generated URLs: Get shareable links instantly
💬 Chat UI Included: Beautiful Next.js frontend with expert review panel
🔄 Real-time Streaming: SSE-based token streaming
🎯 Multi-Tenant: Host unlimited agents on one server
🛡️ Secure: UUID-based isolation, timeout enforcement
🔌 Framework Agnostic: OpenAI, CrewAI, LangGraph, or plain Python
Quick Start
💡 既存のAIエージェントプロジェクトをお持ちですか?
⚠️ 重要: 既に
main.pyなどのコードがあるプロジェクトでは、agent initを使用するとファイルが上書きされます。既存プロジェクトの場合は、移行ガイド (MIGRATION_GUIDE.md) を参照してください。 CrewAI、LangGraph、OpenAIなどの既存プロジェクトを5分でデプロイできます。
Option A: 新規プロジェクトを作成してデプロイ
新しくAIエージェントを作る場合はこちら
1. Install from PyPI
pip install axen-runtime
2. Initialize Your Agent
# 新しいディレクトリを作成
mkdir my-agent
cd my-agent
# テンプレートを生成
agent init --name my-agent
This creates:
agent.yaml- Configuration filemain.py- Sample agent code (テンプレート).env.template- Environment variables template
⚠️ 注意: agent initは新しいmain.pyを作成します。既存のコードがある場合は実行しないでください。
3. Add Your API Keys
cp .env.template .env
# Edit .env and add your API keys
echo "OPENAI_API_KEY=sk-proj-your-key-here" >> .env
# Important: Add .env to .gitignore
echo ".env" >> .gitignore
4. Implement Your Agent
Edit main.py:
from dotenv import load_dotenv
import os
load_dotenv()
from agent_runner import serve
from openai import OpenAI
from typing import List
from agent_runner.types import Message
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def my_agent(messages: List[Message]):
"""Your AI agent logic."""
stream = client.chat.completions.create(
model="gpt-4",
messages=messages,
stream=True
)
for chunk in stream:
if chunk.choices[0].delta.content:
yield chunk.choices[0].delta.content
# Register the agent
serve(my_agent, framework="openai")
5. Deploy to Production
agent deploy
Output:
✅ Deployment successful!
📋 Deployment ID: 550e8400-e29b-41d4-a716-446655440000
🔗 Access your agent here:
https://axen-runner.vercel.app/chat/550e8400-e29b-41d4-a716-446655440000
Your agent is now running on our production infrastructure at:
- API:
https://agent-runner-production-f78a.up.railway.app - Frontend:
https://axen-runner.vercel.app
6. Share & Use
Share the URL with anyone. They can:
- Chat via Web UI:
https://axen-runner.vercel.app/chat/YOUR_DEPLOYMENT_ID - Integrate via API:
curl -X POST https://agent-runner-production-f78a.up.railway.app/api/chat/YOUR_DEPLOYMENT_ID \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Hello"}]}'
Option B: 既存プロジェクトをデプロイ
既にCrewAI、LangGraph、OpenAIなどのコードがある場合はこちら
⚠️ 重要: 既存プロジェクトではagent initを使わないでください(ファイルが上書きされます)
詳細な手順: MIGRATION_GUIDE.md
クイックガイド:
# 1. 既存プロジェクトに移動
cd ~/my-existing-project
# 2. agent.yaml を手動作成(agent init は使わない!)
cat > agent.yaml << 'EOF'
name: my-agent
description: My existing AI agent
version: 1.0.0
runtime:
entrypoint: main.py
framework: langgraph # または crewai, openai, generic
EOF
# 3. 既存の main.py に serve() を追加(3-5行)
# from agent_runner import serve
# def my_agent(messages):
# # 既存のコードを呼び出す
# result = your_existing_function(messages)
# for word in str(result).split():
# yield word + " "
# serve(my_agent, framework="langgraph")
# 4. .env を確認
echo "OPENAI_API_KEY=sk-proj-xxx" > .env
echo ".env" >> .gitignore
# 5. デプロイ
agent deploy
Option C: Local Development
For developing agents locally before deploying to production:
1. Clone & Install
git clone https://github.com/AXEN-INC/Agent-Runner
cd runtime-app
pip install axen-runtime
2. Start Local Server
# Start backend with Docker
docker-compose up -d
# Check server is running
curl http://localhost:8000/health
3. Initialize Your Agent
mkdir my-agent
cd my-agent
agent init --name my-agent
4. Deploy to Local Server
agent deploy --api-url http://localhost:8000
Output:
✅ Deployment successful!
📋 Deployment ID: 550e8400-e29b-41d4-a716-446655440000
🔗 Access your agent here:
http://localhost:3000/chat/550e8400-e29b-41d4-a716-446655440000
5. Test Locally
# Via API
curl -X POST http://localhost:8000/api/chat/YOUR_DEPLOYMENT_ID \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Hello"}]}'
# Via Frontend (if running)
cd frontend
npm install && npm run dev
# Visit http://localhost:3000
When you're ready, deploy to production:
# Deploy to production (omit --api-url)
agent deploy
Deploy & Share
CLI Commands
agent init
Initialize a new agent project:
agent init [OPTIONS]
Options:
--name, -n TEXT: Agent name (default: "my-agent")--force, -f: Overwrite existing files
Example:
agent init --name awesome-chatbot
agent deploy
Deploy your agent to the platform:
agent deploy [OPTIONS]
Options:
--api-url TEXT: API server URL (default: production Railway server)
Examples:
# Deploy to production (default)
agent deploy
# Deploy to local development server
agent deploy --api-url http://localhost:8000
# Deploy to custom server
agent deploy --api-url https://your-custom-server.com
Environment Variables:
AGENT_PLATFORM_URL: Override default API URLAGENT_PLATFORM_FRONTEND_URL: Override default frontend URL
# Configure custom URLs
export AGENT_PLATFORM_URL="https://your-api.com"
export AGENT_PLATFORM_FRONTEND_URL="https://your-frontend.com"
agent deploy
agent.yaml Configuration
The agent.yaml file configures your agent:
# Basic Information
name: my-agent
description: A simple AI agent
version: 1.0.0
# Runtime Configuration
runtime:
python_version: "3.11"
entrypoint: main.py
framework: auto # auto, openai, crewai, langgraph, generic
timeout: 300 # seconds
# Dependencies (optional)
dependencies:
- openai==1.6.0
- langchain==0.1.0
# Environment Variables (optional)
env:
MODEL_NAME: gpt-4
Deployment Process
When you run agent deploy:
- ✅ Validates
agent.yamlandmain.py - 📦 Packages code into
project.zip(excludes venv, .git, etc.) - 🚀 Uploads to server at
/api/deploy - 🔑 Generates unique deployment_id (UUID)
- 📁 Extracts to
uploads/{deployment_id}/ - 🔗 Returns shareable URL
No Docker build = Instant deployment (< 1 second)
Accessing Deployed Agents
Via Web UI:
http://localhost:3000/chat/{deployment_id}
Via API:
curl -X POST http://localhost:8000/api/chat/{deployment_id} \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Hello"}]}'
Environment Variables
Your agent can use environment variables for API keys and configuration. The platform automatically loads .env files from your agent directory.
Using .env Files
- Create a
.envfile in your agent directory:
# .env
OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxx
MODEL_NAME=gpt-4
TEMPERATURE=0.7
- Use
python-dotenvin your agent code:
# main.py
from dotenv import load_dotenv
import os
# Load environment variables (works both locally and on server)
load_dotenv()
from openai import OpenAI
from agent_runner import serve
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def my_agent(messages):
# Your agent logic using the API key
response = client.chat.completions.create(
model=os.getenv("MODEL_NAME", "gpt-4"),
messages=messages
)
yield response.choices[0].message.content
serve(my_agent)
- Add
.envto your.gitignore:
echo ".env" >> .gitignore
- Deploy your agent:
agent deploy
Your .env file will be included in the deployment and loaded automatically on the server.
Security Best Practices
⚠️ Important:
- Always add
.envto.gitignoreto prevent committing secrets to Git - Use different API keys for development vs. production
- Rotate API keys regularly
- Never hardcode API keys in your code
Supported Environment Variables
The platform loads .env files automatically, so you can use any environment variable:
OPENAI_API_KEY- OpenAI API keyANTHROPIC_API_KEY- Anthropic Claude API keyGOOGLE_API_KEY- Google Gemini API key- Custom variables - Any variable you define
Examples
Plain Python Generator
from agent_runner import serve
from typing import List
from agent_runner.types import Message
import time
def my_simple_agent(messages: List[Message]):
latest_message = messages[-1]["content"]
for word in latest_message.split():
yield word + " "
time.sleep(0.1)
serve(my_simple_agent)
OpenAI Streaming (with Full Conversation History)
from agent_runner import serve
from agent_runner.types import Message
from openai import OpenAI
from typing import List
import os
def my_openai_agent(messages: List[Message]):
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
# Pass full conversation history directly to OpenAI
stream = client.chat.completions.create(
model="gpt-4",
messages=messages, # Full history!
stream=True
)
for chunk in stream:
if chunk.choices[0].delta.content:
yield chunk.choices[0].delta.content
serve(my_openai_agent, framework="openai")
CrewAI Multi-Agent
from agent_runner import serve
from crewai import Agent, Task, Crew
def my_crew_agent(input_text: str):
# Define agents
researcher = Agent(role='Researcher', goal='Research topic')
writer = Agent(role='Writer', goal='Write summary')
# Create crew
crew = Crew(agents=[researcher, writer], tasks=[...])
result = crew.kickoff()
for word in str(result).split():
yield word + " "
serve(my_crew_agent, framework="crewai")
LangGraph Workflow
from agent_runner import serve
from langgraph.graph import StateGraph, END
def my_graph_agent(input_text: str):
workflow = StateGraph(...)
# Build graph...
app = workflow.compile()
for state in app.stream({"input": input_text}):
yield state.get("output", "")
serve(my_graph_agent, framework="langgraph")
Project Structure
runtime-app/
├── cli.py # Deployment CLI
├── templates/ # CLI templates
│ ├── agent.yaml # Agent config template
│ └── main.py # Sample agent code
├── uploads/ # Deployed agents
│ └── {deployment_id}/ # One directory per deployment
│
├── agent_runner/ # Universal SDK
│ ├── __init__.py # Public API
│ ├── sdk.py # Core serve() function
│ ├── types.py # Type definitions
│ ├── logger.py # Logging
│ ├── exceptions.py # Custom exceptions
│ ├── adapters/ # Framework adapters
│ │ ├── base.py
│ │ ├── generic_adapter.py
│ │ ├── openai_adapter.py
│ │ ├── crewai_adapter.py
│ │ └── langgraph_adapter.py
│ └── streaming/ # Async/sync bridge
│ └── normalizer.py
│
├── runtime/ # FastAPI server
│ ├── server.py # HTTP endpoints
│ ├── loader.py # Dynamic agent loader
│ ├── config.py # Configuration
│ ├── middleware.py # Timeout, rate limiting
│ └── routers/
│ └── deploy.py # Deployment API
│
├── frontend/ # Next.js Chat UI
│ ├── app/
│ │ ├── page.tsx # Main chat page
│ │ └── layout.tsx # Root layout
│ ├── components/
│ │ ├── chat.tsx # Chat interface (useChat)
│ │ ├── message.tsx # Message bubbles
│ │ ├── review-panel.tsx # Expert review panel
│ │ └── review-form.tsx # Review form
│ └── lib/
│ ├── types.ts # TypeScript types
│ └── utils.ts # Utilities
│
├── sandbox/ # Docker environment (legacy)
│ ├── Dockerfile.base # Base image
│ ├── Dockerfile.runtime # Runtime image
│ └── build.sh # Build script
│
├── examples/ # Example implementations
│ ├── plain_generator_example/
│ ├── openai_example/
│ ├── crewai_example/
│ └── langgraph_example/
│
├── docker-compose.yml # Local development
├── pyproject.toml # SDK package definition
├── README.md # This file
└── DEPLOY.md # Detailed deployment guide
API Endpoints
POST /api/deploy
Deploy a new agent.
Request:
file: Zip file containing agent code (multipart/form-data)name: Agent name (optional)
Response:
{
"deployment_id": "550e8400-e29b-41d4-a716-446655440000",
"name": "my-agent",
"status": "success",
"message": "Agent 'my-agent' deployed successfully"
}
POST /api/chat/{deployment_id}
Chat with a deployed agent (multi-tenant endpoint).
Request:
{
"messages": [
{"role": "user", "content": "Hello!"},
{"role": "assistant", "content": "Hi there!"},
{"role": "user", "content": "How are you?"}
]
}
Response (Server-Sent Events):
data: Hello
data: there
data: !
data: [DONE]
POST /api/chat
Chat with the default agent (single-tenant endpoint).
Same format as above, but uses the agent loaded at startup.
Frontend Integration (Vercel AI SDK):
// app/page.tsx
import { useChat } from 'ai/react';
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit } = useChat({
api: 'http://localhost:8000/api/chat/YOUR_DEPLOYMENT_ID',
});
return (
<div>
{messages.map(m => (
<div key={m.id}>{m.role}: {m.content}</div>
))}
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} />
</form>
</div>
);
}
GET /api/deployments/{deployment_id}
Get information about a deployment.
Response:
{
"deployment_id": "550e8400-e29b-41d4-a716-446655440000",
"name": "my-agent",
"version": "1.0.0",
"description": "A simple AI agent",
"framework": "auto",
"created_at": "1234567890.123"
}
GET /health
Health check endpoint.
Response:
{
"status": "healthy",
"agent_loaded": true,
"uptime_seconds": 123.45
}
GET /docs
Interactive API documentation (Swagger UI).
Visit http://localhost:8000/docs for the full API reference.
Frontend Chat UI
The platform includes a modern Next.js chat interface with expert review capabilities.
Running the Frontend
# Start backend first
docker-compose up -d
# Start frontend
cd frontend
npm install
npm run dev
Visit: http://localhost:3000
Features
- Real-time Streaming: See agent responses token-by-token
- Expert Review Panel: Annotate and review agent responses
- Star rating (1-5)
- Correction input
- Comment textarea
- Responsive Design: Works on desktop and mobile
- Vercel AI SDK Integration: Uses
useChathook
For detailed frontend documentation, see frontend/README.md.
Configuration
Environment Variables
User Agent Configuration (.env file)
These are included in your agent deployment:
# .env (in your agent directory)
OPENAI_API_KEY=sk-proj-your-key-here
ANTHROPIC_API_KEY=sk-ant-your-key-here
MODEL_NAME=gpt-4
TEMPERATURE=0.7
CLI Configuration
# Override deployment targets
export AGENT_PLATFORM_URL="https://agent-runner-production-f78a.up.railway.app" # Default
export AGENT_PLATFORM_FRONTEND_URL="https://axen-runner.vercel.app" # Default
# For local development
export AGENT_PLATFORM_URL="http://localhost:8000"
Server Configuration (for self-hosting)
# Server Configuration
export HOST="0.0.0.0"
export PORT="8000"
export LOG_LEVEL="INFO"
# CORS (for frontend integration)
export CORS_ORIGINS="http://localhost:3000,https://your-frontend.vercel.app"
# Resource Limits
export MAX_EXECUTION_TIME="300" # 5 minutes
export MAX_MEMORY="512m"
export MAX_CPU="1.0"
# Rate Limiting
export RATE_LIMIT_ENABLED="true"
export RATE_LIMIT_MAX_REQUESTS="100"
export RATE_LIMIT_WINDOW="60" # seconds
Docker Compose
For local development:
# Create .env file
echo "OPENAI_API_KEY=your-key" > .env
# Run with docker-compose
docker-compose up
SDK API Reference
serve(handler, *, framework=None, config=None, timeout=300, chunk_size=1, debug=False)
Register an agent handler for serving.
Parameters:
handler(Callable): Your agent function (sync/async generator)framework(str, optional): Framework hint ("auto", "crewai", "langgraph", "openai")config(dict, optional): Additional configurationtimeout(int): Maximum execution time in seconds (default: 300)chunk_size(int): Tokens per chunk for batching (default: 1)debug(bool): Enable debug logging (default: False)
Example:
serve(my_agent, framework="openai", timeout=600, debug=True)
test_agent(messages, handler=None)
Test an agent locally without running the server.
Parameters:
messages(List[Message]): Messages to test withhandler(Callable, optional): Handler to test (uses registered if not provided)
Returns:
- Generator yielding tokens
Example:
messages = [{"role": "user", "content": "Hello"}]
for token in test_agent(messages):
print(token, end="")
Architecture
Hot-Loading System
The platform uses hot-loading for instant deployment:
- Agent code uploaded as zip file
- Extracted to
uploads/{deployment_id}/ - On first request, Python dynamically imports the agent
- AgentRuntime instance cached for subsequent requests
- No Docker build = deployment in < 1 second
Multi-Tenant Design
- Single server handles unlimited agents
- Each agent gets unique UUID deployment_id
- Agents run in isolated namespaces
- Shared infrastructure (FastAPI, middleware, adapters)
- Independent execution contexts per request
Security
- UUID Validation: Prevents directory traversal attacks
- Path Resolution: Ensures files stay within uploads directory
- Timeout Enforcement: Maximum 300s per request
- Rate Limiting: 100 requests/minute per IP
- File Exclusions: Auto-excludes .env, credentials from deployments
Performance
Deployment Speed
- Hot-loading: < 1 second (instant)
- Docker build (legacy): 15-30 seconds
- Base image build (one-time): 2-3 minutes
Resource Limits
- CPU: 1 core per agent
- Memory: 512MB per agent
- Execution timeout: 5 minutes (configurable)
- Rate limit: 100 requests/minute (configurable)
Streaming Latency
- API latency: p95 < 200ms
- Token streaming: p95 < 100ms
- Agent caching: First load ~100ms, cached < 10ms
Troubleshooting
CLI Issues
"agent.yaml not found"
# Run init first
agent init --name my-agent
"Cannot connect to API server"
# For production deployment, check service status
curl https://agent-runner-production-f78a.up.railway.app/health
# For local development, make sure backend is running
docker-compose up -d
curl http://localhost:8000/health
"Invalid zip file"
# Check for syntax errors
python -m py_compile main.py
# Try re-deploying
agent deploy
# For local testing
agent deploy --api-url http://localhost:8000
Runtime Issues
"Agent handler not registered"
Solution: Make sure main.py calls serve(your_handler):
serve(my_agent) # Don't forget this!
"Agent not found or failed to load"
Check deployment exists:
ls -la uploads/{deployment_id}/
# Should show: agent.yaml, main.py
Import errors
ModuleNotFoundError: No module named 'crewai'
Solution: Add the module to agent.yaml:
dependencies:
- crewai>=0.1.0
Timeout errors
Agent execution timeout after 300s
Solution: Increase timeout in agent.yaml:
runtime:
timeout: 600
Rate limit exceeded
Rate limit exceeded: 100 requests per 60s
Solution: Adjust RATE_LIMIT_MAX_REQUESTS environment variable.
Development
Local Testing (Without Docker)
# test_local.py
from agent_runner import serve, test_agent
from agent_runner.types import Message
from typing import List
def my_agent(messages: List[Message]):
yield f"Echo: {messages[-1]['content']}"
serve(my_agent)
# Test it
messages = [{"role": "user", "content": "Hello World"}]
for token in test_agent(messages):
print(token, end="", flush=True)
Run:
python test_local.py
Running Full Stack Locally
# Terminal 1: Start backend
docker-compose up
# Terminal 2: Start frontend
cd frontend
npm install
npm run dev
# Terminal 3: Deploy an agent to local server
agent init --name test-agent
# Edit main.py and .env
agent deploy --api-url http://localhost:8000
Visit:
- Backend:
http://localhost:8000 - Frontend:
http://localhost:3000 - API Docs:
http://localhost:8000/docs - Health Check:
http://localhost:8000/health
Documentation
- DEPLOY.md: Detailed deployment guide
- frontend/README.md: Frontend documentation
- CLAUDE.md: Architecture and development guidelines
- examples/: Working examples for each framework
Troubleshooting
デプロイ時にエラーが発生した場合は、トラブルシューティングガイド (TROUBLESHOOTING.md) を参照してください。
よくあるエラーと解決方法:
Directory 'xxx' does not exist- StaticFilesの相対パス問題No module named 'xxx'- requirements.txtに依存関係を追加Permission denied- ファイル書き込み先をデプロイメントディレクトリ内に変更Agent handler not registered- main.pyでserve()を呼ぶDeployment failed: 502- Railwayのデプロイ完了を待つ
詳細な解決方法は TROUBLESHOOTING.md を参照してください。
Contributing
We welcome contributions! See CLAUDE.md for development guidelines.
License
MIT License - See LICENSE file
Support
- Issues: GitHub Issues
- Troubleshooting: TROUBLESHOOTING.md
- Migration Guide: MIGRATION_GUIDE.md
- Documentation: See
DEPLOY.mdandexamples/ - Architecture: See
CLAUDE.md
Built with the Agent Platform SDK - Deploy and share AI agents in one command.
Metadata
Release files for axen-runtime 0.2.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| axen_runtime-0.2.6.tar.gz | 31.8 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| axen_runtime-0.2.6-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 77.0 kB
Release files / axen_runtime-0.2.6.tar.gz
| Download URL | axen_runtime-0.2.6.tar.gz |
|---|---|
| Size | 31.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
793651ec59ffcb5343027c9acaa0ea12ae87ffff95a9e6ab480d9fa07f51ade1
|
|
BLAKE2b-256 checksum How to use checksums |
cebd78ee08049cd3c6335fac8646c48dddee0aa2fbf5660df805c22dbe4cb201
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.14
|
Release files / axen_runtime-0.2.6-py3-none-any.whl
| Download URL | axen_runtime-0.2.6-py3-none-any.whl |
|---|---|
| Size | 45.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
61715aefa55d4b4cc05510b4c8504cca05c857ebb22f9dddd70bc113076db617
|
|
BLAKE2b-256 checksum How to use checksums |
923ea07441fdaa405915b1e6de47be2b1e52484c4af38d54fbaf369f4d5cad90
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.14
|
Security for axen-runtime 0.2.6
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.