Skip to main content

Python SDK for SINAS - AI Agent Platform

Project description

SINAS Python SDK

Python SDK for SINAS - AI Agent & Automation Orchestration Platform.

Runtime API Client for executing agents, managing state, running webhooks, and tracking executions.

Installation

pip install sinas

With FastAPI integration:

pip install sinas[fastapi]

Quick Start

Authentication

from sinas import SinasClient

# Initialize client with base URL
client = SinasClient(base_url="http://localhost:51245")

# Step 1: Login with email (sends OTP)
login_response = client.auth.login("user@example.com")
session_id = login_response["session_id"]

# Step 2: Verify OTP and get access + refresh tokens
response = client.auth.verify_otp(session_id, "123456")
# Tokens are automatically set on the client
access_token = response["access_token"]
refresh_token = response["refresh_token"]

# Or use an API key directly
client = SinasClient(
    base_url="http://localhost:51245",
    api_key="your-api-key"
)

Environment Variables

Configure the client using environment variables:

export SINAS_BASE_URL="http://localhost:51245"
export SINAS_API_KEY="your-api-key"
# or
export SINAS_TOKEN="your-jwt-token"
from sinas import SinasClient

# Automatically uses environment variables
client = SinasClient()

Core Features

State Management

Store and retrieve key-value data with namespacing and visibility controls:

# Create/set a state value
state = client.state.set(
    namespace="user_prefs",
    key="theme",
    value={"mode": "dark", "accent": "blue"},
    description="User theme preferences",
    visibility="private"  # private, group, or public
)

# Get a specific state
state = client.state.get(state["id"])
print(state["value"])  # {"mode": "dark", "accent": "blue"}

# List states with filtering
states = client.state.list(
    namespace="user_prefs",
    visibility="private",
    tags="settings",
    search="theme"
)

# Update state
updated = client.state.update(
    state["id"],
    value={"mode": "light", "accent": "green"}
)

# Delete state
client.state.delete(state["id"])

Agent Chats

Create chats with agents and send messages:

# Create a chat with an agent
chat = client.chats.create(
    namespace="customer-support",
    agent_name="cs-agent-v1",
    title="Customer Inquiry",
    input={"customer_id": "12345"}  # Optional input validated by agent
)

# Send a message (blocking)
response = client.chats.send(
    chat_id=chat["id"],
    content="What's the status of my order?"
)
print(response["content"])

# Stream a message (Server-Sent Events)
for chunk in client.chats.stream(
    chat_id=chat["id"],
    content="Tell me about your services"
):
    # Parse SSE data
    import json
    try:
        data = json.loads(chunk)
        if "content" in data:
            print(data["content"], end="", flush=True)
    except:
        pass

# Get chat with all messages
chat_data = client.chats.get(chat["id"])
messages = chat_data["messages"]

# List all chats
chats = client.chats.list()

# Update chat
client.chats.update(chat["id"], title="Updated Title")

# Delete chat
client.chats.delete(chat["id"])

Webhook Execution

Execute webhooks by path:

# Execute a webhook
result = client.webhooks.run(
    path="process-payment",
    method="POST",
    body={"amount": 100, "currency": "USD"},
    headers={"X-Custom-Header": "value"},
    query={"idempotency_key": "abc123"}
)

print(result["execution_id"])  # Track execution
print(result["result"])  # Function output

Executions

Track and manage function executions:

# List recent executions
executions = client.executions.list(
    function_name="payment-processor",
    status="completed",
    limit=10
)

# Get execution details
execution = client.executions.get("execution-id")
print(execution["status"])  # running, completed, failed, awaiting_input
print(execution["output_data"])

# Get execution steps
steps = client.executions.get_steps("execution-id")
for step in steps:
    print(f"{step['step_name']}: {step['status']}")

# Continue a paused execution
result = client.executions.continue_execution(
    "execution-id",
    input={"user_choice": "approve"}
)

Authentication Methods

# Refresh access token
refreshed = client.auth.refresh(refresh_token)
new_access_token = refreshed["access_token"]

# Exchange external OIDC token
response = client.auth.external_auth(external_oidc_token)

# Get current user info
user = client.auth.get_me()
print(user["email"])

# Check user permissions with OR logic (user needs at least one)
check = client.auth.check_permissions(
    ["sinas.functions.read:all", "sinas.functions.create:own"],
    logic="OR"
)
print(check["result"])  # True if user has at least one permission
print(check["checks"])  # Detailed results for each permission

# Check user permissions with AND logic (user needs all)
check = client.auth.check_permissions(
    ["sinas.admin:all", "sinas.functions.write:all"],
    logic="AND"
)
print(check["result"])  # True only if user has all permissions

