TinySDK
Shared service clients for the TinyAI microservices platform — MongoDB, MinIO, Temporal workers, and structured logging with a consistent interface.
Installation
pip install tinysdk
Quick Start
from tinysdk import TinyDBService, TinyStorageService, TinyWorkerService, TinyWorkflowService, get_temporal_client, TinyLogger
# MongoDB
db = TinyDBService()
doc_id = db.create("users", {"name": "John", "email": "john@example.com"})
users = db.read("users", filter={"name": "John"})
count = db.update("users", {"name": "John"}, {"$set": {"active": True}})
count = db.delete("users", {"name": "John"})
exists = db.exists("users", {"email": "john@example.com"})
ids = db.create_multiple("users", [{"name": "Alice"}, {"name": "Bob"}])
db.close()
# MinIO / S3
storage = TinyStorageService()
storage.upload_file("my-bucket", "file.txt", buffer) # file_size auto-detected
storage.upload_file("my-bucket", "file.txt", buffer, 1024) # or pass explicitly
file_buffer, name = storage.download_file("my-bucket", "file.txt")
storage.upload_folder("my-bucket", "/local/folder")
storage.download_folder("my-bucket", "folder-name", "/local/dest")
storage.close()
# Temporal (activity/workflow worker for a task queue)
from temporalio import activity
@activity.defn
async def my_activity(payload: dict) -> None:
...
worker = TinyWorkerService() # logger optional; defaults to TinyLogger()
await worker.start(task_queue="my-queue", activities=[my_activity]) # or workflows=[...]
# Or just a connected client
client = await get_temporal_client()
# Temporal (read-only workflow queries) — async, call from an async function
workflow_svc = TinyWorkflowService()
status = await workflow_svc.get_status("order-123") # status, timings, task queue
history = await workflow_svc.get_history_summary("order-123") # condensed event history
summary = await workflow_svc.get_summary("order-123") # status + history in one call
# Logger
logger = TinyLogger()
logger.log("something happened", level="INFO", context={"key": "value"})
logger.start_heartbeat() # emits periodic HEARTBEAT events
logger.stop_heartbeat() # clean shutdown
doc_logger = logger.for_document("doc-id-123")
doc_logger.log("processing started")
Configuration
All clients read credentials from environment variables.
| Service | Variable | Example |
|---|---|---|
| MongoDB | DB_LINK |
mongodb://mongo:27017 |
| MongoDB | DB_NAME |
tinyDatabase |
| MinIO | MINIO_ENDPOINT |
minio:9000 |
| MinIO | MINIO_ACCESSKEY |
minioadmin |
| MinIO | MINIO_SECRETKEY |
minioadmin |
| Temporal | TEMPORAL_ADDRESS |
temporal-server:7233 |
| Temporal | TEMPORAL_NAMESPACE |
default |
| Logger | SERVICE_NAME |
my-service |
| Logger | CLUSTER |
prod |
| Logger | NAMESPACE |
default |
| Logger | LOG_LEVEL |
INFO |
| Logger | HEARTBEAT_INTERVAL |
30 |
Credentials can also be passed directly as constructor arguments.
Storage backends
TinyStorageService addresses storage by logical namespace (customer-documents,
tiny-models). A router resolves each namespace to a target and a physical location.
With no TINYSTORAGE_* variables set, every namespace resolves to the platform
target — S3, bucket name equal to the namespace. This is the default and needs no
configuration.
To route some namespaces to client-owned storage:
TINYSTORAGE_CLIENT_BACKEND=<registered backend name>
TINYSTORAGE_CLIENT_ROOT=/mnt/client-storage
TINYSTORAGE_MAP=customer-documents=client,finalized-documents=client
Both halves of that configuration are validated at startup, because either mistake ends with customer data on Synapze S3 and nothing to notice:
- mapping a namespace to
clientwith no client backend configured, and - configuring a client backend that no namespace is mapped to,
are both startup errors, never a silent fallback. So is mapping the same namespace
twice. Namespaces must match [a-z0-9]([a-z0-9.-]*[a-z0-9])? — S3 bucket syntax —
because a namespace becomes a path segment under the client root, and anything
outside that charset can never match a real call site and would only ever be a typo
routed silently to platform S3.
Every reachable backend is constructed at startup, so a malformed endpoint or an option the adapter does not accept fails at boot. This is not a reachability check: no client library performs network I/O in its constructor, so an unreachable host or a wrong secret still surfaces on first use.
TinyStorageService.client exposes the raw Minio client for the platform target and
ignores routing entirely. It therefore raises as soon as any namespace is mapped
to client — otherwise a caller could read or write customer-owned data straight
from Synapze S3. Use list_objects() and the routed methods instead.
Adding a backend
- Add a module under
src/tinysdk/storage/backends/implementingStorageBackend. - Add it to
REGISTRYinbackends/__init__.py. - Add a test subclassing the shipped conformance suite:
from tinysdk.storage.conformance import StorageBackendConformance
class TestMyBackendConformance(StorageBackendConformance):
def make_backend(self):
return MyBackend(...)
Path-based backends must:
- call
validate_keyon every key, and setenforces_key_policy = Trueon their conformance subclass so the suite checks it; - also confirm the resolved path is still under their root
(
os.path.realpath(p).startswith(root)) —validate_keyvets the key string, but a symlink already inside the root can redirect an otherwise valid key; - write via a temporary file in the target directory followed by an atomic rename — a temp file elsewhere makes the rename cross-device, which silently degrades to a copy and loses atomicity;
- decide how to handle unicode normalization (NFC and NFD spellings collide on APFS
and SharePoint) and case-insensitive volumes (
A.txtvsa.txt).
Options reach an adapter from TINYSTORAGE_CLIENT_* as strings; an adapter with
non-string options must coerce them itself.
License
MIT
Metadata
Release files for tinysdk 0.8.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tinysdk-0.8.3.tar.gz | 94.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tinysdk-0.8.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 163.5 kB
Release files / tinysdk-0.8.3.tar.gz
| Download URL | tinysdk-0.8.3.tar.gz |
|---|---|
| Size | 94.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4cc5ba7e3d8be99211ebb883ae3489663d66977e59284bc8dba238c5bd3d0d16
|
|
BLAKE2b-256 checksum How to use checksums |
ecc666451fb145706fb32237f635ba658855d7dba3d2d859f24ecea2b145ae6f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 3, 2026.
Transparency logRelease files / tinysdk-0.8.3-py3-none-any.whl
| Download URL | tinysdk-0.8.3-py3-none-any.whl |
|---|---|
| Size | 68.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6bbd3b48c2ee705c347229661923445dced21e80dfa0fd294deb0ba3adf63863
|
|
BLAKE2b-256 checksum How to use checksums |
6b051a2fc25b1714e3439b2c0df773c5b265a7c64beeb2ac7d2964c268a75980
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 3, 2026.
Transparency log