Introduction
Starlette is a modern, high‑performance asynchronous web framework for Python that forms the foundation of the popular FastAPI. Built specifically for ASGI (Asynchronous Server Gateway Interface), Starlette gives developers powerful tools to create fast, scalable web applications. In this article we will explore every aspect of working with this framework, its capabilities, and its benefits.
What Is Starlette
Project History and Goals
Starlette was created by Tom Christie, the well‑known author of Django REST Framework. The project launched in 2018 with the aim of providing developers a lightweight yet powerful tool for building asynchronous Python web applications. Starlette’s core philosophy is to offer a minimalist, functional framework that does not impose a specific way of doing things.
Key Framework Features
Starlette stands out thanks to a set of core features that make it attractive for modern web development:
- Full ASGI support — ensures maximum compatibility with contemporary asynchronous servers
- Native async/await — all components are designed around the async/await syntax
- Minimalist architecture — provides only the essentials without unnecessary bloat
- High performance — delivers excellent request‑processing speed
- Modularity — lets you use only the pieces you need
- Easy testing — includes built‑in utilities for writing tests
How Starlette Relates to FastAPI
FastAPI Architecture: Using Starlette as the Base
FastAPI is built on top of Starlette, using it as the low‑level layer for handling HTTP requests, routing, and middleware. This allows FastAPI to focus on extra features such as automatic data validation, OpenAPI documentation generation, and Pydantic integration while retaining Starlette’s speed and flexibility.
Why FastAPI Chose Starlette as Its Foundation
The decision to adopt Starlette was driven by several factors:
- Performance — Starlette consistently tops benchmark results
- ASGI compatibility — full support for the modern asynchronous web standard
- Stable architecture — a well‑designed, thoroughly tested codebase
- Active community — regular updates and strong developer support
Benefits of Asynchrony and Performance
ASGI and Asynchronous Calls Support
ASGI (Asynchronous Server Gateway Interface) is the new standard that replaces WSGI for async applications. Starlette is fully ASGI‑compatible, enabling you to:
- Handle thousands of concurrent connections
- Efficiently work with WebSocket connections
- Leverage modern async servers such as Uvicorn or Hypercorn
- Integrate with asynchronous databases and external APIs
Performance Comparison with WSGI Frameworks
Unlike traditional WSGI frameworks (Flask, Django), Starlette provides:
- Non‑blocking I/O — requests never block each other
- High throughput — more requests per second
- Efficient resource usage — lower memory and CPU consumption
- Better scalability — handles high load more gracefully
Starlette Architecture and Components
Main Framework Components
Starlette consists of several core components:
- Applications — the main application class
- Routing — request routing system
- Requests/Responses — objects for handling HTTP traffic
- Middleware — request/response interceptors
- Background Tasks — support for off‑loading work
- WebSockets — native WebSocket handling
- Static Files — static asset serving
- Templates — template engine integration
Request Lifecycle
A Starlette request passes through the following stages:
- Receive request — the ASGI server forwards the request to the app
- Middleware — request passes through any middleware layers
- Routing — the appropriate handler is selected
- Handler execution — business logic runs
- Response creation — an HTTP response is built
- Middleware — response may be processed by post‑middleware
- Send response — the response is returned to the client
In‑Depth Routing Exploration
Routing Basics in Starlette
Starlette offers a flexible routing system that supports a variety of route types:
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route, Mount
async def homepage(request):
return JSONResponse({'message': 'Home page'})
async def user_detail(request):
user_id = request.path_params['user_id']
return JSONResponse({'user_id': user_id})
app = Starlette(debug=True, routes=[
Route("/", homepage),
Route("/users/{user_id:int}", user_detail),
])
Route Types
Starlette supports several route variants:
- Route — standard HTTP routes
- WebSocketRoute — routes for WebSocket connections
- Mount — mount sub‑applications or sub‑directories
- Host — domain‑based routing
Route Parameters
The framework understands a range of URL parameter types:
- {param} — string parameter
- {param:int} — integer parameter
- {param:float} — floating‑point parameter
- {param:path} — path parameter (may contain slashes)
- {param:uuid} — UUID parameter
Working with Requests and Responses
The Request Object
Starlette provides a rich Request object for accessing incoming data:
from starlette.requests import Request
async def handle_request(request: Request):
# Basic request data
method = request.method
url = request.url
headers = request.headers
query_params = request.query_params
path_params = request.path_params
# Body handling
json_data = await request.json()
form_data = await request.form()
body = await request.body()
return JSONResponse({'method': method, 'url': str(url)})
Response Types
Starlette ships with several response classes for different use‑cases:
from starlette.responses import (
JSONResponse, HTMLResponse, PlainTextResponse,
RedirectResponse, FileResponse, StreamingResponse
)
async def json_response(request):
return JSONResponse({'data': 'json'})
async def html_response(request):
return HTMLResponse('<h1>HTML content</h1>')
async def redirect_response(request):
return RedirectResponse(url='/new-location')
async def file_response(request):
return FileResponse('path/to/file.pdf')
Middleware in Starlette
What Is Middleware
Middleware components intercept requests and responses at the application level, allowing you to add functionality that applies globally or to specific routes.
Built‑In Middleware
Starlette includes several useful middleware classes:
- CORSMiddleware — handles CORS requests
- SessionMiddleware — session management
- HTTPSRedirectMiddleware — forces HTTPS
- TrustedHostMiddleware — protects against Host header attacks
- GZipMiddleware — response compression
Creating Custom Middleware
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
class LoggingMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
start_time = time.time()
# Process request
response = await call_next(request)
# Log details
process_time = time.time() - start_time
logger.info(f"Handled {request.method} {request.url} in {process_time:.2f}s")
return response
app.add_middleware(LoggingMiddleware)
WebSocket and Real‑Time Functionality
WebSocket Basics
Starlette offers native WebSocket support:
from starlette.websockets import WebSocket
from starlette.routing import WebSocketRoute
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
try:
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Echo: {data}")
except WebSocketDisconnect:
print("Client disconnected")
app = Starlette(routes=[
WebSocketRoute("/ws", websocket_endpoint)
])
Advanced WebSocket Features
- Broadcast messaging — send a message to all connected clients
- Room management — group connections into rooms
- Authentication — secure WebSocket connections
- Error handling — manage errors and disconnects gracefully
Background Tasks
Background Tasks
Starlette includes a simple mechanism for running background work:
from starlette.background import BackgroundTasks
def send_email(email: str, message: str):
# Email‑sending logic
print(f"Sending email to {email}: {message}")
async def create_user(request):
user_data = await request.json()
# Create user in DB
user = create_user_in_db(user_data)
# Attach background task
task = BackgroundTasks()
task.add_task(send_email, user.email, "Welcome!")
return JSONResponse(
{"message": "User created"},
background=task
)
Exception and Error Handling
Built‑In Exception Handlers
Starlette provides a flexible exception handling system:
from starlette.exceptions import HTTPException
from starlette.responses import JSONResponse
async def not_found_handler(request, exc):
return JSONResponse(
{"error": "Resource not found"},
status_code=404
)
async def server_error_handler(request, exc):
return JSONResponse(
{"error": "Internal server error"},
status_code=500
)
app.add_exception_handler(404, not_found_handler)
app.add_exception_handler(500, server_error_handler)
Custom Exceptions
class CustomException(Exception):
def __init__(self, message: str, status_code: int = 400):
self.message = message
self.status_code = status_code
async def custom_exception_handler(request, exc):
return JSONResponse(
{"error": exc.message},
status_code=exc.status_code
)
app.add_exception_handler(CustomException, custom_exception_handler)
Static Files and Templates
Serving Static Files
from starlette.staticfiles import StaticFiles
app.mount("/static", StaticFiles(directory="static"), name="static")
app.mount("/media", StaticFiles(directory="media"), name="media")
Template Integration
from starlette.templating import Jinja2Templates
templates = Jinja2Templates(directory='templates')
async def template_response(request):
context = {
'request': request,
'title': 'My Page',
'users': get_users()
}
return templates.TemplateResponse('index.html', context)
Testing Starlette Applications
Testing Basics
from starlette.testclient import TestClient
import pytest
@pytest.fixture
def client():
return TestClient(app)
def test_homepage(client):
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Hello World"}
def test_create_user(client):
user_data = {"name": "Test User", "email": "test@example.com"}
response = client.post("/users", json=user_data)
assert response.status_code == 201
Asynchronous Testing
import httpx
import pytest
@pytest.mark.asyncio
async def test_async_endpoint():
async with httpx.AsyncClient(app=app, base_url="http://test") as client:
response = await client.get("/async-endpoint")
assert response.status_code == 200
Security in Starlette
CORS (Cross‑Origin Resource Sharing)
from starlette.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["https://myapp.com"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
HTTPS Enforcement
from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware
app.add_middleware(HTTPSRedirectMiddleware)
Host Header Protection
from starlette.middleware.trustedhost import TrustedHostMiddleware
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=["myapp.com", "*.myapp.com"]
)
Starlette Methods and Functions Table
| Component | Class / Function | Description | Usage Example |
|---|---|---|---|
| Applications | Starlette() |
Main application class | app = Starlette(debug=True, routes=routes) |
| Routing | Route() |
HTTP route | Route("/users/{id}", get_user) |
WebSocketRoute() |
WebSocket route | WebSocketRoute("/ws", websocket_endpoint) |
|
Mount() |
Mount sub‑applications | Mount("/api", sub_app) |
|
Host() |
Domain‑based routing | Host("api.example.com", routes=api_routes) |
|
| Requests | Request.method |
HTTP method | request.method |
Request.url |
Request URL | request.url |
|
Request.headers |
Request headers | request.headers["Authorization"] |
|
Request.query_params |
Query parameters | request.query_params["page"] |
|
Request.path_params |
Path parameters | request.path_params["user_id"] |
|
Request.json() |
JSON body | data = await request.json() |
|
Request.form() |
Form data | form = await request.form() |
|
Request.body() |
Raw body bytes | body = await request.body() |
|
| Responses | JSONResponse() |
JSON response | JSONResponse({"key": "value"}) |
HTMLResponse() |
HTML response | HTMLResponse("<h1>Hello</h1>") |
|
PlainTextResponse() |
Plain text response | PlainTextResponse("Hello") |
|
RedirectResponse() |
Redirect | RedirectResponse(url="/new-url") |
|
FileResponse() |
File download response | FileResponse("file.pdf") |
|
StreamingResponse() |
Streaming response | StreamingResponse(generate_data()) |
|
| Middleware | BaseHTTPMiddleware |
Base middleware class | class Custom(BaseHTTPMiddleware) |
CORSMiddleware |
CORS support | app.add_middleware(CORSMiddleware) |
|
SessionMiddleware |
Session handling | app.add_middleware(SessionMiddleware) |
|
HTTPSRedirectMiddleware |
HTTPS redirection | app.add_middleware(HTTPSRedirectMiddleware) |
|
TrustedHostMiddleware |
Host‑header protection | app.add_middleware(TrustedHostMiddleware) |
|
GZipMiddleware |
Response compression | app.add_middleware(GZipMiddleware) |
|
| WebSockets | WebSocket.accept() |
Accept a connection | await websocket.accept() |
WebSocket.receive_text() |
Receive text data | data = await websocket.receive_text() |
|
WebSocket.send_text() |
Send text data | await websocket.send_text("Hello") |
|
WebSocket.receive_json() |
Receive JSON data | data = await websocket.receive_json() |
|
WebSocket.send_json() |
Send JSON data | await websocket.send_json({"key": "value"}) |
|
WebSocket.close() |
Close the connection | await websocket.close() |
|
| Background Tasks | BackgroundTasks() |
Background task manager | tasks = BackgroundTasks() |
BackgroundTasks.add_task() |
Add a task | tasks.add_task(func, arg1, arg2) |
|
| Static Files | StaticFiles() |
Static file handling | StaticFiles(directory="static") |
| Templates | Jinja2Templates() |
Jinja2 template integration | templates = Jinja2Templates(directory="templates") |
TemplateResponse() |
Response rendered from a template | templates.TemplateResponse("index.html", context) |
|
| Testing | TestClient() |
Test client | client = TestClient(app) |
| Exceptions | HTTPException() |
HTTP error | HTTPException(status_code=404, detail="Not found") |
WebSocketException() |
WebSocket error | WebSocketException(code=1000) |
Database Integration
Asynchronous ORMs
Starlette works seamlessly with async ORMs:
# Tortoise ORM
from tortoise.contrib.starlette import register_tortoise
register_tortoise(
app,
db_url="sqlite://db.sqlite3",
modules={"models": ["app.models"]},
generate_schemas=True,
add_exception_handlers=True,
)
# SQLAlchemy with databases
from databases import Database
database = Database("postgresql://user:pass@localhost/dbname")
@app.on_event("startup")
async def startup():
await database.connect()
@app.on_event("shutdown")
async def shutdown():
await database.disconnect()
Performance Optimization
Caching
from starlette.middleware.base import BaseHTTPMiddleware
import time
class CacheMiddleware(BaseHTTPMiddleware):
def __init__(self, app, cache_time=300):
super().__init__(app)
self.cache = {}
self.cache_time = cache_time
async def dispatch(self, request, call_next):
cache_key = f"{request.method}:{request.url}"
if cache_key in self.cache:
cached_response, timestamp = self.cache[cache_key]
if time.time() - timestamp < self.cache_time:
return cached_response
response = await call_next(request)
self.cache[cache_key] = (response, time.time())
return response
Connection Pools
import aioredis
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app):
# Initialization
app.state.redis = aioredis.from_url("redis://localhost")
app.state.db_pool = await create_db_pool()
yield
# Cleanup
await app.state.redis.close()
await app.state.db_pool.close()
app = Starlette(lifespan=lifespan)
Monitoring and Logging
Structured Logging
import structlog
from starlette.middleware.base import BaseHTTPMiddleware
logger = structlog.get_logger()
class LoggingMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
start_time = time.time()
try:
response = await call_next(request)
duration = time.time() - start_time
logger.info(
"Request completed",
method=request.method,
url=str(request.url),
status_code=response.status_code,
duration=duration
)
return response
except Exception as e:
logger.error(
"Request failed",
method=request.method,
url=str(request.url),
error=str(e)
)
raise
Performance Metrics
from prometheus_client import Counter, Histogram, generate_latest
REQUEST_COUNT = Counter('http_requests_total', 'Total HTTP requests', ['method', 'endpoint'])
REQUEST_DURATION = Histogram('http_request_duration_seconds', 'HTTP request duration')
class MetricsMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
start_time = time.time()
response = await call_next(request)
REQUEST_COUNT.labels(
method=request.method,
endpoint=request.url.path
).inc()
REQUEST_DURATION.observe(time.time() - start_time)
return response
# Metrics endpoint
async def metrics(request):
return PlainTextResponse(generate_latest())
Production Deployment
Uvicorn Configuration
# gunicorn.conf.py
bind = "0.0.0.0:8000"
workers = 4
worker_class = "uvicorn.workers.UvicornWorker"
worker_connections = 1000
max_requests = 1000
max_requests_jitter = 100
Docker Containerization
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: starlette-app
spec:
replicas: 3
selector:
matchLabels:
app: starlette-app
template:
metadata:
labels:
app: starlette-app
spec:
containers:
- name: app
image: starlette-app:latest
ports:
- containerPort: 8000
env:
- name: DATABASE_URL
value: "postgresql://user:pass@postgres:5432/db"
Extensions and Plugins
Popular Extensions
- Starlette-Admin — admin panel
- Starlette-OAuth — OAuth provider integration
- Starlette-Prometheus — Prometheus metrics
- Starlette-WTF — WTForms integration
- Starlette-GraphQL — GraphQL support
Building a Custom Extension
from starlette.applications import Starlette
from starlette.middleware.base import BaseHTTPMiddleware
class CustomExtension:
def __init__(self, app: Starlette, config: dict):
self.app = app
self.config = config
self.setup_middleware()
self.setup_routes()
def setup_middleware(self):
self.app.add_middleware(CustomMiddleware)
def setup_routes(self):
self.app.add_route("/extension-endpoint", self.handler)
async def handler(self, request):
return JSONResponse({"extension": "active"})
Comparison with Other Frameworks
Starlette vs Flask
| Feature | Starlette | Flask |
|---|---|---|
| Asynchrony | Native support | Limited |
| Performance | Very high | Medium |
| Size | Minimalist | Compact |
| Ecosystem | Growing | Mature |
| Learning curve | Medium | Low |
Starlette vs Sanic
| Feature | Starlette | Sanic |
|---|---|---|
| Stability | High | Medium |
| ASGI compatibility | Full | Limited |
| Documentation | Excellent | Good |
| Testing | Built‑in | External tools |
Starlette vs AIOHTTP
| Feature | Starlette | AIOHTTP |
|---|---|---|
| Ease of use | High | Medium |
| Performance | Excellent | Good |
| Middleware API | Convenient | Complex |
| WebSocket support | Simple integration | Requires extra setup |
Using Starlette in a Microservices Architecture
Service Discovery
import consul
class ServiceRegistry:
def __init__(self):
self.consul = consul.Consul()
async def register_service(self, name, host, port):
self.consul.agent.service.register(
name=name,
service_id=f"{name}-{host}-{port}",
address=host,
port=port,
check=consul.Check.http(f"http://{host}:{port}/health")
)
async def discover_service(self, name):
services = self.consul.health.service(name, passing=True)[1]
return [(s['Service']['Address'], s['Service']['Port'])
for s in services]
Circuit Breaker
import time
from enum import Enum
class CircuitState(Enum):
CLOSED = "closed"
OPEN = "open"
HALF_OPEN = "half_open"
class CircuitBreaker:
def __init__(self, failure_threshold=5, recovery_timeout=60):
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.failure_count = 0
self.last_failure_time = None
self.state = CircuitState.CLOSED
async def call(self, func, *args, **kwargs):
if self.state == CircuitState.OPEN:
if time.time() - self.last_failure_time > self.recovery_timeout:
self.state = CircuitState.HALF_OPEN
else:
raise Exception("Circuit breaker is open")
try:
result = await func(*args, **kwargs)
self.on_success()
return result
except Exception:
self.on_failure()
raise
def on_success(self):
self.failure_count = 0
self.state = CircuitState.CLOSED
def on_failure(self):
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = CircuitState.OPEN
Frequently Asked Questions
Can I use Starlette without FastAPI?
Yes. Starlette is a fully functional standalone framework. You can build applications of any complexity without FastAPI, as it already provides all the tools needed for modern web development.
How does Starlette differ from Flask?
The main differences lie in architecture and performance. Starlette is asynchronous and built on ASGI, allowing it to handle thousands of concurrent connections. Flask is synchronous, based on WSGI, and is less efficient under high load. Starlette also includes built‑in WebSocket support and modern web standards.
What is ASGI and why do I need it?
ASGI (Asynchronous Server Gateway Interface) is the interface standard between asynchronous Python applications and web servers. It replaces WSGI and enables async code, WebSocket connections, HTTP/2, and other modern protocols.
Is Starlette suitable for large projects?
Absolutely. Starlette powers FastAPI, which is used in production by companies such as Microsoft, Netflix, and Uber. Its modular design and high performance make it a solid choice for high‑traffic systems.
Which libraries are commonly used alongside Starlette?
Popular companions include Pydantic for data validation, SQLAlchemy or Tortoise ORM for database access, Jinja2 for templating, Databases for async DB interactions, Redis for caching, and Uvicorn as the ASGI server.
Should I code directly with Starlette or use FastAPI?
The decision depends on your needs. If you need automatic data validation, OpenAPI documentation, and type‑driven development, FastAPI is ideal. If you prefer full control over architecture with minimal dependencies, choose Starlette.
How does Starlette handle security?
Starlette offers built‑in middleware for security: CORSMiddleware for CORS, HTTPSRedirectMiddleware for forced HTTPS, and TrustedHostMiddleware to guard against Host header attacks. It also integrates easily with authentication and authorization systems.
Can I build a GraphQL API with Starlette?
Yes. Starlette works well with GraphQL libraries such as Graphene or Strawberry, and there are ready‑made extensions for seamless GraphQL integration.
How can I optimize a Starlette application’s performance?
Optimization strategies include response caching, database connection pooling, query optimization, using a CDN for static assets, proper ASGI server tuning, performance monitoring, and code profiling.
Does Starlette support Server‑Sent Events (SSE)?
Yes. Using StreamingResponse you can easily implement SSE to push real‑time data from the server to the client, which is useful for notifications, status updates, and other live features.
Real‑World Usage Examples
Creating a REST API
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
from starlette.middleware.cors import CORSMiddleware
# Sample data store
users = [
{"id": 1, "name": "John Doe", "email": "john@example.com"},
{"id": 2, "name": "Jane Smith", "email": "jane@example.com"}
]
# Handlers
async def get_users(request):
return JSONResponse(users)
async def get_user(request):
user_id = int(request.path_params['user_id'])
user = next((u for u in users if u['id'] == user_id), None)
if user:
return JSONResponse(user)
return JSONResponse({"error": "User not found"}, status_code=404)
async def create_user(request):
data = await request.json()
new_user = {
"id": len(users) + 1,
"name": data["name"],
"email": data["email"]
}
users.append(new_user)
return JSONResponse(new_user, status_code=201)
# Application
app = Starlette(routes=[
Route("/users", get_users, methods=["GET"]),
Route("/users", create_user, methods=["POST"]),
Route("/users/{user_id:int}", get_user, methods=["GET"]),
])
app.add_middleware(CORSMiddleware, allow_origins=["*"])
Building a WebSocket Chat
from starlette.applications import Starlette
from starlette.websockets import WebSocket
from starlette.routing import WebSocketRoute
from starlette.responses import HTMLResponse
from starlette.routing import Route
import json
# Connection store
connections = set()
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
connections.add(websocket)
try:
while True:
data = await websocket.receive_text()
message = json.loads(data)
# Broadcast to all clients
for connection in connections.copy():
try:
await connection.send_text(json.dumps({
"user": message["user"],
"message": message["message"],
"timestamp": message.get("timestamp")
}))
except:
connections.remove(connection)
except Exception:
connections.remove(websocket)
async def chat_page(request):
return HTMLResponse("""
""")
app = Starlette(routes=[
Route("/", chat_page),
WebSocketRoute("/ws", websocket_endpoint),
])
Conclusion
Starlette is a powerful, modern framework for building high‑performance Python web applications. Its asynchronous architecture, ASGI compatibility, and minimalist design make it an ideal choice for projects ranging from simple APIs to complex microservice ecosystems.
Key advantages include exceptional speed, architectural flexibility, comprehensive documentation, and an active developer community. Starlette delivers everything needed for contemporary web development, from WebSocket support and middleware to background tasks and seamless integration with a variety of databases and services.
Understanding how Starlette works not only enables you to craft more efficient applications but also gives you insight into the inner workings of FastAPI. This knowledge is especially valuable when developing high‑traffic systems where every millisecond counts.
Starlette continues to evolve rapidly, receiving regular updates and new features. Its impact on the Python web‑framework ecosystem is profound—it has set new standards for performance and architecture in modern web development. Learning and using Starlette is an investment in the future of your projects and your growth as a Python developer.
The Future of AI in Mathematics and Everyday Life: How Intelligent Agents Are Already Changing the Game
Experts warned about the risks of fake charity with AI
In Russia, universal AI-agent for robots and industrial processes was developed