Metadata-Version: 2.4
Name: subspacecomputing
Version: 0.1.4
Summary: Python SDK for ASTRIA — Subspace Computing Engine
Home-page: https://www.subspacecomputing.com/developer
Author: Subspace Computing
Author-email: Subspace Computing <contact@beausoft.ca>
Project-URL: Documentation, https://www.subspacecomputing.com/developer
Project-URL: Homepage, https://www.subspacecomputing.com/developer
Project-URL: Support, https://www.subspacecomputing.com/developer
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.31.0
Provides-Extra: pandas
Requires-Dist: pandas>=2.0.0; extra == "pandas"
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: black>=23.0.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: fastapi>=0.100.0; extra == "dev"
Requires-Dist: httpx>=0.25.0; extra == "dev"
Dynamic: author
Dynamic: home-page
Dynamic: license-file
Dynamic: requires-python

# Subspace Computing Engine - Python SDK

Python SDK for the Subspace Computing Engine API (by Beausoft).

## Installation

```bash
pip install subspacecomputing
```

## Usage

### Initialization

```python
from subspacecomputing import ASTRIA

# Initialize the client (defaults to production URL)
client = ASTRIA(api_key='your-api-key-here')

# For local testing or custom environments (optional)
# client = ASTRIA(api_key='your-api-key-here', base_url='http://localhost:8000')
```

### Teams (`X-Team-Id`)

Keys can carry a `default_team_id` from the portal. If the user is in **several** teams, some endpoints require an explicit team: pass `team_id=...` or call `set_team_id` so the SDK sends `X-Team-Id`.

If you omit it when required, the API returns **400** with `error.reason` **`team_context_required`**. Use `ValidationError` and read `exc.reason`.

```python
client = ASTRIA(api_key="...", team_id="00000000-0000-0000-0000-000000000000")
client.set_team_id(None)  # clear override for following requests
```

### Simple Projection (1 scenario)

```python
# Create a simple SP Model
spec = {
    'scenarios': 1,  # Must be 1 for /project
    'steps': 12,
    'variables': [
        {
            'name': 'capital',
            'init': 1000.0,
            'formula': 'capital[t-1] * 1.05'  # 5% growth per period
        }
    ]
}

# Run the projection
result = client.project(spec)

# Display results
print(f"Final capital: {result['final_values']['capital']}")
print(f"Trajectory: {result['trajectory']['capital']}")
```

### Monte Carlo Simulation (Multiple scenarios)

```python
# SP Model with random variables
spec = {
    'scenarios': 1000,  # 1000 Monte Carlo scenarios
    'steps': 12,
    'variables': [
        {
            'name': 'taux',
            'dist': 'uniform',
            'params': {'min': 0.03, 'max': 0.07},
            'per': 'scenario'
        },
        {
            'name': 'capital',
            'init': 1000.0,
            'formula': 'capital[t-1] * (1 + taux)'
        }
    ]
}

# Run the simulation
result = client.simulate(spec)

# Analyze results
print(f"Mean final capital: {result['last_mean']['capital']}")
print(f"Median: {result['statistics']['capital']['median']}")
print(f"P5: {result['statistics']['capital']['percentiles']['5']}")
print(f"P95: {result['statistics']['capital']['percentiles']['95']}")
```

### Batch Mode (Multiple Entities)

```python
# SP Model template
template = {
    'scenarios': 1,
    'steps': '65 - batch_params.age',  # Dynamic steps
    'variables': [
        {
            'name': 'age_actuel',
            'init': 'batch_params.age'
        },
        {
            'name': 'salaire',
            'init': 'batch_params.salary',
            'formula': 'salaire[t-1] * 1.03'  # 3% annual increase
        },
        {
            'name': 'capital_retraite',
            'init': 0.0,
            'formula': 'capital_retraite[t-1] * 1.05 + salaire[t] * 0.10'
        }
    ]
}

# Entity data
batch_params = [
    {'entity_id': 'emp_001', 'age': 45, 'salary': 60000},
    {'entity_id': 'emp_002', 'age': 50, 'salary': 80000},
    {'entity_id': 'emp_003', 'age': 35, 'salary': 50000}
]

# Global aggregations (optional)
aggregations = [
    {
        'name': 'capital_total',
        'formula': 'sum(capital_retraite[t_final])'
    },
    {
        'name': 'moyenne_capital',
        'formula': 'mean(capital_retraite[t_final])'
    }
]

# Run batch
result = client.project_batch(
    template=template,
    batch_params=batch_params,
    aggregations=aggregations
)

# Analyze results
for entity in result['entities']:
    print(f"{entity['_entity_id']}: Capital = {entity['final_values']['capital_retraite']}")

print(f"Total capital: {result['aggregations']['capital_total']}")
print(f"Average: {result['aggregations']['moyenne_capital']}")
```

### Validation

```python
# Validate an SP Model before execution
validation = client.validate(spec)

if validation['is_valid']:
    print("✅ SP Model is valid")
    if validation.get('warnings'):
        print(f"⚠️  Warnings: {validation['warnings']}")
else:
    print(f"❌ Errors: {validation['errors']}")
```

### Utilities

```python
# Get examples
examples = client.get_examples()
print(f"Available examples: {len(examples['examples'])}")

# Check usage
usage = client.get_usage()
print(f"Simulations used: {usage['usage']['simulations_used']}/{usage['usage']['simulations_limit']}")

# Get plans
plans = client.get_plans()
for plan in plans['plans']:
    print(f"{plan['name']}: ${plan['price_monthly']}/month")
```

### Error Handling

```python
from subspacecomputing import (
    Subspace,
    SubspaceError,
    QuotaExceededError,
    RateLimitError,
    AuthenticationError,
    ValidationError,
)

try:
    result = client.simulate(spec)
except QuotaExceededError as e:
    print(f"Monthly quota exceeded: {e}")
except RateLimitError as e:
    print(f"Rate limit exceeded: {e}")
    print(f"Retry after: {e.response.headers.get('Retry-After')} seconds")
except AuthenticationError as e:
    print(f"Invalid API key: {e}")
except ValidationError as e:
    print(f"Validation error: {e.detail}")
except SubspaceError as e:
    print(f"API error: {e}")
```

### Rate Limit and Quota Information

After making a request, you can check your rate limit and quota status:

```python
# Make a request
result = client.project(spec)

# Check rate limit info
rate_limit = client.get_rate_limit_info()
if rate_limit:
    print(f"Rate limit: {rate_limit['remaining']}/{rate_limit['limit']} remaining")

# Check quota info
quota = client.get_quota_info()
if quota:
    print(f"Quota: {quota['used']}/{quota['limit']} used, {quota['remaining']} remaining")
```

## Documentation

Check out the full documentation at https://www.subspacecomputing.com/developer

API reference is available at https://www.subspacecomputing.com/docs

For support, reach out to contact@beausoft.ca

## License

MIT License. Check the LICENSE file for details.

## Copyright

© 2025 Beausoft Inc. All Rights Reserved
