Ascribe-Link
HTTP-based specimen server for Ascribe-XR with dynamic data generation
Ascribe-Link provides a REST API for serving scientific datasets (meshes, volumes, point clouds) to VR clients. Features include parametric specimen generation, JSON Schema-driven UI generation, and multiplayer result caching.
Features
- Specimen Catalog: Curated collection of 3D datasets with metadata
- Dynamic Specimens: Generate data on-demand from Python functions
- JSON Schema Generation: Auto-generate parameter schemas from function signatures
- Processing API: Invoke functions with parameters, return typed results
- Multiplayer Caching: Room-based result caching for collaborative sessions
- Type System: Support for mesh, volume, point cloud, and image data
- Federation: Relay mode for aggregating specimens from worker nodes
Quick Start
Installation
# Clone the repository
git clone https://github.com/ronpandolfi/Ascribe-Link.git
cd Ascribe-Link
# Install dependencies
pip install -e .
Run the Server
python -m ascribe_link
Server starts at http://localhost:8000
Test the API
# Run comprehensive test suite
./test_dynamic_specimen.py
# Or manually test endpoints
curl http://localhost:8000/api/specimens/
curl http://localhost:8000/api/processing/functions
API Documentation
Specimen Endpoints
List all specimens:
GET /api/specimens/
Returns array of specimen metadata with is_dynamic flag.
Get specimen details:
GET /api/specimens/{specimen_id}
Returns full metadata including JSON Schema for dynamic specimens.
Get specimen data:
GET /api/specimens/{specimen_id}/data
Downloads the specimen data file (mesh, volume, etc.).
Get thumbnail:
GET /api/specimens/{specimen_id}/thumbnail
Returns specimen preview image.
Processing Endpoints
List processing functions:
GET /api/processing/functions
Returns all registered functions with schemas and return types.
Get function schema:
GET /api/processing/functions/{name}/schema
Returns JSON Schema for function parameters.
Invoke function:
POST /api/processing/invoke
Content-Type: application/json
{
"function_name": "generate_sphere",
"args": [],
"kwargs": {"radius": 2.0, "resolution": 64},
"room_id": "ascribe"
}
Returns typed result (mesh, volume, etc.) with automatic caching.
Cache stats:
GET /api/processing/cache/stats
Returns cache usage statistics per room.
Clear cache:
POST /api/processing/cache/clear
Invalidates all cached results.
Dynamic Specimens
Creating a Dynamic Specimen
1. Write a processing function:
# ascribe_link/parametric.py
import pyvista as pv
from ascribe_link.models import MeshResult
def generate_torus(
major_radius: float = 1.0,
minor_radius: float = 0.3,
segments: int = 32,
) -> MeshResult:
"""Generate a parametric torus mesh."""
torus = pv.ParametricTorus(
ringradius=major_radius,
crosssectionradius=minor_radius,
)
return MeshResult.from_pyvista(torus)
2. Register the function:
# ascribe_link/app.py
from ascribe_link.parametric import generate_torus
registry.register_function(generate_torus, "generate_torus", return_type="mesh")
3. Create specimen metadata:
// specimens/parametric_torus/specimen.json
{
"id": "parametric_torus",
"display_name": "Parametric Torus",
"description": "Torus with adjustable radii and resolution",
"type": "mesh",
"thumbnail_file": "thumbnail.png",
"function_name": "generate_torus",
"tags": ["parametric", "mesh", "dynamic"],
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "generate_torus",
"type": "object",
"properties": {
"major_radius": {
"type": "number",
"default": 1.0,
"minimum": 0.5,
"maximum": 3.0
},
"minor_radius": {
"type": "number",
"default": 0.3,
"minimum": 0.1,
"maximum": 1.0
},
"segments": {
"type": "number",
"default": 32,
"minimum": 8,
"maximum": 128
}
}
}
}
The schema is automatically generated from function signatures if omitted, but can be customized for better UX (e.g., min/max ranges for sliders).
Supported Types
Type Hints → JSON Schema:
float,int→"number"or"integer"str→"string"bool→"boolean"Literal["a", "b"]→{"enum": ["a", "b"]}- Defaults extracted from function signature
Return Types:
MeshResult→ vertices, indices, normalsVolumeResult→ shape, dtype, base64 data, spacing/originPointCloudResult→ points, colors, scalarsImageResult→ width, height, channels, data
Multiplayer Caching
Ascribe-Link implements room-based result caching to optimize collaborative VR sessions:
How It Works
- Room-Scoped Cache: Each room (e.g., "ascribe") has one cached result
- First Peer: Computes result, stores in cache
- Subsequent Peers: Get instant cached result (no recomputation)
- Auto-Invalidation: New request with different parameters wipes old cache
Example
Room: "ascribe"
Peer A: generate_sphere(radius=2.0, resolution=64)
→ Cache miss → Compute (500ms) → Store
Peer B: generate_sphere(radius=2.0, resolution=64) [same params]
→ Cache hit! → Return cached result (<10ms)
Peer C: generate_sphere(radius=3.0, resolution=64) [different params]
→ Cache miss → Invalidates old cache → Compute new result → Store
Configuration
# ascribe_link/app.py
result_cache = RoomResultCache(ttl_seconds=300.0) # 5 minute TTL
Project Structure
ascribe_link/
├── app.py # Litestar application factory
├── models.py # Data models (specimens, results, requests)
├── processing.py # FunctionRegistry and schema generation
├── cache.py # RoomResultCache for multiplayer
├── specimen_store.py # Specimen directory management
├── parametric.py # Built-in parametric functions
├── example.py # Example functions
├── routes/
│ ├── specimens.py # Specimen catalog endpoints
│ ├── processing.py # Function invocation endpoints
│ └── federation.py # Worker federation (relay mode)
└── utils.py # Helper functions
specimens/ # Specimen data directory
├── brain/
│ ├── specimen.json # Metadata
│ ├── brain.stl # Data file
│ └── thumbnail.png # Preview image
└── parametric_sphere/
├── specimen.json # Metadata with schema + function_name
└── thumbnail.png
Advanced Usage
AI Agent Generation
Enable AI-powered mesh generation (requires claude-agent-sdk):
app = create_app(
enable_agent=True,
agent_model="claude-sonnet-4",
agent_timeout=300.0
)
Clients can invoke:
{
"function_name": "ai_generate",
"kwargs": {
"prompt": "Create a DNA double helix mesh with 10 base pairs"
}
}
Federation (Relay Mode)
Run as a relay to aggregate specimens from worker nodes:
app = create_app(relay_mode=True)
Workers connect via WebSocket and register their specimens. The relay aggregates all specimens into a unified catalog.
Custom Functions
Register your own processing functions:
from ascribe_link.models import MeshResult
def my_function(param1: float, param2: int) -> MeshResult:
# ... generate mesh
return MeshResult(vertices=..., indices=...)
app = create_app(
mesh_functions={"my_function": my_function}
)
Development
Running Tests
# API validation + cache tests
./test_dynamic_specimen.py
# Unit tests (if available)
pytest
Environment Variables
# Override default port
export PORT=8080
python -m ascribe_link
# Specify specimens directory
export SPECIMENS_DIR=/path/to/specimens
python -m ascribe_link
Dependencies
Core:
litestar- Modern ASGI web frameworkpyvista- 3D mesh processingnumpy- Numerical arrays
Optional:
claude-agent-sdk- AI agent generationfirejail- Sandbox for agent code execution
See pyproject.toml for full dependency list.
Architecture
┌─────────────┐
│ VR Client │ (Ascribe-XR)
│ (Godot) │
└──────┬──────┘
│ HTTP/REST
▼
┌─────────────────────────────┐
│ Ascribe-Link (Litestar) │
│ │
│ ┌──────────────────────┐ │
│ │ Specimen Catalog │ │
│ │ (Static Files) │ │
│ └──────────────────────┘ │
│ │
│ ┌──────────────────────┐ │
│ │ Processing Functions │ │
│ │ (Dynamic Generation) │ │
│ └──────────────────────┘ │
│ │
│ ┌──────────────────────┐ │
│ │ Room Result Cache │ │
│ │ (Multiplayer Sync) │ │
│ └──────────────────────┘ │
└─────────────────────────────┘
Performance
Benchmarks (MacBook Pro M1):
- Sphere generation (32 resolution): ~50ms
- Sphere generation (128 resolution): ~500ms
- Cache hit latency: <10ms
- Typical speedup: 10-50x on cache hits
Contributing
- Add new parametric functions to
ascribe_link/parametric.py - Register in
app.py - Create specimen.json with schema
- Test with
./test_dynamic_specimen.py - Submit PR
Related Projects
- Ascribe-XR - VR client (Godot)
- Paper (ACM) - VRST 2024 publication
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 ascribe_link-0.2.0.tar.gz.
File metadata
- Download URL: ascribe_link-0.2.0.tar.gz
- Upload date:
- Size: 1.5 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
093bcab5a009087c22d16896eb7e48be852e22098ba473e56a5b82de5fd6cc10
|
|
| MD5 |
c58d0679696bddf4635639727af1bd29
|
|
| BLAKE2b-256 |
ea8161ccd6bc3f19b5550cc16d85d3f588f9434edc11d858875b5f9b9805a2ed
|
Provenance
The following attestation bundles were made for ascribe_link-0.2.0.tar.gz:
Publisher:
release.yml on lbl-camera/ascribe-link
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ascribe_link-0.2.0.tar.gz -
Subject digest:
093bcab5a009087c22d16896eb7e48be852e22098ba473e56a5b82de5fd6cc10 - Sigstore transparency entry: 2423735173
- Sigstore integration time:
-
Permalink:
lbl-camera/ascribe-link@2789b423268001335b1ecae06f0b05faae14f714 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/lbl-camera
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2789b423268001335b1ecae06f0b05faae14f714 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ascribe_link-0.2.0-py3-none-any.whl.
File metadata
- Download URL: ascribe_link-0.2.0-py3-none-any.whl
- Upload date:
- Size: 1.5 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3423791dc565750e5c352955df6d2ad7f823e9f076561189a077742916686bc0
|
|
| MD5 |
b5d562aa46249be588fc4ff587ddc5f0
|
|
| BLAKE2b-256 |
c192cf9cd1656a92618c82c9cbc217b2a6343c0c31d91ac20be292f85a9fc283
|
Provenance
The following attestation bundles were made for ascribe_link-0.2.0-py3-none-any.whl:
Publisher:
release.yml on lbl-camera/ascribe-link
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ascribe_link-0.2.0-py3-none-any.whl -
Subject digest:
3423791dc565750e5c352955df6d2ad7f823e9f076561189a077742916686bc0 - Sigstore transparency entry: 2423735248
- Sigstore integration time:
-
Permalink:
lbl-camera/ascribe-link@2789b423268001335b1ecae06f0b05faae14f714 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/lbl-camera
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2789b423268001335b1ecae06f0b05faae14f714 -
Trigger Event:
push
-
Statement type: