The SDK is the public boundary for modules, engines, and extractors.
If you are writing code under modules/*, engines/*, or extractors/*, import public APIs from democrai.sdk.*. The SDK exists to keep extension code stable even when runtime internals, transports, providers, or storage backends change.
What the SDK Is For¶
Use the SDK to:
- build or load UI
- return effects from actions
- access core models
- persist module-owned data
- handle uploads and media references
- run agents, tools, and pipelines
- schedule background work
- translate user-facing strings
Do not skip around it and import from democrai.core.* directly from extension code.
For the package/import contract, see Public SDK Boundary.
Import Style¶
Use domain imports from the packaged SDK surface.
Examples:
from democrai.sdk.decorators import action
from democrai.sdk.auth import permission_requiredWhen a file needs the active request-scoped SDK object outside an injected handler argument, import the proxy explicitly:
from democrai.sdk.client import active_sdk as sdkInside actions, tools, and agents, prefer the injected module_sdk argument instead of importing the proxy just to reach a domain.
Domain Container Mental Model¶
Think of sdk as a namespace of runtime domains.
Common domains:
sdk.ui-> build surfaces, load YAML UI, resolve resources, and compose programmatic component treessdk.effects-> return or publish UI/runtime effects after actions completesdk.models-> work with core-managed entities through model-driven access patternssdk.database-> persist and query module-owned datasdk.media-> store, resolve, and retrieve uploaded or generated files through the active media providersdk.ai-> run completions, tools, agents, and pipelines through the runtime AI layersdk.tasks-> schedule and coordinate background work through the task runtimesdk.i18n-> resolve module-aware translations for user-facing stringssdk.access-> inspect and synchronize approval and access state exposed by the runtimesdk.auth-> apply permission checks and trusted auth helpers in module code
The root is not where you implement business logic. The root is where you choose the correct domain.
Most Important Domain Split¶
This is the split that usually causes confusion:
democrai.sdk.models-> core-managed entities, model-driven CRUD, model-driven schemasdemocrai.sdk.database-> module-owned persistencedemocrai.sdk.media-> persisted files and uploads, regardless of storage backenddemocrai.sdk.effects-> what your action returns to mutate UI/runtime behavior
If you pick the wrong domain, the code often still looks plausible but the architecture becomes wrong very quickly.
Typical Action Shape¶
Most module actions follow this structure:
from democrai.sdk.decorators import action
from democrai.sdk.auth import permission_required
@action("save")
@permission_required(["demo.item.create"])
async def save(ctx: dict, session: dict, module_sdk):
values = dict(ctx.get("values") or {})
saved = module_sdk.database.add(
MyEntity(name=str(values.get("name") or "").strip())
)
return module_sdk.effects.respond(
module_sdk.effects.notify(
"toast",
{"title": "Saved", "text": f"Item {saved.id} created", "variant": "success"},
),
module_sdk.effects.render(),
)What matters here:
- input comes from
ctx - persistence goes through the correct SDK domain
- the return value is wrapped in
sdk.effects.respond(...)
Effects Must Be Returned Explicitly¶
If you define effects, show how they are returned.
Correct:
return sdk.effects.respond(
sdk.effects.navigate("/demo/anonim/index", render=True)
)Also correct:
return sdk.effects.respond(
sdk.effects.ui_property_update("status_text", "text", {"literalString": "Done"})
)Do not leave docs at “use effects” without showing the action return shape.
Media Must Go Through the SDK¶
Uploads and file persistence must use sdk.media.
That is not optional documentation style. It is required because the media provider may be:
- local
- distributed
- object storage backed
If a module handles upload paths manually as if they were always local files, it will become fragile as soon as the deployment changes.
Model Access Must Be Explicit¶
When the entity is a core model:
listing = sdk.models.users.list(page=0, page_size=25, filters={})When the entity belongs to the module:
rows = sdk.database.list(MyModuleEntity, status="open")The documentation must keep these two paths separate.
SDK Pages¶
Use the SDK pages in the navigation for the complete domain list.