# Logout (revoke refresh token)
client.auth.logout(refresh_token)

FastAPI Integration

SINAS provides two powerful approaches for FastAPI integration:

Approach 1: Ready-to-Mount Routers

Mount SINAS Runtime API endpoints directly in your FastAPI app with automatic authentication:

from fastapi import FastAPI
from sinas.integrations.routers import create_runtime_router

app = FastAPI()

# Mount ALL runtime endpoints at once
app.include_router(
    create_runtime_router("http://localhost:51245", include_auth=False),
    prefix="/api/runtime"
)

# This automatically creates endpoints like:
# - GET    /api/runtime/states
# - POST   /api/runtime/states
# - GET    /api/runtime/chats
# - POST   /api/runtime/agents/{namespace}/{agent}/chats
# - POST   /api/runtime/webhooks/{path}
# - GET    /api/runtime/executions

Or mount individual routers:

from sinas.integrations.routers import (
    create_state_router,
    create_chat_router,
    create_webhook_router,
    create_executions_router
)

app.include_router(create_state_router("http://localhost:51245"), prefix="/runtime/states")
app.include_router(create_chat_router("http://localhost:51245"), prefix="/runtime/chats")
app.include_router(create_webhook_router("http://localhost:51245"), prefix="/runtime/webhooks")
app.include_router(create_executions_router("http://localhost:51245"), prefix="/runtime/executions")

Approach 2: Custom Endpoints with SDK Client

Build custom endpoints with business logic using the authenticated client:

import asyncio
from fastapi import FastAPI, Depends
from sinas import SinasClient
from sinas.integrations.fastapi import SinasAuth

app = FastAPI()
sinas = SinasAuth(base_url="http://localhost:51245")

# Include auto-generated auth endpoints
app.include_router(sinas.router, prefix="/auth")

# Custom endpoint with auto-authentication
@app.get("/my-states")
async def get_my_states(client: SinasClient = Depends(sinas)):
    """List user's states with custom filtering."""
    states = await asyncio.to_thread(client.state.list, limit=10)
    return [{"id": s["id"], "key": s["key"]} for s in states]

# Combine multiple SDK calls
@app.post("/quick-chat")
async def quick_chat(
    agent_namespace: str,
    agent_name: str,
    message: str,
    client: SinasClient = Depends(sinas)
):
    """Create chat and send message in one request."""
    chat = await asyncio.to_thread(client.chats.create, agent_namespace, agent_name)
    response = await asyncio.to_thread(client.chats.send, chat["id"], message)
    return {"chat_id": chat["id"], "response": response}

# Permission-protected endpoint
@app.post("/admin/cleanup")
async def cleanup_states(
    namespace: str,
    client: SinasClient = Depends(sinas.require("sinas.contexts.delete:all"))
):
    """Admin-only: Clean up states (requires permission)."""
    states = await asyncio.to_thread(client.state.list, namespace=namespace)
    for state in states:
        await asyncio.to_thread(client.state.delete, state["id"])
    return {"deleted": len(states)}

Auto-Generated Auth Endpoints

The sinas.router automatically provides:

  • POST /auth/login - Send OTP to email
  • POST /auth/verify-otp - Verify OTP and get tokens
  • POST /auth/refresh - Refresh access token using refresh token
  • GET /auth/me - Get current authenticated user

Permission-Based Access Control

Use sinas.require() to protect endpoints with flexible AND/OR logic:

# Require a single permission
@app.delete("/states/{state_id}")
async def delete_state(
    state_id: str,
    client: SinasClient = Depends(sinas.require("sinas.contexts.delete:own"))
):
    await asyncio.to_thread(client.state.delete, state_id)
    return {"message": "Deleted"}

# Require ANY of multiple permissions (OR logic - default)
@app.post("/moderate-content")
async def moderate_content(
    client: SinasClient = Depends(
        sinas.require("sinas.admin:all", "sinas.moderation.write:all")
    )
):
    # User needs EITHER admin:all OR moderation.write:all
    return {"message": "Content moderated"}

# Require ALL permissions (AND logic)
@app.post("/admin-action")
async def admin_action(
    client: SinasClient = Depends(
        sinas.require("sinas.admin:all", "sinas.functions.write:all", logic="AND")
    )
):
    # User needs BOTH admin:all AND functions.write:all
    return {"message": "Admin action completed"}

# Alternative: Use require_all() for AND logic
@app.post("/super-admin-action")
async def super_admin_action(
    client: SinasClient = Depends(
        sinas.require_all("sinas.admin:all", "sinas.functions.write:all", "sinas.contexts.write:all")
    )
):
    # User needs ALL three permissions
    return {"message": "Super admin action completed"}

