Results

How to Write Python Objects for AskRobots

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

  1. Keep objects small - Single responsibility, ~50-100 lines max
  2. Use composition - Load other objects rather than duplicating code
  3. Always reload state - Call state_manager.reload() when reading cross-object data
  4. Return JSON - Except for views which return {'html': ..., 'title': ...}
  5. Handle errors gracefully - Return error info rather than crashing
  6. 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.