quantlix 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- quantlix-0.1.0/LICENSE +21 -0
- quantlix-0.1.0/PKG-INFO +107 -0
- quantlix-0.1.0/README.md +68 -0
- quantlix-0.1.0/api/__init__.py +1 -0
- quantlix-0.1.0/api/auth.py +81 -0
- quantlix-0.1.0/api/config.py +61 -0
- quantlix-0.1.0/api/db.py +38 -0
- quantlix-0.1.0/api/disposable_domains.py +499 -0
- quantlix-0.1.0/api/email.py +202 -0
- quantlix-0.1.0/api/main.py +83 -0
- quantlix-0.1.0/api/models.py +142 -0
- quantlix-0.1.0/api/queue.py +23 -0
- quantlix-0.1.0/api/rate_limit.py +178 -0
- quantlix-0.1.0/api/routes/__init__.py +1 -0
- quantlix-0.1.0/api/routes/auth.py +477 -0
- quantlix-0.1.0/api/routes/billing.py +234 -0
- quantlix-0.1.0/api/routes/deploy.py +34 -0
- quantlix-0.1.0/api/routes/health.py +20 -0
- quantlix-0.1.0/api/routes/jobs.py +43 -0
- quantlix-0.1.0/api/routes/run.py +76 -0
- quantlix-0.1.0/api/routes/status.py +63 -0
- quantlix-0.1.0/api/routes/usage.py +143 -0
- quantlix-0.1.0/api/schemas.py +252 -0
- quantlix-0.1.0/api/usage_service.py +82 -0
- quantlix-0.1.0/cli/__init__.py +1 -0
- quantlix-0.1.0/cli/main.py +367 -0
- quantlix-0.1.0/orchestrator/__init__.py +1 -0
- quantlix-0.1.0/orchestrator/config.py +28 -0
- quantlix-0.1.0/orchestrator/inference_client.py +38 -0
- quantlix-0.1.0/orchestrator/k8s.py +124 -0
- quantlix-0.1.0/orchestrator/main.py +43 -0
- quantlix-0.1.0/orchestrator/worker.py +192 -0
- quantlix-0.1.0/pyproject.toml +55 -0
- quantlix-0.1.0/quantlix.egg-info/PKG-INFO +107 -0
- quantlix-0.1.0/quantlix.egg-info/SOURCES.txt +42 -0
- quantlix-0.1.0/quantlix.egg-info/dependency_links.txt +1 -0
- quantlix-0.1.0/quantlix.egg-info/entry_points.txt +2 -0
- quantlix-0.1.0/quantlix.egg-info/requires.txt +20 -0
- quantlix-0.1.0/quantlix.egg-info/top_level.txt +4 -0
- quantlix-0.1.0/sdk/__init__.py +1 -0
- quantlix-0.1.0/sdk/python/__init__.py +24 -0
- quantlix-0.1.0/sdk/quantlix/__init__.py +21 -0
- quantlix-0.1.0/sdk/quantlix/client.py +311 -0
- quantlix-0.1.0/setup.cfg +4 -0
quantlix-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Quantlix
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
quantlix-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quantlix
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Quantlix — AI-focused GPU inference platform
|
|
5
|
+
Author: Quantlix
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://quantlix.ai
|
|
8
|
+
Project-URL: Repository, https://github.com/quantlix/cloud
|
|
9
|
+
Project-URL: Documentation, https://github.com/quantlix/cloud#readme
|
|
10
|
+
Keywords: ai,inference,gpu,ml,deployment
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Requires-Python: >=3.12
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: typer[all]>=0.9.0
|
|
20
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
21
|
+
Requires-Dist: httpx>=0.26.0
|
|
22
|
+
Requires-Dist: rich>=13.0.0
|
|
23
|
+
Provides-Extra: full
|
|
24
|
+
Requires-Dist: fastapi>=0.109.0; extra == "full"
|
|
25
|
+
Requires-Dist: uvicorn[standard]>=0.27.0; extra == "full"
|
|
26
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.0.0; extra == "full"
|
|
27
|
+
Requires-Dist: asyncpg>=0.29.0; extra == "full"
|
|
28
|
+
Requires-Dist: greenlet>=3.0.0; extra == "full"
|
|
29
|
+
Requires-Dist: psycopg[binary]>=3.0.0; extra == "full"
|
|
30
|
+
Requires-Dist: redis>=5.0.0; extra == "full"
|
|
31
|
+
Requires-Dist: minio>=7.2.0; extra == "full"
|
|
32
|
+
Requires-Dist: python-jose[cryptography]>=3.3.0; extra == "full"
|
|
33
|
+
Requires-Dist: bcrypt>=4.0.0; extra == "full"
|
|
34
|
+
Requires-Dist: pydantic-settings>=2.1.0; extra == "full"
|
|
35
|
+
Requires-Dist: prometheus-client>=0.19.0; extra == "full"
|
|
36
|
+
Requires-Dist: aiosmtplib>=3.0.0; extra == "full"
|
|
37
|
+
Requires-Dist: stripe>=8.0.0; extra == "full"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
# Quantlix
|
|
41
|
+
|
|
42
|
+
Deploy AI models in seconds. A simple inference platform with REST API, usage-based billing, and Kubernetes orchestration.
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- **REST API** — Deploy models, run inference, check status
|
|
47
|
+
- **Usage-based billing** — Free, Starter (€9/mo), Pro (€19/mo) tiers with Stripe
|
|
48
|
+
- **Queue & orchestration** — Redis queue, K8s job scheduling
|
|
49
|
+
- **Customer portal** — Next.js dashboard, usage graphs, real-time logs
|
|
50
|
+
- **CLI** — `quantlix deploy`, `quantlix run` for local dev
|
|
51
|
+
|
|
52
|
+
## Quick start
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# 1. Clone and start services
|
|
56
|
+
git clone https://github.com/quantlix/cloud
|
|
57
|
+
cd cloud
|
|
58
|
+
cp .env.example .env
|
|
59
|
+
docker compose up -d
|
|
60
|
+
|
|
61
|
+
# 2. Create account (or use dev seed)
|
|
62
|
+
pip install -e .
|
|
63
|
+
quantlix signup --email you@example.com --password YourSecurePassword123!
|
|
64
|
+
# Verify email, then:
|
|
65
|
+
quantlix login --email you@example.com --password YourSecurePassword123!
|
|
66
|
+
|
|
67
|
+
# 3. Deploy and run
|
|
68
|
+
quantlix deploy qx-example
|
|
69
|
+
quantlix run <deployment_id> -i '{"prompt": "Hello!"}'
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
See [docs/CLI_GUIDE.md](docs/CLI_GUIDE.md) for detailed setup.
|
|
73
|
+
|
|
74
|
+
## Architecture
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
┌─────────────┐ ┌─────────┐ ┌──────────────┐
|
|
78
|
+
│ Portal │────▶│ API │────▶│ PostgreSQL │
|
|
79
|
+
│ (Next.js) │ │(FastAPI)│ │ Redis │
|
|
80
|
+
└─────────────┘ └────┬────┘ └──────────────┘
|
|
81
|
+
│
|
|
82
|
+
▼
|
|
83
|
+
┌──────────────┐
|
|
84
|
+
│ Orchestrator │────▶ Kubernetes / Inference
|
|
85
|
+
│ (Worker) │
|
|
86
|
+
└──────────────┘
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Project structure
|
|
90
|
+
|
|
91
|
+
| Directory | Description |
|
|
92
|
+
|-----------|-------------|
|
|
93
|
+
| `api/` | FastAPI backend (auth, deploy, run, billing, usage) |
|
|
94
|
+
| `portal/` | Next.js customer dashboard |
|
|
95
|
+
| `orchestrator/` | Redis queue worker, K8s job runner |
|
|
96
|
+
| `inference/` | Mock inference service (replace with your model server) |
|
|
97
|
+
| `sdk/` | Python client library |
|
|
98
|
+
| `cli/` | `quantlix` CLI |
|
|
99
|
+
| `infra/` | Terraform (Hetzner), Kubernetes manifests |
|
|
100
|
+
|
|
101
|
+
## Production
|
|
102
|
+
|
|
103
|
+
See [docs/GO_LIVE.md](docs/GO_LIVE.md) for the full deployment checklist: Stripe, SMTP, DNS, SSL, secrets.
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
MIT — see [LICENSE](LICENSE).
|
quantlix-0.1.0/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Quantlix
|
|
2
|
+
|
|
3
|
+
Deploy AI models in seconds. A simple inference platform with REST API, usage-based billing, and Kubernetes orchestration.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **REST API** — Deploy models, run inference, check status
|
|
8
|
+
- **Usage-based billing** — Free, Starter (€9/mo), Pro (€19/mo) tiers with Stripe
|
|
9
|
+
- **Queue & orchestration** — Redis queue, K8s job scheduling
|
|
10
|
+
- **Customer portal** — Next.js dashboard, usage graphs, real-time logs
|
|
11
|
+
- **CLI** — `quantlix deploy`, `quantlix run` for local dev
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# 1. Clone and start services
|
|
17
|
+
git clone https://github.com/quantlix/cloud
|
|
18
|
+
cd cloud
|
|
19
|
+
cp .env.example .env
|
|
20
|
+
docker compose up -d
|
|
21
|
+
|
|
22
|
+
# 2. Create account (or use dev seed)
|
|
23
|
+
pip install -e .
|
|
24
|
+
quantlix signup --email you@example.com --password YourSecurePassword123!
|
|
25
|
+
# Verify email, then:
|
|
26
|
+
quantlix login --email you@example.com --password YourSecurePassword123!
|
|
27
|
+
|
|
28
|
+
# 3. Deploy and run
|
|
29
|
+
quantlix deploy qx-example
|
|
30
|
+
quantlix run <deployment_id> -i '{"prompt": "Hello!"}'
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
See [docs/CLI_GUIDE.md](docs/CLI_GUIDE.md) for detailed setup.
|
|
34
|
+
|
|
35
|
+
## Architecture
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
┌─────────────┐ ┌─────────┐ ┌──────────────┐
|
|
39
|
+
│ Portal │────▶│ API │────▶│ PostgreSQL │
|
|
40
|
+
│ (Next.js) │ │(FastAPI)│ │ Redis │
|
|
41
|
+
└─────────────┘ └────┬────┘ └──────────────┘
|
|
42
|
+
│
|
|
43
|
+
▼
|
|
44
|
+
┌──────────────┐
|
|
45
|
+
│ Orchestrator │────▶ Kubernetes / Inference
|
|
46
|
+
│ (Worker) │
|
|
47
|
+
└──────────────┘
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Project structure
|
|
51
|
+
|
|
52
|
+
| Directory | Description |
|
|
53
|
+
|-----------|-------------|
|
|
54
|
+
| `api/` | FastAPI backend (auth, deploy, run, billing, usage) |
|
|
55
|
+
| `portal/` | Next.js customer dashboard |
|
|
56
|
+
| `orchestrator/` | Redis queue worker, K8s job runner |
|
|
57
|
+
| `inference/` | Mock inference service (replace with your model server) |
|
|
58
|
+
| `sdk/` | Python client library |
|
|
59
|
+
| `cli/` | `quantlix` CLI |
|
|
60
|
+
| `infra/` | Terraform (Hetzner), Kubernetes manifests |
|
|
61
|
+
|
|
62
|
+
## Production
|
|
63
|
+
|
|
64
|
+
See [docs/GO_LIVE.md](docs/GO_LIVE.md) for the full deployment checklist: Stripe, SMTP, DNS, SSL, secrets.
|
|
65
|
+
|
|
66
|
+
## License
|
|
67
|
+
|
|
68
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Quantlix API."""
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""API key and password authentication."""
|
|
2
|
+
import hashlib
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
5
|
+
import bcrypt
|
|
6
|
+
from fastapi import Depends, HTTPException, status
|
|
7
|
+
from fastapi.security import APIKeyHeader
|
|
8
|
+
from sqlalchemy import select
|
|
9
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
10
|
+
|
|
11
|
+
from api.db import get_db
|
|
12
|
+
from api.models import APIKey, User
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def hash_password(password: str) -> str:
|
|
19
|
+
"""Hash password with bcrypt."""
|
|
20
|
+
return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def verify_password(plain: str, hashed: str) -> bool:
|
|
24
|
+
"""Verify password against hash."""
|
|
25
|
+
return bcrypt.checkpw(plain.encode(), hashed.encode())
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def hash_api_key(key: str) -> str:
|
|
29
|
+
"""Hash API key for storage (never store plain)."""
|
|
30
|
+
return hashlib.sha256(key.encode()).hexdigest()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
async def get_user_from_api_key(
|
|
34
|
+
api_key: Annotated[str | None, Depends(api_key_header)],
|
|
35
|
+
db: Annotated[AsyncSession, Depends(get_db)],
|
|
36
|
+
) -> User:
|
|
37
|
+
"""Resolve API key to user. Raises 401 if invalid or missing."""
|
|
38
|
+
if not api_key or not api_key.strip():
|
|
39
|
+
raise HTTPException(
|
|
40
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
41
|
+
detail="Missing API key. Provide X-API-Key header.",
|
|
42
|
+
)
|
|
43
|
+
key_hash = hash_api_key(api_key.strip())
|
|
44
|
+
result = await db.execute(
|
|
45
|
+
select(APIKey).where(APIKey.key_hash == key_hash)
|
|
46
|
+
)
|
|
47
|
+
api_key_row = result.scalar_one_or_none()
|
|
48
|
+
if not api_key_row:
|
|
49
|
+
raise HTTPException(
|
|
50
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
51
|
+
detail="Invalid API key.",
|
|
52
|
+
)
|
|
53
|
+
result = await db.execute(select(User).where(User.id == api_key_row.user_id))
|
|
54
|
+
user = result.scalar_one()
|
|
55
|
+
return user
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
async def get_current_api_key(
|
|
59
|
+
api_key: Annotated[str | None, Depends(api_key_header)],
|
|
60
|
+
db: Annotated[AsyncSession, Depends(get_db)],
|
|
61
|
+
) -> APIKey:
|
|
62
|
+
"""Resolve API key header to APIKey row. Raises 401 if invalid. For rotate/revoke flows."""
|
|
63
|
+
if not api_key or not api_key.strip():
|
|
64
|
+
raise HTTPException(
|
|
65
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
66
|
+
detail="Missing API key. Provide X-API-Key header.",
|
|
67
|
+
)
|
|
68
|
+
key_hash = hash_api_key(api_key.strip())
|
|
69
|
+
result = await db.execute(select(APIKey).where(APIKey.key_hash == key_hash))
|
|
70
|
+
api_key_row = result.scalar_one_or_none()
|
|
71
|
+
if not api_key_row:
|
|
72
|
+
raise HTTPException(
|
|
73
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
74
|
+
detail="Invalid API key.",
|
|
75
|
+
)
|
|
76
|
+
return api_key_row
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# Type aliases for route dependencies
|
|
80
|
+
CurrentUser = Annotated[User, Depends(get_user_from_api_key)]
|
|
81
|
+
CurrentAPIKey = Annotated[APIKey, Depends(get_current_api_key)]
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Application configuration."""
|
|
2
|
+
from pydantic import ConfigDict
|
|
3
|
+
from pydantic_settings import BaseSettings
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Settings(BaseSettings):
|
|
7
|
+
model_config = ConfigDict(
|
|
8
|
+
extra="ignore",
|
|
9
|
+
env_file=".env",
|
|
10
|
+
env_file_encoding="utf-8",
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
postgres_host: str = "localhost"
|
|
14
|
+
postgres_port: int = 5432
|
|
15
|
+
postgres_user: str = "cloud"
|
|
16
|
+
postgres_password: str = "cloud_secret"
|
|
17
|
+
postgres_db: str = "cloud"
|
|
18
|
+
redis_url: str = "redis://localhost:6379/0"
|
|
19
|
+
minio_endpoint: str = "localhost:9000"
|
|
20
|
+
minio_access_key: str = "minioadmin"
|
|
21
|
+
minio_secret_key: str = "minioadmin"
|
|
22
|
+
minio_bucket: str = "models"
|
|
23
|
+
jwt_secret: str = "dev-secret-change-me"
|
|
24
|
+
|
|
25
|
+
# Email (Sweego). Set to False to disable all email until domain is verified.
|
|
26
|
+
# Use SWEEGO_API_KEY for HTTP API (recommended in K8s; avoids SMTP port blocking).
|
|
27
|
+
# Otherwise use SMTP with smtp_user/smtp_password.
|
|
28
|
+
email_enabled: bool = True
|
|
29
|
+
sweego_api_key: str = "" # When set, use Sweego HTTP API instead of SMTP
|
|
30
|
+
sweego_auth_type: str = "api_token" # "api_token" (Api-Token), "api_key" (Api-Key), or "bearer" (Authorization: Bearer)
|
|
31
|
+
smtp_host: str = "smtp.sweego.io"
|
|
32
|
+
smtp_port: int = 587
|
|
33
|
+
smtp_user: str = ""
|
|
34
|
+
smtp_password: str = ""
|
|
35
|
+
smtp_from_email: str = "support@quantlix.ai"
|
|
36
|
+
smtp_from_name: str = "Quantlix"
|
|
37
|
+
app_base_url: str = "https://api.quantlix.ai" # For verification links
|
|
38
|
+
portal_base_url: str = "https://app.quantlix.ai" # For Stripe redirects
|
|
39
|
+
dev_return_verification_link: bool = False # If True, include verification link in signup response (for local testing)
|
|
40
|
+
|
|
41
|
+
# Usage limits (0 = unlimited)
|
|
42
|
+
usage_limit_tokens_per_month: int = 0
|
|
43
|
+
usage_limit_compute_seconds_per_month: float = 0.0
|
|
44
|
+
|
|
45
|
+
# CORS (comma-separated extra origins, e.g. for Vercel: https://quantlix.vercel.app)
|
|
46
|
+
cors_origins: str = ""
|
|
47
|
+
|
|
48
|
+
# Stripe
|
|
49
|
+
stripe_secret_key: str = ""
|
|
50
|
+
stripe_webhook_secret: str = ""
|
|
51
|
+
stripe_price_id_starter: str = "" # Price ID for Starter €9/mo
|
|
52
|
+
stripe_price_id_pro: str = "" # Price ID for Pro plan (e.g. price_xxx)
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def database_url(self) -> str:
|
|
56
|
+
return (
|
|
57
|
+
f"postgresql+asyncpg://{self.postgres_user}:{self.postgres_password}"
|
|
58
|
+
f"@{self.postgres_host}:{self.postgres_port}/{self.postgres_db}"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
settings = Settings()
|
quantlix-0.1.0/api/db.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Database setup and session management."""
|
|
2
|
+
from collections.abc import AsyncGenerator
|
|
3
|
+
|
|
4
|
+
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
|
5
|
+
from sqlalchemy.orm import DeclarativeBase
|
|
6
|
+
|
|
7
|
+
from api.config import settings
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Base(DeclarativeBase):
|
|
11
|
+
"""SQLAlchemy declarative base."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
engine = create_async_engine(
|
|
15
|
+
settings.database_url,
|
|
16
|
+
echo=False,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
async_session_maker = async_sessionmaker(
|
|
20
|
+
engine,
|
|
21
|
+
class_=AsyncSession,
|
|
22
|
+
expire_on_commit=False,
|
|
23
|
+
autocommit=False,
|
|
24
|
+
autoflush=False,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
async def get_db() -> AsyncGenerator[AsyncSession, None]:
|
|
29
|
+
"""Dependency for async DB session."""
|
|
30
|
+
async with async_session_maker() as session:
|
|
31
|
+
try:
|
|
32
|
+
yield session
|
|
33
|
+
await session.commit()
|
|
34
|
+
except Exception:
|
|
35
|
+
await session.rollback()
|
|
36
|
+
raise
|
|
37
|
+
finally:
|
|
38
|
+
await session.close()
|