|
1 | 1 | # Insforge Python SDK |
2 | 2 |
|
3 | | -Bootstrap package for the Insforge Python client. |
| 3 | +Async-first, stateless Python client for the [Insforge](https://insforge.com) platform. Covers auth, database, storage, serverless functions, AI, email, and metadata APIs. |
4 | 4 |
|
| 5 | +## Requirements |
| 6 | + |
| 7 | +- Python 3.11+ |
| 8 | + |
| 9 | +## Installation |
| 10 | + |
| 11 | +```bash |
| 12 | +pip install insforge |
| 13 | +``` |
| 14 | + |
| 15 | +## Quick Start |
| 16 | + |
| 17 | +```python |
| 18 | +import asyncio |
| 19 | +from insforge import InsforgeClient |
| 20 | + |
| 21 | +async def main(): |
| 22 | + async with InsforgeClient(base_url="https://your-project.insforge.app", api_key="ins_xxx") as client: |
| 23 | + # Public endpoint (API key only) |
| 24 | + config = await client.auth.get_public_config() |
| 25 | + |
| 26 | + # Sign in |
| 27 | + session = await client.auth.sign_in_with_password(email="user@example.com", password="secret") |
| 28 | + token = session.access_token |
| 29 | + |
| 30 | + # Authenticated request |
| 31 | + me = await client.auth.get_current_session(access_token=token) |
| 32 | + print(me.user.email) |
| 33 | + |
| 34 | +asyncio.run(main()) |
| 35 | +``` |
| 36 | + |
| 37 | +## Modules |
| 38 | + |
| 39 | +### Auth |
| 40 | + |
| 41 | +User registration, sign-in, session management, email verification, password reset, admin auth, and OAuth. |
| 42 | + |
| 43 | +```python |
| 44 | +# Sign in |
| 45 | +session = await client.auth.sign_in_with_password(email="...", password="...") |
| 46 | + |
| 47 | +# Create user |
| 48 | +user = await client.auth.create_user(email="...", password="...") |
| 49 | + |
| 50 | +# Email verification |
| 51 | +await client.auth.send_email_verification(email="...") |
| 52 | +await client.auth.verify_email(email="...", otp="123456") |
| 53 | + |
| 54 | +# Password reset |
| 55 | +await client.auth.send_reset_password_email(email="...") |
| 56 | +resp = await client.auth.exchange_reset_password_token(email="...", code="123456") |
| 57 | +await client.auth.reset_password(new_password="...", token=resp.token) |
| 58 | + |
| 59 | +# User management (admin) |
| 60 | +users = await client.auth.list_users(access_token=admin_token) |
| 61 | +await client.auth.delete_users(["user-id-1"], access_token=admin_token) |
| 62 | + |
| 63 | +# Config |
| 64 | +await client.auth.get_config(access_token=admin_token) |
| 65 | +await client.auth.update_config({"requireEmailVerification": True}, access_token=admin_token) |
| 66 | +``` |
| 67 | + |
| 68 | +### Database |
| 69 | + |
| 70 | +PostgREST-style query builder and table administration. |
| 71 | + |
| 72 | +```python |
| 73 | +# Query records |
| 74 | +rows = await client.database.from_("posts") \ |
| 75 | + .select("id,title") \ |
| 76 | + .eq("status", "published") \ |
| 77 | + .order("created_at", desc=True) \ |
| 78 | + .limit(10) \ |
| 79 | + .execute() |
| 80 | + |
| 81 | +# Insert |
| 82 | +await client.database.from_("posts").insert([{"title": "Hello", "status": "draft"}]) |
| 83 | + |
| 84 | +# Update with filters |
| 85 | +await client.database.from_("posts").eq("id", 1).update({"status": "published"}) |
| 86 | + |
| 87 | +# Delete with filters |
| 88 | +await client.database.from_("posts").eq("id", 1).delete() |
| 89 | + |
| 90 | +# Table admin |
| 91 | +tables = await client.database.list_tables() |
| 92 | +schema = await client.database.get_table_schema("posts") |
| 93 | +await client.database.create_table( |
| 94 | + table_name="comments", |
| 95 | + columns=[{"name": "id", "type": "uuid", "nullable": False}], |
| 96 | +) |
| 97 | +await client.database.update_table_schema("comments", add_columns=[...]) |
| 98 | +await client.database.delete_table("comments") |
| 99 | +``` |
| 100 | + |
| 101 | +### Storage |
| 102 | + |
| 103 | +Object upload, download, and deletion. |
| 104 | + |
| 105 | +```python |
| 106 | +buckets = await client.storage.list_buckets() |
| 107 | + |
| 108 | +await client.storage.upload_object("my-bucket", "photos/cat.jpg", image_bytes, content_type="image/jpeg") |
| 109 | + |
| 110 | +data = await client.storage.download_object("my-bucket", "photos/cat.jpg") |
| 111 | + |
| 112 | +await client.storage.delete_object("my-bucket", "photos/cat.jpg") |
| 113 | +``` |
| 114 | + |
| 115 | +### Functions |
| 116 | + |
| 117 | +Serverless function admin and invocation. |
| 118 | + |
| 119 | +```python |
| 120 | +# CRUD |
| 121 | +await client.functions.create_function(name="greet", code="export default (req) => ...", access_token=token) |
| 122 | +fns = await client.functions.list_functions(access_token=token) |
| 123 | +fn = await client.functions.get_function("greet", access_token=token) |
| 124 | +await client.functions.update_function("greet", code="...", access_token=token) |
| 125 | +await client.functions.delete_function("greet", access_token=token) |
| 126 | + |
| 127 | +# Invoke |
| 128 | +result = await client.functions.invoke("greet", body={"name": "World"}) |
| 129 | +``` |
| 130 | + |
| 131 | +### AI |
| 132 | + |
| 133 | +Chat completions, image generation, embeddings, configuration, usage tracking, and credits. |
| 134 | + |
| 135 | +```python |
| 136 | +from insforge.ai.models import AIChatMessage |
| 137 | + |
| 138 | +# Chat |
| 139 | +resp = await client.ai.chat_completion( |
| 140 | + model="openai/gpt-4o", |
| 141 | + messages=[AIChatMessage(role="user", content="Hello!")], |
| 142 | + access_token=token, |
| 143 | +) |
| 144 | +print(resp.text) |
| 145 | + |
| 146 | +# Image generation |
| 147 | +images = await client.ai.generate_images(model="openai/dall-e-3", prompt="A cat", access_token=token) |
| 148 | + |
| 149 | +# Embeddings |
| 150 | +emb = await client.ai.generate_embeddings(model="openai/text-embedding-3-small", input="hello", access_token=token) |
| 151 | + |
| 152 | +# Configuration & usage |
| 153 | +configs = await client.ai.list_configurations(access_token=token) |
| 154 | +summary = await client.ai.get_usage_summary(access_token=token) |
| 155 | +credits = await client.ai.get_credits(access_token=token) |
| 156 | +models = await client.ai.list_models(access_token=token) |
| 157 | +``` |
| 158 | + |
| 159 | +### Email |
| 160 | + |
| 161 | +```python |
| 162 | +await client.email.send_raw( |
| 163 | + to="recipient@example.com", |
| 164 | + subject="Hello", |
| 165 | + html="<h1>Hi there</h1>", |
| 166 | + access_token=token, |
| 167 | +) |
| 168 | +``` |
| 169 | + |
| 170 | +### Metadata |
| 171 | + |
| 172 | +```python |
| 173 | +app = await client.metadata.get_app_metadata() |
| 174 | +db = await client.metadata.get_database_metadata() |
| 175 | +key = await client.metadata.get_api_key() |
| 176 | +``` |
| 177 | + |
| 178 | +## Error Handling |
| 179 | + |
| 180 | +```python |
| 181 | +from insforge.exceptions import InsforgeHTTPError, InsforgeAuthError |
| 182 | + |
| 183 | +try: |
| 184 | + await client.auth.sign_in_with_password(email="...", password="wrong") |
| 185 | +except InsforgeAuthError as e: |
| 186 | + print(e.status_code, e.error, e.message, e.next_action) |
| 187 | +except InsforgeHTTPError as e: |
| 188 | + print(e.method, e.path, e.status_code) |
| 189 | +``` |
| 190 | + |
| 191 | +Exception hierarchy: |
| 192 | + |
| 193 | +- `InsforgeError` - base |
| 194 | + - `InsforgeHTTPError` - HTTP errors (status code, parsed error/message) |
| 195 | + - `InsforgeAuthError` - auth-specific HTTP errors |
| 196 | + - `InsforgeValidationError` - Pydantic validation failures |
| 197 | + - `InsforgeSerializationError` - serialization failures |
| 198 | + |
| 199 | +## Authentication Model |
| 200 | + |
| 201 | +The SDK is **stateless** - it never stores or caches tokens. Every method that requires user auth takes an explicit `access_token` parameter. The API key is sent as `X-API-Key` on every request automatically. |
| 202 | + |
| 203 | +```python |
| 204 | +# API key only (default for all requests) |
| 205 | +config = await client.auth.get_public_config() |
| 206 | + |
| 207 | +# API key + bearer token |
| 208 | +me = await client.auth.get_current_session(access_token="user_jwt_here") |
| 209 | +``` |
| 210 | + |
| 211 | +## Development |
| 212 | + |
| 213 | +### Setup |
| 214 | + |
| 215 | +```bash |
| 216 | +git clone <repo-url> |
| 217 | +cd insforge-python |
| 218 | +python -m venv .venv && source .venv/bin/activate |
| 219 | +pip install -e ".[dev]" 2>/dev/null || pip install -e . |
| 220 | +pip install pytest pytest-asyncio |
| 221 | +``` |
| 222 | + |
| 223 | +### Run Tests |
| 224 | + |
| 225 | +```bash |
| 226 | +python -m pytest |
| 227 | +``` |
| 228 | + |
| 229 | +### Build |
| 230 | + |
| 231 | +```bash |
| 232 | +pip install build |
| 233 | +python -m build |
| 234 | +``` |
| 235 | + |
| 236 | +This produces `dist/insforge-<version>.tar.gz` and `dist/insforge-<version>-py3-none-any.whl`. |
| 237 | + |
| 238 | +### Publish to PyPI |
| 239 | + |
| 240 | +```bash |
| 241 | +pip install twine |
| 242 | + |
| 243 | +# Test PyPI (recommended first) |
| 244 | +twine upload --repository testpypi dist/* |
| 245 | + |
| 246 | +# Production PyPI |
| 247 | +twine upload dist/* |
| 248 | +``` |
| 249 | + |
| 250 | +## License |
| 251 | + |
| 252 | +See [LICENSE](LICENSE) for details. |
0 commit comments