How to Write Python Objects for AskRobots
This guide explains how to write Python objects that run on the AskRobots object-primitive system. Objects are small, self-contained Python modules that execute on-demand without deployment.
Object Anatomy
Every object is a Python file with:
- Module-level docstring (description)
- Variable declarations for injected dependencies
- One or more HTTP method handlers (GET, POST, PUT, DELETE)
"""
My Object - Brief description of what it does.
"""
# Injected at runtime (declare as None)
_state_manager = None
_runtime = None
_logger = None
def GET(request):
"""Handle GET requests"""
return {"message": "Hello from my object"}
def POST(request):
"""Handle POST requests with data"""
data = request.get('data', {})
return {"received": data}
Injected Variables
These are automatically injected before execution:
| Variable | Type | Purpose |
|---|---|---|
_state_manager |
StateManager | Persistent key-value storage |
_runtime |
Runtime | Load and call other objects |
_logger |
Logger | Logging (info, warning, error) |
_user_id |
int | Current user's ID |
_object_id |
str | This object's ID |
Always declare them as None at module level:
_state_manager = None
_runtime = None
State Management
Use _state_manager for persistent storage:
def POST(request):
# Get value (with default)
count = int(_state_manager.get('counter', '0'))
# Set value (always strings)
count += 1
_state_manager.set('counter', str(count))
# Get all state as dict
all_state = _state_manager.all()
# Reload from disk (for cross-object consistency)
_state_manager.reload()
return {"count": count}
State is stored in TSV files at data/state/{object_id}/state.tsv.
Loading Other Objects
Use _runtime.load_object() for object composition:
def GET(request):
# Load another object
deals_obj = _runtime.load_object('u_1_deals')
# Reload its state to get fresh data
deals_obj.state_manager.reload()
# Read its state
deals_json = deals_obj.state_manager.get('deals', '[]')
deals = json.loads(deals_json)
return {"deal_count": len(deals)}
Object IDs follow the pattern:
- u_{user_id}_{name} - User objects (e.g., u_1_deals)
- basics_{name} - System/example objects
Allowed Imports
The sandbox allows these imports:
import json
import re
import math
import datetime
import hashlib
import base64
import urllib.parse
import html
import random
import string
import collections
import itertools
import functools
import decimal
import uuid
import time
Blocked imports (security): os, sys, subprocess, socket, requests, traceback, importlib, builtins, eval, exec, compile.
View Objects
View objects return HTML for browser display. The route /v/{view_name}/ renders them:
"""
My View - Renders HTML for the browser.
"""
def POST(request):
html = '''
<div class="retro-panel">
<h2>Welcome</h2>
<p>This is rendered from a Python object!</p>
</div>
'''
return {
'html': html,
'title': 'My Page Title'
}
Views use POST because they often need request context. Access via:
- Browser: https://askrobots.com/v/my_view/
- The system injects this into your base template
Data Objects
Data objects store and manage data, called by views or other objects:
"""
Deals Data - Stores deal records.
"""
import json
_state_manager = None
def GET(request):
"""Return all deals"""
deals = json.loads(_state_manager.get('deals', '[]'))
return {"deals": deals, "count": len(deals)}
def POST(request):
"""Add a new deal"""
data = request.get('data', {})
deals = json.loads(_state_manager.get('deals', '[]'))
new_deal = {
"id": str(len(deals) + 1),
"name": data.get('name', 'Untitled'),
"value": data.get('value', 0),
"stage": data.get('stage', 'lead')
}
deals.append(new_deal)
_state_manager.set('deals', json.dumps(deals))
return {"created": new_deal}
Error Handling
Always wrap external calls in try/except:
def GET(request):
try:
other = _runtime.load_object('u_1_data')
data = other.state_manager.get('items', '[]')
return {"items": json.loads(data)}
except Exception as e:
return {"error": str(e), "items": []}
Request Object
The request parameter contains:
{
"method": "POST", # HTTP method
"data": {...}, # POST/PUT body (parsed JSON)
"query_params": {...}, # URL query parameters
"user_id": 1, # Authenticated user ID
"headers": {...} # HTTP headers (limited)
}
Best Practices
- Keep objects small - Single responsibility, ~50-100 lines max
- Use composition - Load other objects rather than duplicating code
- Always reload state - Call
state_manager.reload()when reading cross-object data - Return JSON - Except for views which return
{'html': ..., 'title': ...} - Handle errors gracefully - Return error info rather than crashing
- Document with docstrings - First line shows in object list
Creating Objects
Via MCP (AI agents):
mcp__askrobots__create_object(
name="my_data",
code="...",
description="Stores my data"
)
Via Web UI:
- Navigate to Objects → Create Object
- Enter name (lowercase, underscores OK)
- Write code
- Save
Testing Objects
Via MCP:
mcp__askrobots__execute_object(object_id="u_1_my_data", method="GET")
Via curl:
curl -X POST "https://askrobots.com/objects/u_1_my_data/execute/" \
-H "Authorization: Token YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"method": "GET"}'
Example: Counter Object
Complete working example:
"""
Counter - Simple incrementing counter with reset.
"""
_state_manager = None
def GET(request):
"""Get current count"""
count = int(_state_manager.get('count', '0'))
return {"count": count}
def POST(request):
"""Increment counter"""
count = int(_state_manager.get('count', '0'))
count += 1
_state_manager.set('count', str(count))
return {"count": count, "action": "incremented"}
def DELETE(request):
"""Reset counter to zero"""
_state_manager.set('count', '0')
return {"count": 0, "action": "reset"}
This is the 100x architecture: write Python, see it live instantly, no deploy needed.