Permission Logic:

  • OR logic (default): User needs at least one of the specified permissions
  • AND logic: User needs all of the specified permissions
  • Use logic="AND" parameter or require_all() method for AND logic

Complete Example

See examples/fastapi_app.py for a complete working example with both approaches.

Run it with:

uvicorn examples.fastapi_app:app --reload

Then visit http://localhost:8000/docs for interactive API documentation.

Context Manager

The client can be used as a context manager for proper resource cleanup:

with SinasClient(base_url="http://localhost:51245") as client:
    states = client.state.list()
    # Client is automatically closed when exiting

Error Handling

from sinas import (
    SinasClient,
    SinasAPIError,
    SinasAuthError,
    SinasNotFoundError,
    SinasValidationError
)

client = SinasClient(base_url="http://localhost:51245")

try:
    state = client.state.get("non-existent-id")
except SinasNotFoundError as e:
    print(f"State not found: {e}")
except SinasAuthError as e:
    print(f"Authentication failed: {e}")
except SinasValidationError as e:
    print(f"Validation error: {e}")
except SinasAPIError as e:
    print(f"API error: {e}")
    print(f"Status code: {e.status_code}")
    print(f"Response: {e.response}")

API Reference

SinasClient

auth

  • login(email) - Send OTP to email
  • verify_otp(session_id, otp_code) - Verify OTP and get tokens
  • external_auth(token) - Exchange external OIDC token
  • refresh(refresh_token) - Refresh access token
  • logout(refresh_token) - Revoke refresh token
  • get_me() - Get current user info
  • check_permissions(permissions, logic="AND") - Check if user has permissions with AND/OR logic

state

  • set(namespace, key, value, ...) - Create/update state
  • get(state_id) - Get state by ID
  • list(namespace?, visibility?, ...) - List states with filters
  • update(state_id, value?, ...) - Update state
  • delete(state_id) - Delete state

chats

  • create(namespace, agent_name, input?, title?) - Create chat with agent
  • send(chat_id, content) - Send message (blocking)
  • stream(chat_id, content) - Send message (streaming SSE)
  • get(chat_id) - Get chat with messages
  • list() - List user's chats
  • update(chat_id, title?) - Update chat
  • delete(chat_id) - Delete chat

webhooks

  • run(path, method?, body?, headers?, query?) - Execute webhook

executions

  • list(function_name?, status?, skip?, limit?) - List executions
  • get(execution_id) - Get execution details
  • get_steps(execution_id) - Get execution steps
  • continue_execution(execution_id, input) - Continue paused execution

FastAPI Integration

SinasAuth(base_url, auto_error=True)

FastAPI dependency for authentication and authorization.

Methods:

  • require(*permissions, logic="OR") - Create dependency requiring permissions with AND/OR logic
  • require_all(*permissions) - Create dependency requiring ALL permissions (AND logic)

Router Functions

  • create_runtime_router(base_url, include_auth=False) - Complete runtime router
  • create_state_router(base_url, prefix="") - State endpoints
  • create_chat_router(base_url, prefix="") - Chat endpoints
  • create_webhook_router(base_url, prefix="") - Webhook endpoints
  • create_executions_router(base_url, prefix="") - Execution endpoints

Development

Install Development Dependencies

pip install -e ".[dev]"

Run Examples

# Basic usage
python examples/basic_usage.py

# FastAPI app
uvicorn examples.fastapi_app:app --reload

Code Formatting

black sinas
ruff check sinas

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sinas-0.1.2.tar.gz (24.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sinas-0.1.2-py3-none-any.whl (19.9 kB view details)

Uploaded Python 3

File details

Details for the file sinas-0.1.2.tar.gz.

File metadata

  • Download URL: sinas-0.1.2.tar.gz
  • Upload date:
  • Size: 24.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.8

File hashes

Hashes for sinas-0.1.2.tar.gz
Algorithm Hash digest
SHA256 8986a52fb863b4e6cfe25e70c1268e9776ba9218b6412bc26d9f991a5aeebc5b
MD5 ba46c2260c72179e7e65b6d98bb2a352
BLAKE2b-256 f313a46a7fe507d054aeb080dd22d10583fdf928ee17bff839544aae100d21c0

See more details on using hashes here.

File details

Details for the file sinas-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: sinas-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 19.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.8

File hashes

Hashes for sinas-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 9508a0a1e177fd47482228dd6e4bdb939abcaa3911275e42d8893fda2c09c49d
MD5 2cdc7f45fb6dbfc5702532815c0bb853
BLAKE2b-256 7f6b9c4a17109edc542e6829150e6d7177f7b29a0fc4b5c338a9d16053890ed8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page